diff --git a/CMakeExternals/Boost.cmake b/CMakeExternals/Boost.cmake index 392f597f9f..208c32fcc9 100644 --- a/CMakeExternals/Boost.cmake +++ b/CMakeExternals/Boost.cmake @@ -1,333 +1,337 @@ #----------------------------------------------------------------------------- # Boost #----------------------------------------------------------------------------- include(mitkFunctionGetMSVCVersion) #[[ Sanity checks ]] if(DEFINED BOOST_ROOT AND NOT EXISTS ${BOOST_ROOT}) message(FATAL_ERROR "BOOST_ROOT variable is defined but corresponds to non-existing directory") endif() string(REPLACE "^^" ";" MITK_USE_Boost_LIBRARIES "${MITK_USE_Boost_LIBRARIES}") set(proj Boost) set(proj_DEPENDENCIES ) set(Boost_DEPENDS ${proj}) if(NOT DEFINED BOOST_ROOT AND NOT MITK_USE_SYSTEM_Boost) #[[ Reset variables. ]] set(patch_cmd "") set(configure_cmd "") set(install_cmd "") set(BOOST_ROOT ${ep_prefix}) if(WIN32) set(BOOST_LIBRARYDIR "${BOOST_ROOT}/lib") endif() #[[ If you update Boost, make sure that the FindBoost module of the minimum required version of CMake supports the new version of Boost. In case you are using a higher version of CMake, download at least the source code of the minimum required version of CMake to look into the right version of the FindBoost module: /share/cmake-/Modules/FindBoost.cmake Search for a list called _Boost_KNOWN_VERSIONS. If the new version is not included in this list, you have three options: * Update the minimum required version of CMake. This may require adaptions of other parts of our CMake scripts and has the most impact on other MITK developers. Yet this is the safest and cleanest option. * Set Boost_ADDITIONAL_VERSIONS (see the documentation of the FindBoost module). As Boost libraries and dependencies between them are hard-coded in the FindBoost module only for known versions, this may cause trouble for other MITK developers relying on new components of Boost or components with changed dependencies. * Copy a newer version of the FindBoost module into our CMake directory. Our CMake directory has a higher precedence than the default CMake module directory. Doublecheck if the minimum required version of CMake is able to process the newer version of the FindBoost module. Also, DO NOT FORGET to mention this option right above the call of cmake_minimum_required() in the top-level CMakeLists.txt file AND in this file right above the set(url) command below so if we update the minimum required version of CMake or use another option in the future, we do not forget to remove our copy of the FindBoost module again. ]] set(url "${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_74_0.tar.gz") set(md5 3c8fb92ce08b9ad5a5f0b35731ac2c8e) if(MITK_USE_Boost_LIBRARIES) #[[ Boost has a two-step build process. In the first step, a bootstrap script is called to build b2, an executable that is used to actually build Boost in the second step. The bootstrap script expects a toolset (compiler) argument that is used to build the b2 executable. The scripts and their expected argument format differ between Windows and Unix. ]] if(WIN32) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_VERSION_MINOR EQUAL 0) #[[ Use just the major version in the toolset name. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}) elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 20) #[[ Assume Visual Studio 2017. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}1) elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR LESS 30) #[[ Assume Visual Studio 2019. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}2) else() #[[ Fallback to the generic case. Be prepared to add another elseif branch above for future versions of Visual Studio. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}) endif() else() #[[ We support GCC and Clang on Unix. On macOS, the toolset must be set to clang. The actual compiler for all of these toolkits is set further below, after the bootstrap script but before b2. ]] if(CMAKE_CXX_COMPILER_ID STREQUAL GNU) set(toolset gcc) elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang OR APPLE) set(toolset clang) endif() if(toolset) set(bootstrap_args --with-toolset=${toolset}) endif() #[[ At least give it a shot if the toolset is something else and let the bootstrap script decide on the toolset by not passing any argument. ]] endif() #[[ The call of b2 is more complex. b2 arguments are grouped into options and properties. Options follow the standard format for arguments while properties are plain key-value pairs. ]] set(b2_options --build-dir= --stagedir= --ignore-site-config #[[ Build independent of any site.config file ]] -q #[[ Stop at first error ]] ) if(APPLE AND CMAKE_OSX_SYSROOT) #[[ Specify the macOS platform SDK to be used. ]] list(APPEND b2_options --sysroot=${CMAKE_OSX_SYSROOT}) endif() foreach(lib ${MITK_USE_Boost_LIBRARIES}) list(APPEND b2_options --with-${lib}) endforeach() set(b2_properties threading=multi runtime-link=shared "cxxflags=${MITK_CXX14_FLAG} ${CMAKE_CXX_FLAGS}" ) if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND b2_properties address-model=64) else() list(APPEND b2_properties address-model=32) endif() if(BUILD_SHARED_LIBS) list(APPEND b2_properties link=shared) else() list(APPEND b2_properties link=static) endif() list(APPEND b2_properties "\ $<$:variant=debug>\ $<$:variant=release>\ $<$:variant=release>\ $<$:variant=release>") if(WIN32) set(bootstrap_cmd if not exist b2.exe \( call bootstrap.bat ${bootstrap_args} \)) set(b2_cmd b2 ${b2_options} ${b2_properties} stage) else() set(bootstrap_cmd #[[ test -e ./b2 || ]] ./bootstrap.sh ${bootstrap_args}) set(b2_cmd ./b2 ${b2_options} ${b2_properties} stage) #[[ We already told Boost if we want to use GCC or Clang but so far we were not able to specify the exact same compiler we set in CMake when configuring the MITK superbuild for the first time. For example, this can be different from the system default when multiple versions of the same compiler are installed at the same time. The bootstrap script creates a configuration file for b2 that should be modified if necessary before b2 is called. We look for a line like using gcc ; and replace it with something more specific like using gcc : : /usr/bin/gcc-7.3 ; - We use the stream editor sed for the replacement but as macOS is + We use the stream editor sed for the replacement but since macOS is based on BSD Unix, we use the limited but portable BSD syntax instead of the more powerful GNU syntax. We also use | instead of the more commonly used / separator for sed because the replacement - contains slashes. ]] + contains slashes. - if(toolset) + 2021/06/15: The custom project-config.jam does not work well with + SDK paths on macOS anymore, so we use a custom project-config.jam + only on Linux for now. ]] + + if(toolset AND NOT APPLE) set(configure_cmd sed -i.backup "\ s|\ using[[:space:]][[:space:]]*${toolset}[[:space:]]*$|\ using ${toolset} : : ${CMAKE_CXX_COMPILER} $|\ g" /project-config.jam ) endif() endif() endif() if(WIN32) set(dummy_cmd cd .) else() set(dummy_cmd true) #[[ "cd ." does not work reliably ]] endif() if(NOT patch_cmd) set(patch_cmd ${dummy_cmd}) #[[ Do nothing ]] endif() if(NOT configure_cmd) set(configure_cmd ${dummy_cmd}) #[[ Do nothing ]] endif() if(WIN32) set(install_cmd if not exist $ \( ${CMAKE_COMMAND} -E copy_directory /boost /include/boost \) ) else() set(install_cmd # test -e /include/boost/config.hpp || ${CMAKE_COMMAND} -E copy_directory /boost /include/boost ) endif() ExternalProject_Add(${proj} URL ${url} URL_MD5 ${md5} PATCH_COMMAND ${patch_cmd} CONFIGURE_COMMAND ${configure_cmd} BUILD_COMMAND "" INSTALL_COMMAND ${install_cmd} ) ExternalProject_Add_Step(${proj} bootstrap COMMAND ${bootstrap_cmd} DEPENDEES patch DEPENDERS configure WORKING_DIRECTORY ) ExternalProject_Add_Step(${proj} b2 COMMAND ${b2_cmd} DEPENDEES bootstrap DEPENDERS build WORKING_DIRECTORY ) if(WIN32) #[[ Reuse already extracted files. ]] set(stamp_dir ${ep_prefix}/src/Boost-stamp) configure_file( ${CMAKE_CURRENT_LIST_DIR}/extract-Boost.replacement.cmake ${stamp_dir}/extract-Boost.replacement.cmake COPYONLY) ExternalProject_Add_Step(${proj} pre_download COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-pre_download.cmake DEPENDEES mkdir DEPENDERS download WORKING_DIRECTORY ${stamp_dir} ) endif() set(install_manifest_dependees install) if(MITK_USE_Boost_LIBRARIES) if(WIN32) #[[ Move DLLs from lib to bin directory. ]] ExternalProject_Add_Step(${proj} post_install COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-WIN32.cmake DEPENDEES install WORKING_DIRECTORY /lib ) set(install_manifest_dependees post_install) elseif(APPLE) #[[ Boost does not follow the common practice of either using rpath or absolute paths for referencing dependencies. We have to use the install_name_tool to fix this. ]] ExternalProject_Add_Step(${proj} post_install COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-APPLE.cmake DEPENDEES install WORKING_DIRECTORY /lib ) set(install_manifest_dependees post_install) endif() endif() ExternalProject_Add_Step(${proj} install_manifest COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-install_manifest.cmake DEPENDEES ${install_manifest_dependees} WORKING_DIRECTORY ${ep_prefix} ) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/Modules/Core/include/mitkLevelWindowManager.h b/Modules/Core/include/mitkLevelWindowManager.h index 92768c692c..aedd51c4c7 100644 --- a/Modules/Core/include/mitkLevelWindowManager.h +++ b/Modules/Core/include/mitkLevelWindowManager.h @@ -1,203 +1,205 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKLEVELWINDOWMANAGER_H #define MITKLEVELWINDOWMANAGER_H // mitk core #include "mitkBaseProperty.h" #include "mitkDataStorage.h" #include "mitkLevelWindowProperty.h" // c++ #include #include namespace mitk { /** @brief Provides access to the LevelWindowProperty object and LevelWindow of the "current" image. - provides a LevelWindowProperty for purposes like GUI editors - this property comes from one of two possible sources - either something (e.g. the application) sets the property because of some user selection - OR the "Auto top-most" logic is used to search a DataStorage for the image with the highest "layer" property value Changes on Level/Window can be set with SetLevelWindow() and will affect either the topmost layer image, - if isAutoTopMost() returns true, or an image which is set by SetLevelWindowProperty(LevelWindowProperty::Pointer + if IsAutoTopMost() returns true, or an image which is set by SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty). + Additionally the changes on Level/Window will affect one or multiple selected images, if IsSelectedImages() returns true. + Only one of the two different modes can be enabled at the same time. Changes to Level/Window, when another image gets active or by SetLevelWindow(const LevelWindow& levelWindow), will be sent to all listeners by Modified(). - DataStorageChanged() listens to the DataStorage for new or removed images. Depending on how m_AutoTopMost is set, + DataStorageChanged() listens to the DataStorage for new or removed images. Depending on the currently enabled mode, the new image becomes active or not. If an image is removed from the DataStorage and m_AutoTopMost is false, there is a check to proof, if the active image is still available. If not, then m_AutoTopMost becomes true. Note that this class is not thread safe at the moment! */ class MITKCORE_EXPORT LevelWindowManager : public itk::Object { public: mitkClassMacroItkParent(LevelWindowManager, itk::Object); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void SetDataStorage(DataStorage* dataStorage); DataStorage *GetDataStorage(); /** * @brief (Re-)Initialize the LevelWindowManager by setting the topmost image. * Use the removedNode parameter if a node was removed. * * @param autoTopMost Set the topmost layer image to be affected by changes, if true. * @param removedNode A node was removed from the data storage if != nullptr. */ void SetAutoTopMostImage(bool autoTopMost, const DataNode *removedNode = nullptr); /** * @brief (Re-)Initialize the LevelWindowManager by setting the selected images. * Use the removedNode parameter if a node was removed. * * @param selectedImagesMode Set the selected images to be affected by changes, if true. * @param removedNode A node was removed from the data storage if != nullptr. */ void SetSelectedImages(bool selectedImagesMode, const DataNode *removedNode = nullptr); void RecalculateLevelWindowForSelectedComponent(const itk::EventObject&); /** * @brief Update the level window. * This function is called if a property of a data node is changed. * Relevant properties are defined in the protected 'ObserverToPropertyValueMap'-members. */ void Update(const itk::EventObject&); /** * @brief Update the level window. * This function is only called if the 'selected' property of a data node is changed. * This is done in order to avoid finding the correct image each time a node is selected but * the 'm_SelectedImages' bool value is set to false (as the normal 'Update'-function would do). * Changes of the 'selected' property happen quite a lot so this should not slow down the application. */ void UpdateSelected(const itk::EventObject&); /** * @brief Set a specific LevelWindowProperty; all changes will affect the image belonging to this property. * @throw mitk::Exception Throw an exception if the there is no image in the data storage which belongs to this * property. */ void SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty); /** * @brief Set new Level/Window values and inform all listeners about changes. */ void SetLevelWindow(const LevelWindow &levelWindow); /** * @brief Return the current LevelWindowProperty object from the image that is affected by changes. * * @return The current LevelWindowProperty */ - LevelWindowProperty::Pointer GetLevelWindowProperty(); + LevelWindowProperty::Pointer GetLevelWindowProperty() const; /** * @brief Return Level/Window values for the current image * * @return The LevelWindow value for the current image. */ - const LevelWindow &GetLevelWindow(); + const LevelWindow &GetLevelWindow() const; /** - * @brief Return true, if the changes on slider or line-edits will affect the topmost layer image. + * @brief Return true, if level window changes will affect the topmost layer image. * * @return Return the member value that denotes the auto-topmost mode. */ - bool IsAutoTopMost(); + bool IsAutoTopMost() const; /** - * @brief Return true, if changes on slider or line-edits will affect the currently selected images. + * @brief Return true, if level window changes will affect the currently selected images. * * @return Return the member value that denotes the selected-images mode. */ - bool IsSelectedImages(); + bool IsSelectedImages() const; /** * @brief This method is called when a node is added to the data storage. * A listener on the data storage is used to call this method automatically after a node was added. * @throw mitk::Exception Throws an exception if something is wrong, e.g. if the number of observers differs from * the number of nodes. */ void DataStorageAddedNode(const DataNode *dataNode = nullptr); /** * @brief This method is called when a node is removed from the data storage. * A listener on the data storage is used to call this method automatically before a node will be removed. * @throw mitk::Exception Throws an exception if something is wrong, e.g. if the number of observers differs from * the number of nodes. */ void DataStorageRemovedNode(const DataNode *dataNode = nullptr); /** * @brief Change notifications from mitkLevelWindowProperty. */ void OnPropertyModified(const itk::EventObject&); /** * @brief Return the currently active image. * * @return The member variable holding the currently active image. */ - Image *GetCurrentImage(); + Image *GetCurrentImage() const; /** * @brief Return the number of observers for data node's "visible" property. * This basically returns the number of relevant nodes to observe. * * @return The current number of observers which are registered in this object. */ - int GetNumberOfObservers(); + int GetNumberOfObservers() const; /** * @brief Return all nodes in the data storage that have the following properties: * - "binary" == false * - "levelwindow" * - DataType == Image / DiffusionImage / TensorImage / OdfImage / ShImage * @ return The filtered list of relevant nodes in the data storage */ - DataStorage::SetOfObjects::ConstPointer GetRelevantNodes(); + DataStorage::SetOfObjects::ConstPointer GetRelevantNodes() const; private: LevelWindowManager(); ~LevelWindowManager() override; DataStorage::Pointer m_DataStorage; LevelWindowProperty::Pointer m_LevelWindowProperty; typedef std::pair PropDataPair; typedef std::map ObserverToPropertyValueMap; ObserverToPropertyValueMap m_ObserverToVisibleProperty; ObserverToPropertyValueMap m_ObserverToLayerProperty; ObserverToPropertyValueMap m_ObserverToRenderingModeProperty; ObserverToPropertyValueMap m_ObserverToDisplayedComponentProperty; ObserverToPropertyValueMap m_ObserverToLevelWindowImageProperty; ObserverToPropertyValueMap m_ObserverToSelectedProperty; void UpdateObservers(); void ClearPropertyObserverMaps(); void CreatePropertyObserverMaps(); - bool HasLevelWindowRenderingMode(DataNode *dataNode); + bool HasLevelWindowRenderingMode(DataNode *dataNode) const; // This variable holds a data node which will be deleted from the datastorage immediately. const DataNode *m_NodeMarkedToDelete; bool m_AutoTopMost; bool m_SelectedImagesMode; unsigned long m_PropertyModifiedTag; Image *m_CurrentImage; std::vector m_DataNodesForLevelWindow; bool m_IsPropertyModifiedTagSet; bool m_LevelWindowMutex; }; } #endif // MITKLEVELWINDOWMANAGER_H diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp index 109716cbd2..d67daa36e3 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp @@ -1,724 +1,723 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkLevelWindowManager.h" -#include "mitkDataStorage.h" + #include "mitkImage.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkRenderingModeProperty.h" #include mitk::LevelWindowManager::LevelWindowManager() : m_DataStorage(nullptr) , m_LevelWindowProperty(nullptr) , m_AutoTopMost(true) , m_SelectedImagesMode(false) , m_CurrentImage(nullptr) , m_IsPropertyModifiedTagSet(false) , m_LevelWindowMutex(false) { } mitk::LevelWindowManager::~LevelWindowManager() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); m_DataStorage = nullptr; } if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } this->ClearPropertyObserverMaps(); } void mitk::LevelWindowManager::SetDataStorage(DataStorage *dataStorage) { if (nullptr == dataStorage) return; // remove listeners of old DataStorage if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); } // register listener for new DataStorage m_DataStorage = dataStorage; m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); this->DataStorageAddedNode(); } mitk::DataStorage *mitk::LevelWindowManager::GetDataStorage() { return m_DataStorage.GetPointer(); } void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const DataNode *removedNode/* = nullptr*/) { m_AutoTopMost = autoTopMost; if (false == m_AutoTopMost) { return; } // deactivate other mode m_SelectedImagesMode = false; if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } // find topmost image in the data storage if (m_DataStorage.IsNull()) { mitkThrow() << "DataStorage not set"; } DataNode::Pointer topLevelNode; int maxVisibleLayer = itk::NumericTraits::min(); m_LevelWindowProperty = nullptr; m_CurrentImage = nullptr; DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull() || node == removedNode) { continue; } // reset the "imageForLevelWindow" of each node m_LevelWindowMutex = true; node->SetBoolProperty("imageForLevelWindow", false); m_LevelWindowMutex = false; if (false == node->IsVisible(nullptr)) { continue; } bool validRenderingMode = HasLevelWindowRenderingMode(node); if (false == validRenderingMode) { continue; } int layer = -1; node->GetIntProperty("layer", layer); - if (layer < maxVisibleLayer) + if (layer <= maxVisibleLayer) { continue; } m_LevelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); topLevelNode = node; maxVisibleLayer = layer; } // this will set the "imageForLevelWindow" property and the 'm_CurrentImage' and call 'Modified()' this->SetLevelWindowProperty(m_LevelWindowProperty); if (m_LevelWindowProperty.IsNull()) { this->Modified(); } } void mitk::LevelWindowManager::SetSelectedImages(bool selectedImagesMode, const DataNode *removedNode/* = nullptr*/) { m_SelectedImagesMode = selectedImagesMode; if (false == m_SelectedImagesMode) { return; } // deactivate other mode m_AutoTopMost = false; if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } // find selected images in the data storage if (m_DataStorage.IsNull()) { mitkThrow() << "DataStorage not set"; } - DataNode::Pointer lastSelectedNode; m_LevelWindowProperty = nullptr; m_CurrentImage = nullptr; DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull() || node == removedNode) { continue; } // reset the "imageForLevelWindow" of each node m_LevelWindowMutex = true; node->SetBoolProperty("imageForLevelWindow", false); m_LevelWindowMutex = false; if (false == node->IsSelected()) { continue; } - bool validRenderingMode = HasLevelWindowRenderingMode(node); if (false == validRenderingMode) { continue; } m_LevelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); - m_DataNodesForLevelWindow.push_back(node); - lastSelectedNode = node; + m_DataNodesForLevelWindow.push_back(node); // nodes are used inside "SetLevelWindow" if the level window is changed } // this will set the "imageForLevelWindow" property and the 'm_CurrentImage' and call 'Modified()' this->SetLevelWindowProperty(m_LevelWindowProperty); if (m_LevelWindowProperty.IsNull()) { this->Modified(); } } void mitk::LevelWindowManager::RecalculateLevelWindowForSelectedComponent(const itk::EventObject &event) { DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; bool isSelected = false; node->GetBoolProperty("selected", isSelected); if (isSelected) { LevelWindow selectedLevelWindow; node->GetLevelWindow(selectedLevelWindow); // node is an image node because of predicates auto *image = dynamic_cast(node->GetData()); int displayedComponent = 0; if (image && (node->GetIntProperty("Image.Displayed Component", displayedComponent))) { // we found a selected image with a displayed component // let's recalculate the levelwindow for this. selectedLevelWindow.SetAuto(image, true, true, static_cast(displayedComponent)); node->SetLevelWindow(selectedLevelWindow); } } LevelWindow levelWindow; node->GetLevelWindow(levelWindow); } this->Update(event); } void mitk::LevelWindowManager::Update(const itk::EventObject &) { if (m_LevelWindowMutex) // no mutex, should still help { return; } m_DataNodesForLevelWindow.clear(); if (m_AutoTopMost) { this->SetAutoTopMostImage(true); return; } if (m_SelectedImagesMode) { this->SetSelectedImages(true); return; } int maxVisibleLayer = itk::NumericTraits::min(); DataNode::Pointer topLevelNode = nullptr; std::vector nodesForLevelWindow; DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull()) { continue; } if (false == node->IsVisible(nullptr)) { continue; } bool validRenderingMode = HasLevelWindowRenderingMode(node); if (false == validRenderingMode) { continue; } bool prop = false; node->GetBoolProperty("imageForLevelWindow", prop); if (prop) { nodesForLevelWindow.push_back(node); continue; } int layer = -1; node->GetIntProperty("layer", layer); - if (layer > maxVisibleLayer) + if (layer <= maxVisibleLayer) { - // top level node is backup node, if no node with - // "imageForLevelWindow" property with value "true" is found - topLevelNode = node; - maxVisibleLayer = layer; + continue; } + + // top level node is backup node, if no node with + // "imageForLevelWindow" property with value "true" is found + topLevelNode = node; + maxVisibleLayer = layer; } int nodesForLevelWindowSize = nodesForLevelWindow.size(); if (nodesForLevelWindowSize > 2) { MITK_ERROR << "Error: not more than two visible nodes are expected to have the imageForLevelWindow property set at " "any point."; } if (nodesForLevelWindowSize > 0) { // 1 or 2 nodes for level window found for (const auto& node : nodesForLevelWindow) { LevelWindowProperty::Pointer newProp = dynamic_cast(node->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } } else if (topLevelNode) { // no nodes for level window found LevelWindowProperty::Pointer lvlProp = dynamic_cast(topLevelNode->GetProperty("levelwindow")); this->SetLevelWindowProperty(lvlProp); } else { // no nodes for level window found and no visible top level node found this->Modified(); } } void mitk::LevelWindowManager::UpdateSelected(const itk::EventObject &) { if (m_LevelWindowMutex) // no mutex, should still help { return; } m_DataNodesForLevelWindow.clear(); if (m_SelectedImagesMode) { this->SetSelectedImages(true); } } void mitk::LevelWindowManager::SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty) { if (levelWindowProperty.IsNull()) { return; } // find data node that belongs to the property DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetAll(); - mitk::DataNode::Pointer propNode = nullptr; + DataNode::Pointer propNode = nullptr; for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it.Value(); LevelWindowProperty::Pointer property = dynamic_cast(node->GetProperty("levelwindow")); if (property == levelWindowProperty) { propNode = node; } else { m_LevelWindowMutex = true; node->SetBoolProperty("imageForLevelWindow", false); m_LevelWindowMutex = false; } } if (propNode.IsNull()) { mitkThrow() << "No Image in the data storage that belongs to level-window property " << m_LevelWindowProperty; } if (m_IsPropertyModifiedTagSet) // remove listener for old property { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } m_LevelWindowProperty = levelWindowProperty; auto command = itk::ReceptorMemberCommand::New(); // register listener for new property command->SetCallbackFunction(this, &LevelWindowManager::OnPropertyModified); m_PropertyModifiedTag = m_LevelWindowProperty->AddObserver(itk::ModifiedEvent(), command); m_IsPropertyModifiedTagSet = true; m_CurrentImage = dynamic_cast(propNode->GetData()); m_LevelWindowMutex = true; propNode->SetBoolProperty("imageForLevelWindow", true); m_LevelWindowMutex = false; this->Modified(); } void mitk::LevelWindowManager::SetLevelWindow(const LevelWindow &levelWindow) { if (m_LevelWindowProperty.IsNull()) { return; } m_LevelWindowProperty->SetLevelWindow(levelWindow); for (const auto &dataNode : m_DataNodesForLevelWindow) { auto levelWindowProperty = dynamic_cast(dataNode->GetProperty("levelwindow")); if (nullptr == levelWindowProperty) { continue; } levelWindowProperty->SetLevelWindow(levelWindow); } this->Modified(); } -mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() +mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() const { return m_LevelWindowProperty; } -const mitk::LevelWindow &mitk::LevelWindowManager::GetLevelWindow() +const mitk::LevelWindow &mitk::LevelWindowManager::GetLevelWindow() const { if (m_LevelWindowProperty.IsNotNull()) { return m_LevelWindowProperty->GetLevelWindow(); } else { mitkThrow() << "No LevelWindow available!"; } } -bool mitk::LevelWindowManager::IsAutoTopMost() +bool mitk::LevelWindowManager::IsAutoTopMost() const { return m_AutoTopMost; } -bool mitk::LevelWindowManager::IsSelectedImages() +bool mitk::LevelWindowManager::IsSelectedImages() const { return m_SelectedImagesMode; } void mitk::LevelWindowManager::DataStorageAddedNode(const DataNode *) { // update observers with new data storage this->UpdateObservers(); // initialize LevelWindowManager to new image this->SetAutoTopMostImage(true); // check if everything is still ok if ((m_ObserverToVisibleProperty.size() != m_ObserverToLayerProperty.size()) || (m_ObserverToLayerProperty.size() != this->GetRelevantNodes()->size())) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::DataStorageRemovedNode(const DataNode *removedNode) { // First: check if deleted node is part of relevant nodes. // If not, abort method because there is no need change anything. bool removedNodeIsRelevant = false; DataStorage::SetOfObjects::ConstPointer relevantNodes = GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = relevantNodes->Begin(); it != relevantNodes->End(); ++it) { if (it->Value() == removedNode) { removedNodeIsRelevant = true; } } if (false == removedNodeIsRelevant) { return; } // remember node which will be removed m_NodeMarkedToDelete = removedNode; // update observers this->UpdateObservers(); // search image that belongs to the property if (m_LevelWindowProperty.IsNull()) { this->SetAutoTopMostImage(true, removedNode); } else { auto property = NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); DataNode *n = m_DataStorage->GetNode(property); if (n == nullptr || m_AutoTopMost) // if node was deleted, change our behavior to AutoTopMost, if AutoTopMost is // true change level window to topmost node { this->SetAutoTopMostImage(true, removedNode); } } // reset variable m_NodeMarkedToDelete = nullptr; // check if everything is still ok if ((m_ObserverToVisibleProperty.size() != m_ObserverToLayerProperty.size()) || (m_ObserverToLayerProperty.size() != (relevantNodes->size() - 1))) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject &) { this->Modified(); } -mitk::Image *mitk::LevelWindowManager::GetCurrentImage() +mitk::Image *mitk::LevelWindowManager::GetCurrentImage() const { return m_CurrentImage; } -int mitk::LevelWindowManager::GetNumberOfObservers() +int mitk::LevelWindowManager::GetNumberOfObservers() const { return m_ObserverToVisibleProperty.size(); } -mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() +mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() const { if (m_DataStorage.IsNull()) { return DataStorage::SetOfObjects::ConstPointer(DataStorage::SetOfObjects::New()); } auto notBinary = NodePredicateProperty::New("binary", BoolProperty::New(false)); auto hasLevelWindow = NodePredicateProperty::New("levelwindow", nullptr); auto isImage = NodePredicateDataType::New("Image"); auto isDImage = NodePredicateDataType::New("DiffusionImage"); auto isTImage = NodePredicateDataType::New("TensorImage"); auto isOdfImage = NodePredicateDataType::New("OdfImage"); auto isShImage = NodePredicateDataType::New("ShImage"); auto predicateTypes = NodePredicateOr::New(); predicateTypes->AddPredicate(isImage); predicateTypes->AddPredicate(isDImage); predicateTypes->AddPredicate(isTImage); predicateTypes->AddPredicate(isOdfImage); predicateTypes->AddPredicate(isShImage); NodePredicateAnd::Pointer predicate = NodePredicateAnd::New(); predicate->AddPredicate(notBinary); predicate->AddPredicate(hasLevelWindow); predicate->AddPredicate(predicateTypes); DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset(predicate); return relevantNodes; } void mitk::LevelWindowManager::UpdateObservers() { this->ClearPropertyObserverMaps(); this->CreatePropertyObserverMaps(); } void mitk::LevelWindowManager::ClearPropertyObserverMaps() { for (auto iter = m_ObserverToVisibleProperty.begin(); iter != m_ObserverToVisibleProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToVisibleProperty.clear(); for (auto iter = m_ObserverToLayerProperty.begin(); iter != m_ObserverToLayerProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToLayerProperty.clear(); for (auto iter = m_ObserverToRenderingModeProperty.begin(); iter != m_ObserverToRenderingModeProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToRenderingModeProperty.clear(); for (auto iter = m_ObserverToDisplayedComponentProperty.begin(); iter != m_ObserverToDisplayedComponentProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToDisplayedComponentProperty.clear(); for (auto iter = m_ObserverToLevelWindowImageProperty.begin(); iter != m_ObserverToLevelWindowImageProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToLevelWindowImageProperty.clear(); for (auto iter = m_ObserverToSelectedProperty.begin(); iter != m_ObserverToSelectedProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_ObserverToSelectedProperty.clear(); } void mitk::LevelWindowManager::CreatePropertyObserverMaps() { if (m_DataStorage.IsNull()) { mitkThrow() << "DataStorage not set"; } // add observers for all relevant nodes DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull() || node == m_NodeMarkedToDelete) { continue; } auto command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long visIdx = node->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command); m_ObserverToVisibleProperty[PropDataPair(visIdx, node)] = node->GetProperty("visible"); auto command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long layerIdx = node->GetProperty("layer")->AddObserver(itk::ModifiedEvent(), command2); m_ObserverToLayerProperty[PropDataPair(layerIdx, node)] = node->GetProperty("layer"); auto command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &LevelWindowManager::Update); BaseProperty::Pointer imageRenderingMode = node->GetProperty("Image Rendering.Mode"); if (imageRenderingMode.IsNotNull()) { unsigned long rendIdx = imageRenderingMode->AddObserver(itk::ModifiedEvent(), command3); m_ObserverToRenderingModeProperty[PropDataPair(rendIdx, node)] = imageRenderingMode.GetPointer(); } auto command4 = itk::ReceptorMemberCommand::New(); command4->SetCallbackFunction(this, &LevelWindowManager::RecalculateLevelWindowForSelectedComponent); BaseProperty::Pointer displayedImageComponent = node->GetProperty("Image.Displayed Component"); if (displayedImageComponent.IsNotNull()) { unsigned long dispIdx = displayedImageComponent->AddObserver(itk::ModifiedEvent(), command4); m_ObserverToDisplayedComponentProperty[PropDataPair(dispIdx, node)] = displayedImageComponent.GetPointer(); } auto command5 = itk::ReceptorMemberCommand::New(); command5->SetCallbackFunction(this, &LevelWindowManager::Update); BaseProperty::Pointer imgForLvlWin = node->GetProperty("imageForLevelWindow"); if (imgForLvlWin.IsNull()) { node->SetBoolProperty("imageForLevelWindow", false); imgForLvlWin = node->GetProperty("imageForLevelWindow"); } unsigned long lvlWinIdx = imgForLvlWin->AddObserver(itk::ModifiedEvent(), command5); m_ObserverToLevelWindowImageProperty[PropDataPair(lvlWinIdx, node)] = node->GetProperty("imageForLevelWindow"); auto command6 = itk::ReceptorMemberCommand::New(); command6->SetCallbackFunction(this, &LevelWindowManager::UpdateSelected); BaseProperty::Pointer selectedDataNode = node->GetProperty("selected"); if (selectedDataNode.IsNull()) { node->SetBoolProperty("selected", false); selectedDataNode = node->GetProperty("selected"); } unsigned long selectedIdx = selectedDataNode->AddObserver(itk::ModifiedEvent(), command5); m_ObserverToSelectedProperty[PropDataPair(selectedIdx, node)] = node->GetProperty("selected"); } } -bool mitk::LevelWindowManager::HasLevelWindowRenderingMode(DataNode *dataNode) +bool mitk::LevelWindowManager::HasLevelWindowRenderingMode(DataNode *dataNode) const { RenderingModeProperty::Pointer mode = dynamic_cast(dataNode->GetProperty("Image Rendering.Mode")); if (mode.IsNotNull()) { int currMode = mode->GetRenderingMode(); if (currMode == RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR || currMode == RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR) { return true; } } return false; } diff --git a/Modules/Core/test/CMakeLists.txt b/Modules/Core/test/CMakeLists.txt index dc8d4ee9b4..7839cb73bd 100644 --- a/Modules/Core/test/CMakeLists.txt +++ b/Modules/Core/test/CMakeLists.txt @@ -1,181 +1,177 @@ # The core tests need relaxed compiler flags... # TODO fix core tests to compile without these additional no-error flags if(MSVC_VERSION) # disable deprecated warnings (they would lead to errors) mitkFunctionCheckCAndCXXCompilerFlags("/wd4996" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) else() mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-declarations" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() MITK_CREATE_MODULE_TESTS() if(TARGET ${TESTDRIVER}) mitk_use_modules(TARGET ${TESTDRIVER} PACKAGES ITK|IONRRD VTK|TestingRendering tinyxml2) mitkAddCustomModuleTest(mitkVolumeCalculatorTest_Png2D-bw mitkVolumeCalculatorTest ${MITK_DATA_DIR}/Png2D-bw.png ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) mitkAddCustomModuleTest(mitkEventConfigTest_CreateObjectInDifferentWays mitkEventConfigTest ${MITK_SOURCE_DIR}/Modules/Core/test/resource/Interactions/StatemachineConfigTest.xml ) mitkAddCustomModuleTest(mitkDataStorageTest_US4DCyl mitkDataStorageTest ${MITK_DATA_DIR}/US4DCyl.nrrd ) mitkAddCustomModuleTest(mitkPointSetReaderTest mitkPointSetReaderTest ${MITK_DATA_DIR}/PointSetReaderTestData.mps ) mitkAddCustomModuleTest(mitkImageTest_4DImageData mitkImageTest ${MITK_DATA_DIR}/US4DCyl.nrrd ) mitkAddCustomModuleTest(mitkImageTest_2D+tImageData mitkImageTest ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) mitkAddCustomModuleTest(mitkImageTest_3DImageData mitkImageTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkImageEqualTest mitkImageEqualTest) mitkAddCustomModuleTest(mitkImageTest_brainImage mitkImageTest ${MITK_DATA_DIR}/brain.mhd ) - mitkAddCustomModuleTest(mitkLevelWindowManagerTest mitkLevelWindowManagerTest - ${MITK_DATA_DIR}/Pic3D.nrrd - ) - mitkAddCustomModuleTest(mitkMultiComponentImageDataComparisonFilterTest mitkMultiComponentImageDataComparisonFilterTest ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg ) mitkAddCustomModuleTest(mitkImageToItkTest mitkImageToItkTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkImageSliceSelectorTest mitkImageSliceSelectorTest ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) mitkAddCustomModuleTest(mitkRotatedSlice4DTest mitkRotatedSlice4DTest ${MITK_DATA_DIR}/UltrasoundImages/4D_TEE_Data_MV.dcm ) mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2DTest ${MITK_DATA_DIR}/RenderingTestData/rgbaImage.png #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/rgbaImage640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2D_pic3d640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3d640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2D_pic3dColorBlue640x480 mitkImageVtkMapper2DColorTest #test for color property (=blue) Pic3D sagittal slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dColorBlue640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2D_pic3dLevelWindow640x480 mitkImageVtkMapper2DLevelWindowTest #test for levelwindow property (=blood) #Pic3D sagittal slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dLevelWindowBlood640x480REF.png #corresponding reference #screenshot ) mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2D_pic3dSwivel640x480 mitkImageVtkMapper2DSwivelTest #test for a randomly chosen Pic3D swivelled slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dSwivel640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkPointSetVtkMapper2D_openMeAlone640x480 mitkPointSetVtkMapper2DTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAlone640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkPointSetVtkMapper2D_Pic3DPointSetForPic3D640x480 mitkPointSetVtkMapper2DImageTest ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/RenderingTestData/PointSetForPic3D.mps #input point set and image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Pic3DPointSetForPic3D640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkPointSetVtkMapper2D_openMeAloneGlyphType640x480 mitkPointSetVtkMapper2DGlyphTypeTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAloneGlyphType640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkPointSetVtkMapper2D_openMeAloneTransformed640x480 mitkPointSetVtkMapper2DTransformedPointsTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAloneTransformedPoints640x480REF.png #corresponding reference screenshot ) # Currently not working on windows because of a rendering timing issue # see bug 18083 for details if(NOT WIN32) mitkAddCustomModuleRenderingTest(mitkSurfaceDepthSortingTransparency_StanfordBunnySTL640x480 mitkSurfaceDepthSortingTest ${MITK_DATA_DIR}/RenderingTestData/Stanford_bunny.stl -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Stanford_bunnySTLDepthSorting640x480REF.png) endif() # BUG 18695 - tests deactivated, because win 32 bit continuous renders images slightly different. TODO! #Test reslice interpolation #note: nearest mode is already tested by swivel test #mitkAddCustomModuleRenderingTest(ResliceInterpolationIsLinear mitkImageVtkMapper2DResliceInterpolationPropertyTest # 1 #linear # ${MITK_DATA_DIR}/Pic3D.nrrd # -V # ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dRefLinear.png #corresponding reference screenshot LINEAR #) #mitkAddCustomModuleRenderingTest(ResliceInterpolationIsCubic mitkImageVtkMapper2DResliceInterpolationPropertyTest # 3 #cubic # ${MITK_DATA_DIR}/Pic3D.nrrd # -V # ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dRefCubic.png #corresponding reference screenshot CUBIC #) #End test reslice interpolation # Testing of the rendering of binary images #mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2D_binaryTestImage640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice # ${MITK_DATA_DIR}/RenderingTestData/binaryImage.nrrd #input image to load in data storage # -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/binaryImage640x480REF.png #corresponding reference screenshot #) #mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2D_binaryTestImageWithRef640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice # ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/RenderingTestData/binaryImage.nrrd #input image to load in data storage # -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/binaryImageWithRef640x480REF.png #corresponding reference screenshot #) # End of binary image tests mitkAddCustomModuleRenderingTest(mitkSurfaceVtkMapper3DTest_TextureProperty mitkSurfaceVtkMapper3DTest ${MITK_DATA_DIR}/RenderingTestData/earth.jpg -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/texturedSphere640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2DTransferFunctionTest_Png2D-bw mitkImageVtkMapper2DTransferFunctionTest ${MITK_DATA_DIR}/Png2D-bw.png -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-TransferFunctionRGBImage640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2DOpacityTransferFunctionTest_Png2D-bw mitkImageVtkMapper2DOpacityTransferFunctionTest ${MITK_DATA_DIR}/Png2D-bw.png -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-OpacityTransferFunctionRGBImage640x480REF.png #corresponding reference screenshot ) ############################## DISABLED TESTS mitkAddCustomModuleRenderingTest(mitkImageVtkMapper2DLookupTableTest_Png2D-bw mitkImageVtkMapper2DLookupTableTest ${MITK_DATA_DIR}/Png2D-bw.png -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-LookupTableRGBImage640x480REF.png #corresponding reference screenshot ) #mitkAddCustomModuleRenderingTest(mitkImageTest_color2DImage mitkImageTest # ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg #) #mitkAddCustomModuleRenderingTest(mitkNodeDependentPointSetInteractorTest mitkNodeDependentPointSetInteractorTest # ${MITK_DATA_DIR}/Pic3D.pic.gz ${MITK_DATA_DIR}/BallBinary30x30x30.pic.gz #) mitkAddCustomModuleRenderingTest(mitkPlaneGeometryDataMapper2DTest mitkPlaneGeometryDataMapper2DTest ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/PlaneGeometryMapper640x480REF.png #corresponding reference screenshot ) endif() # TARGET ${TESTDRIVER} diff --git a/Modules/Core/test/files.cmake b/Modules/Core/test/files.cmake index 201b7c43ef..9aa105bf67 100644 --- a/Modules/Core/test/files.cmake +++ b/Modules/Core/test/files.cmake @@ -1,198 +1,198 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkDataNodeTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkFileReaderRegistryTest.cpp #mitkFileWriterRegistryTest.cpp mitkFloatToStringTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataIOTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkImageCastTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetOnEmptyTest.cpp mitkPointSetLocaleTest.cpp mitkPointSetWriterTest.cpp mitkPointSetPointOperationsTest.cpp mitkProgressBarTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkPropertyPersistenceTest.cpp mitkPropertyPersistenceInfoTest.cpp mitkPropertyRelationRuleBaseTest.cpp mitkPropertyRelationsTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkProportionalTimeGeometryTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp mitkCompositePixelValueToStringTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkNodePredicateDataPropertyTest.cpp mitkNodePredicateFunctionTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkPropertyKeyPathTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp mitkPointTypeConversionTest.cpp mitkVectorTypeConversionTest.cpp mitkMatrixTypeConversionTest.cpp mitkArrayTypeConversionTest.cpp mitkSurfaceToImageFilterTest.cpp mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp mitkLineTest.cpp mitkArbitraryTimeGeometryTest.cpp mitkItkImageIOTest.cpp - mitkLevelWindowManagerCppUnitTest.cpp + mitkLevelWindowManagerTest.cpp mitkVectorPropertyTest.cpp mitkTemporoSpatialStringPropertyTest.cpp mitkPropertyNameHelperTest.cpp mitkNodePredicateGeometryTest.cpp mitkNodePredicateSubGeometryTest.cpp mitkPreferenceListReaderOptionsFunctorTest.cpp mitkGenericIDRelationRuleTest.cpp mitkSourceImageRelationRuleTest.cpp mitkTemporalJoinImagesFilterTest.cpp ) set(MODULE_RENDERING_TESTS mitkPointSetDataInteractorTest.cpp mitkSurfaceVtkMapper2DTest.cpp mitkSurfaceVtkMapper2D3DTest.cpp ) # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces ) # list of images for which the tests are run set(MODULE_TESTIMAGE US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACE binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkEventConfigTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DOpacityTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp mitkPointSetReaderTest.cpp mitkImageEqualTest.cpp mitkRotatedSlice4DTest.cpp mitkPlaneGeometryDataMapper2DTest.cpp ) # Currently not working on windows because of a rendering timing issue # see bug 18083 for details if(NOT WIN32) set(MODULE_CUSTOM_TESTS ${MODULE_CUSTOM_TESTS} mitkSurfaceDepthSortingTest.cpp) endif() set(RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) diff --git a/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp b/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp deleted file mode 100644 index ece270c293..0000000000 --- a/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkTestingMacros.h" -#include - -#include "mitkLevelWindowManager.h" -#include "mitkStandaloneDataStorage.h" -#include -#include -#include -#include -#include - -class mitkLevelWindowManagerCppUnitTestSuite : public mitk::TestFixture -{ - CPPUNIT_TEST_SUITE(mitkLevelWindowManagerCppUnitTestSuite); - MITK_TEST(TestMultiComponentRescaling); - CPPUNIT_TEST_SUITE_END(); - -private: - mitk::Image::Pointer m_mitkMultiComponentImage; - mitk::Image::Pointer m_mitkImageComponent1; - mitk::Image::Pointer m_mitkImageComponent2; - -public: - void setUp() override - { - typedef itk::Image ImageType; - typedef itk::ImageRegionIterator ImageIteratorType; - typedef itk::ImageDuplicator DuplicatorType; - typedef itk::ComposeImageFilter CompositeFilterType; - - // generate two images with one component - ImageType::Pointer imageComponent1 = itk::Image::New(); - ImageType::IndexType start; - start.Fill(0); - ImageType::SizeType size; - size.Fill(5); - ImageType::RegionType region; - region.SetSize(size); - region.SetIndex(start); - imageComponent1->SetRegions(region); - imageComponent1->Allocate(); - - DuplicatorType::Pointer duplicator = DuplicatorType::New(); - duplicator->SetInputImage(imageComponent1); - duplicator->Update(); - ImageType::Pointer imageComponent2 = duplicator->GetOutput(); - - // give them differing data - ImageIteratorType iterator1(imageComponent1, imageComponent1->GetLargestPossibleRegion()); - iterator1.GoToBegin(); - int i = 0; - while (!iterator1.IsAtEnd()) - { - iterator1.Set((double)i); - ++iterator1; - ++i; - } - - ImageIteratorType iterator2(imageComponent2, imageComponent2->GetLargestPossibleRegion()); - iterator2.GoToBegin(); - i = 2000; - while (!iterator2.IsAtEnd()) - { - iterator2.Set((double)i); - ++iterator2; - ++i; - } - - // copy into single VectorImage - CompositeFilterType::Pointer compositeFilter = CompositeFilterType::New(); - compositeFilter->SetInput(0, imageComponent1); - compositeFilter->SetInput(1, imageComponent2); - compositeFilter->Update(); - itk::VectorImage::Pointer multiComponentImage = compositeFilter->GetOutput(); - - // cast images to mitk - mitk::CastToMitkImage(multiComponentImage, m_mitkMultiComponentImage); - mitk::CastToMitkImage(imageComponent1, m_mitkImageComponent1); - mitk::CastToMitkImage(imageComponent2, m_mitkImageComponent2); - } - - static mitk::LevelWindow getLevelWindowForImage(mitk::Image *image, unsigned component) - { - mitk::LevelWindowManager::Pointer manager; - manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - - try - { - manager->SetDataStorage(ds); - } - catch (std::exception &e) - { - MITK_ERROR << "Exception: " << e.what(); - } - - mitk::DataNode::Pointer node = mitk::DataNode::New(); - node->SetData(image); - ds->Add(node); - - node->SetBoolProperty("selected", true); - node->SetIntProperty("Image.Displayed Component", component); - - mitk::LevelWindow levelWindow; - node->GetLevelWindow(levelWindow); - return levelWindow; // node is an image node because of predicates - } - - void TestMultiComponentRescaling() - { - // compute level windows for the three images - mitk::LevelWindow imageComponent1LevelWindow = getLevelWindowForImage(m_mitkImageComponent1, 0); - mitk::LevelWindow imageComponent2LevelWindow = getLevelWindowForImage(m_mitkImageComponent2, 0); - // calculate level window for second component in multi-component image - mitk::LevelWindow multiComponentImageLevelWindow = getLevelWindowForImage(m_mitkMultiComponentImage, 1); - - // compare level window boundaries. the multicomponent image level window - // should match the second image, since the second component was selected - CPPUNIT_ASSERT_EQUAL_MESSAGE("default lower bounds equal", - imageComponent2LevelWindow.GetDefaultLowerBound(), - multiComponentImageLevelWindow.GetDefaultLowerBound()); - - CPPUNIT_ASSERT_EQUAL_MESSAGE("default upper bounds equal", - imageComponent2LevelWindow.GetDefaultUpperBound(), - multiComponentImageLevelWindow.GetDefaultUpperBound()); - - CPPUNIT_ASSERT_EQUAL_MESSAGE( - "range equal", imageComponent2LevelWindow.GetRange(), multiComponentImageLevelWindow.GetRange()); - - CPPUNIT_ASSERT(imageComponent1LevelWindow.GetDefaultLowerBound() != - multiComponentImageLevelWindow.GetDefaultLowerBound()); - - CPPUNIT_ASSERT(imageComponent1LevelWindow.GetDefaultUpperBound() != - multiComponentImageLevelWindow.GetDefaultUpperBound()); - } -}; - -MITK_TEST_SUITE_REGISTRATION(mitkLevelWindowManagerCppUnit) diff --git a/Modules/Core/test/mitkLevelWindowManagerTest.cpp b/Modules/Core/test/mitkLevelWindowManagerTest.cpp index 1d7de8c01e..43cddaee66 100644 --- a/Modules/Core/test/mitkLevelWindowManagerTest.cpp +++ b/Modules/Core/test/mitkLevelWindowManagerTest.cpp @@ -1,621 +1,439 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#include "itkMersenneTwisterRandomVariateGenerator.h" -#include "mitkLevelWindowManager.h" -#include "mitkRenderingModeProperty.h" -#include "mitkStandaloneDataStorage.h" +#include +#include + +#include +#include +#include +#include + #include #include #include #include -#include -#include #include -#include -class mitkLevelWindowManagerTestClass +class mitkLevelWindowManagerTestSuite : public mitk::TestFixture { -public: - static void TestInstantiation() - { - mitk::LevelWindowManager::Pointer manager; - manager = mitk::LevelWindowManager::New(); - MITK_TEST_CONDITION_REQUIRED(manager.IsNotNull(), "Testing mitk::LevelWindowManager::New()"); - } + CPPUNIT_TEST_SUITE(mitkLevelWindowManagerTestSuite); + MITK_TEST(TestModes); + MITK_TEST(TestSetLevelWindowProperty); + MITK_TEST(TestVisibilityPropertyChanged); + MITK_TEST(TestLayerPropertyChanged); + MITK_TEST(TestImageRenderingModePropertyChanged); + MITK_TEST(TestImageForLevelWindowPropertyChanged); + MITK_TEST(TestSelectedPropertyChanged); + MITK_TEST(TestRemoveDataNodes); + MITK_TEST(TestCombinedPropertiesChanged); - static void TestSetGetDataStorage() - { - mitk::LevelWindowManager::Pointer manager; - manager = mitk::LevelWindowManager::New(); - MITK_TEST_OUTPUT(<< "Creating DataStorage: "); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); + CPPUNIT_TEST_SUITE_END(); - bool success = true; - try - { - manager->SetDataStorage(ds); - } - catch (std::exception &e) - { - success = false; - MITK_ERROR << "Exception: " << e.what(); - } - MITK_TEST_CONDITION_REQUIRED(success, "Testing mitk::LevelWindowManager SetDataStorage() "); - MITK_TEST_CONDITION_REQUIRED(ds == manager->GetDataStorage(), "Testing mitk::LevelWindowManager GetDataStorage "); - } +private: - static void TestMethodsWithInvalidParameters() - { - mitk::LevelWindowManager::Pointer manager; - manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - manager->SetDataStorage(ds); - - bool success = false; - mitk::LevelWindowProperty::Pointer levelWindowProperty = mitk::LevelWindowProperty::New(); - try - { - manager->SetLevelWindowProperty(levelWindowProperty); - } - catch (const mitk::Exception &) - { - success = true; - } - MITK_TEST_CONDITION(success, "Testing mitk::LevelWindowManager SetLevelWindowProperty with invalid parameter"); - } + mitk::LevelWindowManager::Pointer m_LevelWindowManager; + mitk::StandaloneDataStorage::Pointer m_DataManager; - static void TestOtherMethods() - { - mitk::LevelWindowManager::Pointer manager; - manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - manager->SetDataStorage(ds); - - MITK_TEST_CONDITION(manager->IsAutoTopMost(), "Testing mitk::LevelWindowManager isAutoTopMost"); - - // It is not clear what the following code is supposed to test. The expression in - // the catch(...) block does have no effect, so success is always true. - // Related bugs are 13894 and 13889 - /* - bool success = true; - try + std::string m_ImagePath1; + std::string m_ImagePath2; + std::string m_ImagePath3; + + mitk::DataNode::Pointer m_DataNode1; + mitk::DataNode::Pointer m_DataNode2; + mitk::DataNode::Pointer m_DataNode3; + + mitk::Image::Pointer m_mitkMultiComponentImage; + mitk::Image::Pointer m_mitkImageComponent1; + mitk::Image::Pointer m_mitkImageComponent2; + + bool AssertImageForLevelWindowProperty(bool assert1, bool assert2, bool assert3) { - mitk::LevelWindow levelWindow = manager->GetLevelWindow(); - manager->SetLevelWindow(levelWindow); + bool imageForLevelWindowProperty1, imageForLevelWindowProperty2, imageForLevelWindowProperty3; + + m_DataNode1->GetBoolProperty("imageForLevelWindow", imageForLevelWindowProperty1); + m_DataNode2->GetBoolProperty("imageForLevelWindow", imageForLevelWindowProperty2); + m_DataNode3->GetBoolProperty("imageForLevelWindow", imageForLevelWindowProperty3); + + return (assert1 == imageForLevelWindowProperty1) && + (assert2 == imageForLevelWindowProperty2) && + (assert3 == imageForLevelWindowProperty3); } - catch (...) + + bool AssertLevelWindowProperty(bool assert1, bool assert2, bool assert3) { - success == false; + auto levelWindowProperty1 = + dynamic_cast(m_DataNode1->GetProperty("levelwindow")); + auto levelWindowProperty2 = + dynamic_cast(m_DataNode2->GetProperty("levelwindow")); + auto levelWindowProperty3 = + dynamic_cast(m_DataNode3->GetProperty("levelwindow")); + + // Check if the active level window property of the manager is equal to any of the level window properties of the nodes + auto managerLevelWindowProperty = m_LevelWindowManager->GetLevelWindowProperty(); + + return (assert1 == (managerLevelWindowProperty == levelWindowProperty1)) && + (assert2 == (managerLevelWindowProperty == levelWindowProperty2)) && + (assert3 == (managerLevelWindowProperty == levelWindowProperty3)); } - MITK_TEST_CONDITION(success,"Testing mitk::LevelWindowManager GetLevelWindow() and SetLevelWindow()"); - */ - manager->SetAutoTopMostImage(true); - MITK_TEST_CONDITION(manager->IsAutoTopMost(), "Testing mitk::LevelWindowManager isAutoTopMost()"); - } +public: - static void TestRemoveObserver(std::string testImageFile) + void setUp() override { - mitk::LevelWindowManager::Pointer manager; - manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - manager->SetDataStorage(ds); - - // add multiple objects to the data storage => multiple observers should be created - mitk::Image::Pointer image1 = mitk::IOUtil::Load(testImageFile); - mitk::DataNode::Pointer node1 = mitk::DataNode::New(); - node1->SetData(image1); - mitk::Image::Pointer image2 = mitk::IOUtil::Load(testImageFile); - mitk::DataNode::Pointer node2 = mitk::DataNode::New(); - node2->SetData(image2); - ds->Add(node1); - ds->Add(node2); - - MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 2, "Test if nodes have been added"); - MITK_TEST_CONDITION_REQUIRED( - static_cast(manager->GetRelevantNodes()->size()) == manager->GetNumberOfObservers(), - "Test if number of nodes is similar to number of observers"); - - mitk::Image::Pointer image3 = mitk::IOUtil::Load(testImageFile); - mitk::DataNode::Pointer node3 = mitk::DataNode::New(); - node3->SetData(image3); - ds->Add(node3); - MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 3, "Test if another node have been added"); - MITK_TEST_CONDITION_REQUIRED( - static_cast(manager->GetRelevantNodes()->size()) == manager->GetNumberOfObservers(), - "Test if number of nodes is similar to number of observers"); - - ds->Remove(node1); - MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 2, "Deleted node 1 (test GetRelevantNodes())"); - MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 2, "Deleted node 1 (test GetNumberOfObservers())"); - - ds->Remove(node2); - MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 1, "Deleted node 2 (test GetRelevantNodes())"); - MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 1, "Deleted node 2 (test GetNumberOfObservers())"); - - ds->Remove(node3); - MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 0, "Deleted node 3 (test GetRelevantNodes())"); - MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 0, "Deleted node 3 (test GetNumberOfObservers())"); + m_LevelWindowManager = mitk::LevelWindowManager::New(); + m_DataManager = mitk::StandaloneDataStorage::New(); + + CPPUNIT_ASSERT_NO_THROW_MESSAGE("DataStorage could not be set for the new level window manager", m_LevelWindowManager->SetDataStorage(m_DataManager)); + CPPUNIT_ASSERT_MESSAGE("DataStorage could not be retrieved from the new level window manager", m_DataManager == m_LevelWindowManager->GetDataStorage()); + + m_ImagePath1 = GetTestDataFilePath("Pic3D.nrrd"); + m_ImagePath2 = GetTestDataFilePath("UltrasoundImages/4D_TEE_Data_MV.dcm"); + m_ImagePath3 = GetTestDataFilePath("RenderingTestData/defaultWatermark.png"); + + // add multiple objects to the data storage => property observers will be created + m_DataNode1 = mitk::IOUtil::Load(m_ImagePath1, *m_DataManager)->GetElement(0); + m_DataNode2 = mitk::IOUtil::Load(m_ImagePath2, *m_DataManager)->GetElement(0); + CPPUNIT_ASSERT_MESSAGE("Not two relevant nodes found in the data storage", + m_LevelWindowManager->GetRelevantNodes()->size() == 2); + CPPUNIT_ASSERT_MESSAGE("Not two observers created for the relevant nodes", + m_LevelWindowManager->GetNumberOfObservers() == 2); + + m_DataNode1->SetIntProperty("layer", 1); + m_DataNode2->SetIntProperty("layer", 2); + + bool isImageForLevelWindow1, isImageForLevelWindow2; + m_DataNode1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); + m_DataNode2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); + CPPUNIT_ASSERT_MESSAGE("Initial \"imageForLevelWindow\" property not exclusively set for node 2", + !isImageForLevelWindow1 && isImageForLevelWindow2); + + m_DataNode3 = mitk::IOUtil::Load(m_ImagePath3, *m_DataManager)->GetElement(0); + CPPUNIT_ASSERT_MESSAGE("Not three relevant nodes found in the data storage", + m_LevelWindowManager->GetRelevantNodes()->size() == 3); + CPPUNIT_ASSERT_MESSAGE("Not three observers created for the relevant nodes", + m_LevelWindowManager->GetNumberOfObservers() == 3); + + m_DataNode3->SetIntProperty("layer", 3); + AssertImageForLevelWindowProperty(false, false, true); } - static bool VerifyRenderingModes() + void tearDown() override {} + + void TestModes() { - bool ok = (mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR == 1) && - (mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR == 2) && - (mitk::RenderingModeProperty::LOOKUPTABLE_COLOR == 3) && - (mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR == 4); + CPPUNIT_ASSERT_MESSAGE("AutoTopMost mode is not enabled per default", m_LevelWindowManager->IsAutoTopMost()); + CPPUNIT_ASSERT_MESSAGE("SelectedImagesMode mode is not disabled per default", !m_LevelWindowManager->IsSelectedImages()); - return ok; - } + m_LevelWindowManager->SetSelectedImages(true); + CPPUNIT_ASSERT_MESSAGE("AutoTopMost mode was not disabled on mode switch", !m_LevelWindowManager->IsAutoTopMost()); + CPPUNIT_ASSERT_MESSAGE("SelectedImagesMode mode was not enabled on mode switch", m_LevelWindowManager->IsSelectedImages()); - static void TestLevelWindowSliderVisibility(std::string testImageFile) - { - bool renderingModesValid = mitkLevelWindowManagerTestClass::VerifyRenderingModes(); - if (!renderingModesValid) - { - MITK_ERROR << "Exception: Image Rendering.Mode property value types inconsistent."; - } + m_LevelWindowManager->SetSelectedImages(false); + CPPUNIT_ASSERT_MESSAGE("AutoTopMost mode was not disabled on mode switch", !m_LevelWindowManager->IsAutoTopMost()); + CPPUNIT_ASSERT_MESSAGE("SelectedImagesMode mode was not disabled on mode switch", !m_LevelWindowManager->IsSelectedImages()); - mitk::LevelWindowManager::Pointer manager; - manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - manager->SetDataStorage(ds); - - // add multiple objects to the data storage => multiple observers should be created - mitk::Image::Pointer image1 = mitk::IOUtil::Load(testImageFile); - mitk::DataNode::Pointer node1 = mitk::DataNode::New(); - node1->SetData(image1); - ds->Add(node1); - - mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - std::vector nodeVec; - // nodeVec.resize( 3 ); - nodeVec.push_back(node1); - nodeVec.push_back(node2); - nodeVec.push_back(node3); - - typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; - RandomGeneratorType::Pointer rnd = RandomGeneratorType::New(); - rnd->Initialize(); - - for (unsigned int i = 0; i < 8; ++i) - { - unsigned int parity = i; - - for (unsigned int img = 0; img < 3; ++img) - { - if (parity & 1) - { - int mode = rnd->GetIntegerVariate() % 3; - nodeVec[img]->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New(mode)); - } - else - { - int mode = rnd->GetIntegerVariate() % 2; - nodeVec[img]->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New(3 + mode)); - } - parity >>= 1; - } - - MITK_TEST_CONDITION( - renderingModesValid && ((!manager->GetLevelWindowProperty() && !i) || (manager->GetLevelWindowProperty() && i)), - "Testing level window property member according to rendering mode"); - } + m_LevelWindowManager->SetSelectedImages(true); // to enable "SelectedImagesMode" + m_LevelWindowManager->SetAutoTopMostImage(true); + CPPUNIT_ASSERT_MESSAGE("AutoTopMost mode was not enabled on mode switch", m_LevelWindowManager->IsAutoTopMost()); + CPPUNIT_ASSERT_MESSAGE("SelectedImagesMode mode was not disabled on mode switch", !m_LevelWindowManager->IsSelectedImages()); } - static void TestSetLevelWindowProperty(std::string testImageFile) + void TestSetLevelWindowProperty() { - mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - manager->SetDataStorage(ds); - - // add multiple objects to the data storage => multiple observers should be created - mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - - node3->SetIntProperty("layer", 1); - node2->SetIntProperty("layer", 2); - node1->SetIntProperty("layer", 3); - - manager->SetAutoTopMostImage(true); - - bool isImageForLevelWindow1, isImageForLevelWindow2, isImageForLevelWindow3; - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); - - MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, - "Testing exclusive imageForLevelWindow property for node 1."); - - manager->SetAutoTopMostImage(false); - - mitk::LevelWindowProperty::Pointer prop = - dynamic_cast(node2->GetProperty("levelwindow")); - manager->SetLevelWindowProperty(prop); - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); - - MITK_TEST_CONDITION(!isImageForLevelWindow1 && isImageForLevelWindow2 && !isImageForLevelWindow3, - "Testing exclusive imageForLevelWindow property for node 2."); - - prop = dynamic_cast(node3->GetProperty("levelwindow")); - manager->SetLevelWindowProperty(prop); - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); - - MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, - "Testing exclusive imageForLevelWindow property for node 3."); - - prop = dynamic_cast(node1->GetProperty("levelwindow")); - manager->SetLevelWindowProperty(prop); - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); - - MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, - "Testing exclusive imageForLevelWindow property for node 3."); + m_LevelWindowManager->SetAutoTopMostImage(false); + + // Setting the level window property of the manager + // will make the corresponding node be the "imageForLevelWindow" node. + auto levelWindowProperty = dynamic_cast(m_DataNode1->GetProperty("levelwindow")); + m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + levelWindowProperty = dynamic_cast(m_DataNode2->GetProperty("levelwindow")); + m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + + levelWindowProperty = dynamic_cast(m_DataNode3->GetProperty("levelwindow")); + m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + + levelWindowProperty = mitk::LevelWindowProperty::New(); + //CPPUNIT_ASSERT_THROW_MESSAGE("Expected exception for an invalid level window property was not thrown", + //m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty), mitk::Exception); } - static void TestImageForLevelWindowOnVisibilityChange(std::string testImageFile) + void TestVisibilityPropertyChanged() { - mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - manager->SetDataStorage(ds); - - // add multiple objects to the data storage => multiple observers should be created - mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - - node3->SetIntProperty("layer", 1); - node2->SetIntProperty("layer", 2); - node1->SetIntProperty("layer", 3); + // Hiding a node will make the "next" node be the "imageForLevelWindow" node, if + // the hidden node was the "imageForLevelWindow" node before. "Next" is dependent on the node layer. + m_DataNode3->SetVisibility(false); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + + m_DataNode2->SetVisibility(false); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + m_DataNode1->SetVisibility(false); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); + + m_DataNode3->SetVisibility(true); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + } - manager->SetAutoTopMostImage(false); + void TestLayerPropertyChanged() + { + m_DataNode3->SetIntProperty("layer", itk::NumericTraits::min()); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + + m_DataNode2->SetIntProperty("layer", itk::NumericTraits::min()); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + m_DataNode1->SetIntProperty("layer", itk::NumericTraits::min()); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); + + m_DataNode3->SetIntProperty("layer", 1); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + } - bool isImageForLevelWindow1, isImageForLevelWindow2, isImageForLevelWindow3; - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); + void TestImageRenderingModePropertyChanged() + { + // checking the default rendering mode + auto renderingMode = dynamic_cast(m_DataNode3->GetProperty("Image Rendering.Mode")); + CPPUNIT_ASSERT_MESSAGE("Initial \"Image Rendering.Mode\" property not set to \"RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR\"", + mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR == renderingMode->GetRenderingMode()); + + // Changing the "Image Rendering.Mode" of a node to either "LOOKUPTABLE_COLOR" or + // "COLORTRANSFERFUNCTION_COLOR" will ignore this node for the level window + m_DataNode3->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( + mitk::RenderingModeProperty::LOOKUPTABLE_COLOR)); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + + m_DataNode2->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( + mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR)); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + m_DataNode1->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( + mitk::RenderingModeProperty::LOOKUPTABLE_COLOR)); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); + + m_DataNode3->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( + mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR)); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + } - MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, - "Testing initial imageForLevelWindow setting."); + void TestImageForLevelWindowPropertyChanged() + { + m_LevelWindowManager->SetAutoTopMostImage(false); - node1->SetVisibility(false); - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); + m_DataNode1->SetBoolProperty("imageForLevelWindow", true); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); - MITK_TEST_CONDITION(!isImageForLevelWindow1 && isImageForLevelWindow2 && !isImageForLevelWindow3, - "Testing exclusive imageForLevelWindow property for node 2."); + m_DataNode2->SetBoolProperty("imageForLevelWindow", true); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); - node2->SetVisibility(false); - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); + m_DataNode3->SetBoolProperty("imageForLevelWindow", true); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); - MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, - "Testing exclusive imageForLevelWindow property for node 3."); + // The top level node will alsways be used as a fall back node + // if no specific mode is selected. + m_DataNode3->SetBoolProperty("imageForLevelWindow", false); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + } - node3->SetVisibility(false); - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); + void TestSelectedPropertyChanged() + { + // Selecting a node will make this node be the "imageForLevelWindow" node, if + // the "SelectedImagesMode" is enabled (disabled per default). + m_DataNode1->SetSelected(true); // selection mode not enabled - node3 stays the "imageForLevelWindow" node + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + + // This will immediately check for the selected node (node 1). + m_LevelWindowManager->SetSelectedImages(true); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + // Second node will be selected (node 1 and node 2 selected). + // It is not specified which data node will be used for the "imageForLevelWindow" / "levelwindow" property, + // since the data storage access to the nodes is non-deterministic. + // At least check for any valid level window property. + m_DataNode2->SetSelected(true); + CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is null", m_LevelWindowManager->GetLevelWindowProperty()); + + // Third node will be selected (node 1, node 2 and node 3 selected) + // It is not specified which data node will be used for the "imageForLevelWindow" / "levelwindow" property, + // since the data storage access to the nodes is non-deterministic. + // At least check for any valid level window property. + m_DataNode3->SetSelected(true); + auto usedLevelWindowProperty = m_LevelWindowManager->GetLevelWindowProperty(); + CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is null", usedLevelWindowProperty); + + // All three nodes are selected: Check if only one node has the "imageForLevelWindow" property set and this node's + // "levelwindow" property is used by the level window manager. + auto levelWindowProperty1 = dynamic_cast(m_DataNode1->GetProperty("imageForLevelWindow")); + if (usedLevelWindowProperty == levelWindowProperty1) + { + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + } - MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, - "Testing exclusive imageForLevelWindow property for node 3."); + auto levelWindowProperty2 = dynamic_cast(m_DataNode2->GetProperty("imageForLevelWindow")); + if (usedLevelWindowProperty == levelWindowProperty2) + { + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + } - node1->SetVisibility(true); - node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); - node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); - node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); + auto levelWindowProperty3 = dynamic_cast(m_DataNode3->GetProperty("imageForLevelWindow")); + if (usedLevelWindowProperty == levelWindowProperty3) + { + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + } - MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, - "Testing exclusive imageForLevelWindow property for node 3."); + m_DataNode1->SetSelected(false); + m_DataNode2->SetSelected(false); + m_DataNode3->SetSelected(false); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); } - static void TestImageForLevelWindowOnRandomPropertyChange(std::string testImageFile) + void TestCombinedPropertiesChanged() { - typedef std::vector BoolVecType; - typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; - - // initialize the data storage - mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - manager->SetDataStorage(ds); - - mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - - node3->SetIntProperty("layer", 1); - node2->SetIntProperty("layer", 2); - node1->SetIntProperty("layer", 3); - - // node visibilities - std::vector nodesVisible; - nodesVisible.resize(3); - std::fill(nodesVisible.begin(), nodesVisible.end(), true); - - // which node has the level window - std::vector nodesForLevelWindow; - nodesForLevelWindow.resize(3); - std::fill(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), false); - - // the nodes themselves - std::vector nodes; - nodes.push_back(node1); - nodes.push_back(node2); - nodes.push_back(node3); - - // status quo - manager->SetAutoTopMostImage(false); - - bool lvlWin1, lvlWin2, lvlWin3; - node1->GetBoolProperty("imageForLevelWindow", lvlWin1); - node2->GetBoolProperty("imageForLevelWindow", lvlWin2); - node3->GetBoolProperty("imageForLevelWindow", lvlWin3); - - MITK_TEST_CONDITION(lvlWin1 && !lvlWin2 && !lvlWin3, "Testing initial imageForLevelWindow setting."); - - nodesForLevelWindow[0] = lvlWin1; - nodesForLevelWindow[1] = lvlWin2; - nodesForLevelWindow[2] = lvlWin3; - - // prepare randomized visibility changes - RandomGeneratorType::Pointer ranGen = RandomGeneratorType::New(); - ranGen->Initialize(); - - int ranCount = 100; - int validCount = 0; - int invalidCount = 0; - int mustHaveLvlWindow = 4; - for (int run = 0; run < ranCount; ++run) - { - // toggle node visibility - int ran = ranGen->GetIntegerVariate(2); - nodes[ran]->SetBoolProperty("imageForLevelWindow", !nodesForLevelWindow[ran]); - - // one node must have the level window - std::vector::const_iterator found = std::find(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), true); - if (found == nodesForLevelWindow.end()) - { - break; - } - - // all invisible? - found = std::find(nodesVisible.begin(), nodesVisible.end(), true); - - if (!nodesForLevelWindow[ran]) - { - mustHaveLvlWindow = pow(2, 2 - ran); - } - else - { - mustHaveLvlWindow = 4; - } - - // get the current status - node1->GetBoolProperty("imageForLevelWindow", lvlWin1); - node2->GetBoolProperty("imageForLevelWindow", lvlWin2); - node3->GetBoolProperty("imageForLevelWindow", lvlWin3); - nodesForLevelWindow[0] = lvlWin1; - nodesForLevelWindow[1] = lvlWin2; - nodesForLevelWindow[2] = lvlWin3; - - int hasLevelWindow = 0; - for (int i = 0; i < 3; ++i) - { - if (nodesForLevelWindow[i]) - { - hasLevelWindow += pow(2, 2 - i); - } - } - - validCount += hasLevelWindow == mustHaveLvlWindow ? 1 : 0; - - // test sensitivity - int falseran = 0; - while (falseran == 0) - { - falseran = ranGen->GetIntegerVariate(7); - } - BoolVecType falseNodes; - falseNodes.push_back((falseran & 1) == 1 ? !lvlWin1 : lvlWin1); - falseran >>= 1; - falseNodes.push_back((falseran & 1) == 1 ? !lvlWin2 : lvlWin2); - falseran >>= 1; - falseNodes.push_back((falseran & 1) == 1 ? !lvlWin3 : lvlWin3); - int falseLevelWindow = 0; - for (int i = 0; i < 3; ++i) - { - if (falseNodes[i]) - { - falseLevelWindow += pow(2, 2 - i); - } - } - - invalidCount += falseLevelWindow == mustHaveLvlWindow ? 0 : 1; - - // in case of errors proceed anyway - mustHaveLvlWindow = hasLevelWindow; - } + m_LevelWindowManager->SetSelectedImages(true); + m_DataNode1->SetSelected(true); // selection mode enabled - node1 becomes the "imageForLevelWindow" node + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + m_DataNode2->SetIntProperty("layer", itk::NumericTraits::max()); // selection mode enabled - node1 stays the "imageForLevelWindow" node + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + m_DataNode1->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( + mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR)); // selection mode enabled - but node1 is ignored - no active level window property + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); + + m_DataNode3->SetBoolProperty("imageForLevelWindow", true); // selection mode enabled - no valid node selected, no active level window property + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); + + m_LevelWindowManager->SetSelectedImages(false); // no mode enabled - however, level window is not modified / updated + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); + + m_LevelWindowManager->Update(itk::ModifiedEvent()); // no mode enabled - level window is modified / updated with topmost visible node2 as fallback + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + + m_DataNode3->SetBoolProperty("imageForLevelWindow", true); // no mode enabled - use node3 with "imageForLevelWindow" property set + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + + m_DataNode3->SetVisibility(false); // node3 with "imageForLevelWindow" property not visible - use topmost visible node2 as fallback + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + + m_DataNode2->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( + mitk::RenderingModeProperty::LOOKUPTABLE_COLOR)); // fallback node2 is ignored - but "imageForLevelWindow" and "levelWindow" stay "true" for node2 + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + + m_LevelWindowManager->SetAutoTopMostImage(true); // no visible node exists that is not ignored + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, false)); + + auto levelWindowProperty = dynamic_cast(m_DataNode3->GetProperty("levelwindow")); + m_LevelWindowManager->SetLevelWindowProperty(levelWindowProperty); // explicitely set the level window to node3 + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); + + m_DataNode1->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( + mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR)); // auto topmost mode enabled - node1 is the only visible non-ignored node + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + m_DataNode2->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New( + mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR)); // auto topmost mode enabled - node2 is topmost visible node + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, true, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, true, false)); + + m_DataNode2->SetIntProperty("layer", itk::NumericTraits::min()); // auto topmost mode enabled - node1 is topmost visible node + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(true, false, false)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(true, false, false)); + + m_DataNode3->SetVisibility(true); // auto topmost mode enabled - node3 is topmost visible node + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", AssertImageForLevelWindowProperty(false, false, true)); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", AssertLevelWindowProperty(false, false, true)); - MITK_TEST_CONDITION(validCount == ranCount, "Testing proper node for level window property."); - MITK_TEST_CONDITION(invalidCount == ranCount, "Sensitivity test."); } - static void TestImageForLevelWindowOnRandomVisibilityChange(std::string testImageFile) + void TestRemoveDataNodes() { - typedef std::vector BoolVecType; - typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; - - // initialize the data storage - mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); - mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); - manager->SetDataStorage(ds); - - mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); - - node3->SetIntProperty("layer", 1); - node2->SetIntProperty("layer", 2); - node1->SetIntProperty("layer", 3); - - // node visibilities - std::vector nodesVisible; - nodesVisible.resize(3); - std::fill(nodesVisible.begin(), nodesVisible.end(), true); - - // which node has the level window - std::vector nodesForLevelWindow; - nodesForLevelWindow.resize(3); - std::fill(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), false); - - // the nodes themselves - std::vector nodes; - nodes.push_back(node1); - nodes.push_back(node2); - nodes.push_back(node3); - - // status quo - manager->SetAutoTopMostImage(false); - - bool lvlWin1, lvlWin2, lvlWin3; - node1->GetBoolProperty("imageForLevelWindow", lvlWin1); - node2->GetBoolProperty("imageForLevelWindow", lvlWin2); - node3->GetBoolProperty("imageForLevelWindow", lvlWin3); - - MITK_TEST_CONDITION(lvlWin1 && !lvlWin2 && !lvlWin3, "Testing initial imageForLevelWindow setting."); - - nodesForLevelWindow[0] = lvlWin1; - nodesForLevelWindow[1] = lvlWin2; - nodesForLevelWindow[2] = lvlWin3; - - // prepare randomized visibility changes - RandomGeneratorType::Pointer ranGen = RandomGeneratorType::New(); - ranGen->Initialize(); - - int ranCount = 100; - int validCount = 0; - int invalidCount = 0; - int mustHaveLvlWindow = 4; - for (int run = 0; run < ranCount; ++run) - { - // toggle node visibility - int ran = ranGen->GetIntegerVariate(2); - nodesVisible[ran] = !nodesVisible[ran]; - nodes[ran]->SetVisibility(nodesVisible[ran]); - - // one node must have the level window - std::vector::const_iterator found = std::find(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), true); - if (found == nodesForLevelWindow.end()) - { - break; - } - int ind = found - nodesForLevelWindow.begin(); - - // all invisible? - found = std::find(nodesVisible.begin(), nodesVisible.end(), true); - bool allInvisible = (found == nodesVisible.end()); - - // which node shall get the level window now - if (!allInvisible && !nodesVisible[ind]) - { - int count = 0; - for (std::vector::const_iterator it = nodesVisible.begin(); it != nodesVisible.end(); ++it, ++count) - { - if (*it) - { - mustHaveLvlWindow = pow(2, 2 - count); - break; - } - } - } - - // get the current status - node1->GetBoolProperty("imageForLevelWindow", lvlWin1); - node2->GetBoolProperty("imageForLevelWindow", lvlWin2); - node3->GetBoolProperty("imageForLevelWindow", lvlWin3); - nodesForLevelWindow[0] = lvlWin1; - nodesForLevelWindow[1] = lvlWin2; - nodesForLevelWindow[2] = lvlWin3; - - int hasLevelWindow = 0; - for (int i = 0; i < 3; ++i) - { - if (nodesForLevelWindow[i]) - { - hasLevelWindow += pow(2, 2 - i); - } - } - - validCount += hasLevelWindow == mustHaveLvlWindow ? 1 : 0; - - // test sensitivity - int falseran = 0; - while (falseran == 0) - { - falseran = ranGen->GetIntegerVariate(7); - } - BoolVecType falseNodes; - falseNodes.push_back((falseran & 1) == 1 ? !lvlWin1 : lvlWin1); - falseran >>= 1; - falseNodes.push_back((falseran & 1) == 1 ? !lvlWin2 : lvlWin2); - falseran >>= 1; - falseNodes.push_back((falseran & 1) == 1 ? !lvlWin3 : lvlWin3); - int falseLevelWindow = 0; - for (int i = 0; i < 3; ++i) - { - if (falseNodes[i]) - { - falseLevelWindow += pow(2, 2 - i); - } - } - - invalidCount += falseLevelWindow == mustHaveLvlWindow ? 0 : 1; - - // in case of errors proceed anyway - mustHaveLvlWindow = hasLevelWindow; - } - - MITK_TEST_CONDITION(validCount == ranCount, "Testing proper node for level window property."); - MITK_TEST_CONDITION(invalidCount == ranCount, "Sensitivity test."); + m_DataManager->Remove(m_DataNode3); + CPPUNIT_ASSERT_MESSAGE("Node not correctly removed", m_LevelWindowManager->GetRelevantNodes()->size() == 2); + CPPUNIT_ASSERT_MESSAGE("Observer not correctly removed", m_LevelWindowManager->GetNumberOfObservers() == 2); + + bool isImageForLevelWindow1, isImageForLevelWindow2; + m_DataNode1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); + m_DataNode2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", !isImageForLevelWindow1 && isImageForLevelWindow2); + + auto levelWindowProperty1 = dynamic_cast(m_DataNode1->GetProperty("levelwindow")); + auto levelWindowProperty2 = dynamic_cast(m_DataNode2->GetProperty("levelwindow")); + auto managerLevelWindowProperty = m_LevelWindowManager->GetLevelWindowProperty(); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", + (false == (managerLevelWindowProperty == levelWindowProperty1)) && + (true == (managerLevelWindowProperty == levelWindowProperty2))); + + m_DataManager->Remove(m_DataNode2); + CPPUNIT_ASSERT_MESSAGE("Node not correctly removed", m_LevelWindowManager->GetRelevantNodes()->size() == 1); + CPPUNIT_ASSERT_MESSAGE("Observer not correctly removed", m_LevelWindowManager->GetNumberOfObservers() == 1); + + m_DataNode1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); + CPPUNIT_ASSERT_MESSAGE("\"imageForLevelWindow\" property not correctly set", isImageForLevelWindow1); + + levelWindowProperty1 = dynamic_cast(m_DataNode1->GetProperty("levelwindow")); + managerLevelWindowProperty = m_LevelWindowManager->GetLevelWindowProperty(); + CPPUNIT_ASSERT_MESSAGE("\"levelwindow\" property not correctly set", true == (managerLevelWindowProperty == levelWindowProperty1)); + + m_DataManager->Remove(m_DataNode1); + CPPUNIT_ASSERT_MESSAGE("Node not correctly removed", m_LevelWindowManager->GetRelevantNodes()->size() == 0); + CPPUNIT_ASSERT_MESSAGE("Observer not correctly removed", m_LevelWindowManager->GetNumberOfObservers() == 0); + + CPPUNIT_ASSERT_MESSAGE("LevelWindowProperty is not null", !m_LevelWindowManager->GetLevelWindowProperty()); } }; -int mitkLevelWindowManagerTest(int argc, char *args[]) -{ - MITK_TEST_BEGIN("mitkLevelWindowManager"); - - MITK_TEST_CONDITION_REQUIRED(argc >= 2, "Testing if test file is given."); - std::string testImage = args[1]; - - mitkLevelWindowManagerTestClass::TestInstantiation(); - mitkLevelWindowManagerTestClass::TestSetGetDataStorage(); - mitkLevelWindowManagerTestClass::TestMethodsWithInvalidParameters(); - mitkLevelWindowManagerTestClass::TestOtherMethods(); - mitkLevelWindowManagerTestClass::TestRemoveObserver(testImage); - mitkLevelWindowManagerTestClass::TestLevelWindowSliderVisibility(testImage); - mitkLevelWindowManagerTestClass::TestSetLevelWindowProperty(testImage); - mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnVisibilityChange(testImage); - mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnRandomVisibilityChange(testImage); - mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnRandomPropertyChange(testImage); - - MITK_TEST_END(); -} +MITK_TEST_SUITE_REGISTRATION(mitkLevelWindowManager) diff --git a/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp b/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp index dfd7b828f6..dd290731b4 100644 --- a/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp +++ b/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp @@ -1,301 +1,304 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // mitk core #include // mitk qt widgets #include "QmitkLevelWindowPresetDefinitionDialog.h" #include "QmitkLevelWindowRangeChangeDialog.h" // qt #include QmitkLevelWindowWidgetContextMenu::QmitkLevelWindowWidgetContextMenu(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { m_LevelWindowPreset = mitk::LevelWindowPreset::New(); m_LevelWindowPreset->LoadPreset(); } QmitkLevelWindowWidgetContextMenu::~QmitkLevelWindowWidgetContextMenu() { m_LevelWindowPreset->Delete(); } void QmitkLevelWindowWidgetContextMenu::OnSetPreset(const QAction *presetAction) { QString item = presetAction->text(); if (!(presetAction == m_PresetAction)) { double dlevel = m_LevelWindowPreset->getLevel(item.toStdString()); double dwindow = m_LevelWindowPreset->getWindow(item.toStdString()); if ((dlevel + dwindow / 2) > m_LevelWindow.GetRangeMax()) { double lowerBound = (dlevel - dwindow / 2); if (!(lowerBound > m_LevelWindow.GetRangeMax())) { dwindow = m_LevelWindow.GetRangeMax() - lowerBound; dlevel = lowerBound + dwindow / 2; } else { dlevel = m_LevelWindow.GetRangeMax() - 1; dwindow = 2; } } else if ((dlevel - dwindow / 2) < m_LevelWindow.GetRangeMin()) { double upperBound = (dlevel + dwindow / 2); if (!(upperBound < m_LevelWindow.GetRangeMin())) { dwindow = m_LevelWindow.GetRangeMin() + upperBound; dlevel = upperBound - dwindow / 2; } else { dlevel = m_LevelWindow.GetRangeMin() + 1; dwindow = 2; } } m_LevelWindow.SetLevelWindow(dlevel, dwindow); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLevelWindowWidgetContextMenu::SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) { m_Manager = levelWindowManager; } void QmitkLevelWindowWidgetContextMenu::OnAddPreset() { QmitkLevelWindowPresetDefinitionDialog addPreset(this); addPreset.setPresets(m_LevelWindowPreset->getLevelPresets(), m_LevelWindowPreset->getWindowPresets(), QString::number((int)m_LevelWindow.GetLevel()), QString::number((int)m_LevelWindow.GetWindow())); if (addPreset.exec()) { m_LevelWindowPreset->newPresets(addPreset.getLevelPresets(), addPreset.getWindowPresets()); } } void QmitkLevelWindowWidgetContextMenu::OnSetFixed() { m_LevelWindow.SetFixed(!m_LevelWindow.GetFixed()); m_Manager->SetLevelWindow(m_LevelWindow); } void QmitkLevelWindowWidgetContextMenu::OnUseAllGreyvaluesFromImage() { m_LevelWindow.SetToImageRange(m_Manager->GetCurrentImage()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnUseOptimizedLevelWindow() { m_LevelWindow.SetAuto(m_Manager->GetCurrentImage(), false, false); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnSetDefaultLevelWindow() { m_LevelWindow.ResetDefaultLevelWindow(); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnSetMaximumWindow() { m_LevelWindow.SetToMaxWindowSize(); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnSetDefaultScaleRange() { m_LevelWindow.ResetDefaultRangeMinMax(); m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), m_LevelWindow.GetWindow()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLevelWindowWidgetContextMenu::OnChangeScaleRange() { QmitkLevelWindowRangeChangeDialog changeRange(this); changeRange.setLowerLimit((mitk::ScalarType)m_LevelWindow.GetRangeMin()); changeRange.setUpperLimit((mitk::ScalarType)m_LevelWindow.GetRangeMax()); if (changeRange.exec()) { m_LevelWindow.SetRangeMinMax(changeRange.getLowerLimit(), changeRange.getUpperLimit()); m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), m_LevelWindow.GetWindow()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLevelWindowWidgetContextMenu::OnSetImage(QAction *imageAction) { if (imageAction == m_AutoTopmostAction) { if (m_Manager->IsAutoTopMost() == false) { m_Manager->SetAutoTopMostImage(true); + m_SelectedImagesAction->setChecked(false); } else { m_Manager->SetAutoTopMostImage(false); } } else if(imageAction == m_SelectedImagesAction) { if (m_Manager->IsSelectedImages() == false) { m_Manager->SetSelectedImages(true); + m_AutoTopmostAction->setChecked(false); } else { m_Manager->SetSelectedImages(false); } } else { + m_AutoTopmostAction->setChecked(false); + m_SelectedImagesAction->setChecked(false); m_Manager->SetLevelWindowProperty(m_Images.at(imageAction)); } } void QmitkLevelWindowWidgetContextMenu::GetContextMenu(QMenu *contextMenu) { if (nullptr == contextMenu) { return; } try { m_LevelWindow = m_Manager->GetLevelWindow(); QAction *sliderFixed = contextMenu->addAction(tr("Set slider fixed"), this, &QmitkLevelWindowWidgetContextMenu::OnSetFixed); sliderFixed->setCheckable(true); sliderFixed->setChecked(m_LevelWindow.IsFixed()); contextMenu->addSeparator(); contextMenu->addAction(tr("Use whole image grey values"), this, &QmitkLevelWindowWidgetContextMenu::OnUseAllGreyvaluesFromImage); contextMenu->addAction(tr("Use optimized level-window"), this, &QmitkLevelWindowWidgetContextMenu::OnUseOptimizedLevelWindow); contextMenu->addSeparator(); contextMenu->addAction(tr("Set maximum window"), this, &QmitkLevelWindowWidgetContextMenu::OnSetMaximumWindow); contextMenu->addAction(tr("Default level-window"), this, &QmitkLevelWindowWidgetContextMenu::OnSetDefaultLevelWindow); contextMenu->addSeparator(); contextMenu->addAction(tr("Change scale range"), this, &QmitkLevelWindowWidgetContextMenu::OnChangeScaleRange); contextMenu->addAction(tr("Default scale range"), this, &QmitkLevelWindowWidgetContextMenu::OnSetDefaultScaleRange); contextMenu->addSeparator(); m_PresetSubmenu = new QMenu(this); m_PresetSubmenu->setTitle("Presets"); m_PresetAction = m_PresetSubmenu->addAction(tr("Preset definition"), this, &QmitkLevelWindowWidgetContextMenu::OnAddPreset); m_PresetSubmenu->addSeparator(); std::map preset = m_LevelWindowPreset->getLevelPresets(); for (auto iter = preset.begin(); iter != preset.end(); iter++) { QString item = ((*iter).first.c_str()); m_PresetSubmenu->addAction(item); } connect(m_PresetSubmenu, &QMenu::triggered, this, &QmitkLevelWindowWidgetContextMenu::OnSetPreset); contextMenu->addMenu(m_PresetSubmenu); contextMenu->addSeparator(); m_ImageSubmenu = new QMenu(this); m_ImageSubmenu->setTitle("Images"); // add action for "auto topmost image" action m_AutoTopmostAction = m_ImageSubmenu->addAction(tr("Set topmost image")); m_AutoTopmostAction->setCheckable(true); if (m_Manager->IsAutoTopMost()) { m_AutoTopmostAction->setChecked(true); } // add action for "selected images" action - m_ImageSubmenu->addSeparator(); m_SelectedImagesAction = m_ImageSubmenu->addAction(tr("Use selected images")); m_SelectedImagesAction->setCheckable(true); if (m_Manager->IsSelectedImages()) { m_SelectedImagesAction->setChecked(true); } // add action for individual images m_ImageSubmenu->addSeparator(); mitk::DataStorage::SetOfObjects::ConstPointer allObjects = m_Manager->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator objectIter = allObjects->Begin(); objectIter != allObjects->End(); ++objectIter) { mitk::DataNode *node = objectIter->Value(); if (nullptr == node) { continue; } bool isHelperObject = false; node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) { continue; } if (!node->IsVisible(nullptr)) { continue; } mitk::LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNotNull()) { std::string name; node->GetName(name); QString item = name.c_str(); QAction *id = m_ImageSubmenu->addAction(item); id->setCheckable(true); m_Images[id] = levelWindowProperty; if (levelWindowProperty == m_Manager->GetLevelWindowProperty()) { id->setChecked(true); } } } connect(m_ImageSubmenu, &QMenu::triggered, this, &QmitkLevelWindowWidgetContextMenu::OnSetImage); contextMenu->addMenu(m_ImageSubmenu); contextMenu->exec(QCursor::pos()); } catch (...) { } } void QmitkLevelWindowWidgetContextMenu::GetContextMenu() { auto contextMenu = new QMenu(this); GetContextMenu(contextMenu); delete contextMenu; } diff --git a/Plugins/org.mitk.gui.qt.dicombrowser/src/internal/DicomEventHandler.cpp b/Plugins/org.mitk.gui.qt.dicombrowser/src/internal/DicomEventHandler.cpp index ac03ea8e63..a33ad356bd 100644 --- a/Plugins/org.mitk.gui.qt.dicombrowser/src/internal/DicomEventHandler.cpp +++ b/Plugins/org.mitk.gui.qt.dicombrowser/src/internal/DicomEventHandler.cpp @@ -1,266 +1,258 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPluginActivator.h" #include "DicomEventHandler.h" #include #include #include #include #include #include #include #include #include #include "mitkImage.h" #include #include #include #include #include #include #include #include #include #include "mitkBaseDICOMReaderService.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { mitk::IFileReader* GetReader(mitk::FileReaderRegistry& readerRegistry, const mitk::CustomMimeType& mimeType) { try { return readerRegistry.GetReaders(mitk::MimeType(mimeType, -1, -1)).at(0); } catch (const std::out_of_range&) { mitkThrow() << "Cannot find " << mimeType.GetCategory() << " " << mimeType.GetComment() << " file reader."; } } } DicomEventHandler::DicomEventHandler() { } DicomEventHandler::~DicomEventHandler() { } void DicomEventHandler::OnSignalAddSeriesToDataManager(const ctkEvent& ctkEvent) { QStringList listOfFilesForSeries; listOfFilesForSeries = ctkEvent.getProperty("FilesForSeries").toStringList(); if (!listOfFilesForSeries.isEmpty()) { //for rt data, if the modality tag isn't defined or is "CT" the image is handled like before if(ctkEvent.containsProperty("Modality") && (ctkEvent.getProperty("Modality").toString().compare("RTDOSE",Qt::CaseInsensitive) == 0 || ctkEvent.getProperty("Modality").toString().compare("RTSTRUCT",Qt::CaseInsensitive) == 0 || ctkEvent.getProperty("Modality").toString().compare("RTPLAN", Qt::CaseInsensitive) == 0)) { QString modality = ctkEvent.getProperty("Modality").toString(); mitk::FileReaderRegistry readerRegistry; if(modality.compare("RTDOSE",Qt::CaseInsensitive) == 0) { auto doseReader = GetReader(readerRegistry, mitk::DICOMRTMimeTypes::DICOMRT_DOSE_MIMETYPE()); doseReader->SetInput(ImporterUtil::getUTF8String(listOfFilesForSeries.front())); std::vector > readerOutput = doseReader->Read(); if (!readerOutput.empty()){ mitk::Image::Pointer doseImage = dynamic_cast(readerOutput.at(0).GetPointer()); mitk::DataNode::Pointer doseImageNode = mitk::DataNode::New(); doseImageNode->SetData(doseImage); doseImageNode->SetName("RTDose"); if (doseImage != nullptr) { std::string sopUID; if (mitk::GetBackwardsCompatibleDICOMProperty(0x0008, 0x0016, "dicomseriesreader.SOPClassUID", doseImage->GetPropertyList(), sopUID)) { doseImageNode->SetName(sopUID); }; berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IPreferences::Pointer prefNode = prefService->GetSystemPreferences()->Node(mitk::RTUIConstants::ROOT_DOSE_VIS_PREFERENCE_NODE_ID.c_str()); if (prefNode.IsNull()) { mitkThrow() << "Error in preference interface. Cannot find preset node under given name. Name: " << prefNode->ToString().toStdString(); } //set some specific colorwash and isoline properties bool showColorWashGlobal = prefNode->GetBool(mitk::RTUIConstants::GLOBAL_VISIBILITY_COLORWASH_ID.c_str(), true); //Set reference dose property double referenceDose = prefNode->GetDouble(mitk::RTUIConstants::REFERENCE_DOSE_ID.c_str(), mitk::RTUIConstants::DEFAULT_REFERENCE_DOSE_VALUE); mitk::ConfigureNodeAsDoseNode(doseImageNode, mitk::GenerateIsoLevels_Virtuos(), referenceDose, showColorWashGlobal); ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); dataStorage->Add(doseImageNode); mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); } }//END DOSE } else if(modality.compare("RTSTRUCT",Qt::CaseInsensitive) == 0) { auto structReader = GetReader(readerRegistry, mitk::DICOMRTMimeTypes::DICOMRT_STRUCT_MIMETYPE()); structReader->SetInput(ImporterUtil::getUTF8String(listOfFilesForSeries.front())); std::vector > readerOutput = structReader->Read(); if (readerOutput.empty()){ MITK_ERROR << "No structure sets were created" << endl; } else { std::vector modelVector; ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); for (const auto& aStruct : readerOutput){ mitk::ContourModelSet::Pointer countourModelSet = dynamic_cast(aStruct.GetPointer()); mitk::DataNode::Pointer structNode = mitk::DataNode::New(); structNode->SetData(countourModelSet); structNode->SetProperty("name", aStruct->GetProperty("name")); structNode->SetProperty("color", aStruct->GetProperty("contour.color")); structNode->SetProperty("contour.color", aStruct->GetProperty("contour.color")); structNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); - structNode->SetVisibility(true, mitk::BaseRenderer::GetInstance( - mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget0"))); - structNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( - mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); - structNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( - mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); - structNode->SetVisibility(true, mitk::BaseRenderer::GetInstance( - mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); dataStorage->Add(structNode); } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); } } else if (modality.compare("RTPLAN", Qt::CaseInsensitive) == 0) { auto planReader = GetReader(readerRegistry, mitk::DICOMRTMimeTypes::DICOMRT_PLAN_MIMETYPE()); planReader->SetInput(ImporterUtil::getUTF8String(listOfFilesForSeries.front())); std::vector > readerOutput = planReader->Read(); if (!readerOutput.empty()){ //there is no image, only the properties are interesting mitk::Image::Pointer planDummyImage = dynamic_cast(readerOutput.at(0).GetPointer()); mitk::DataNode::Pointer planImageNode = mitk::DataNode::New(); planImageNode->SetData(planDummyImage); planImageNode->SetName("RTPlan"); ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); dataStorage->Add(planImageNode); } } } else { mitk::StringList seriesToLoad; QStringListIterator it(listOfFilesForSeries); while (it.hasNext()) { seriesToLoad.push_back(ImporterUtil::getUTF8String(it.next())); } //Get Reference for default data storage. ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); std::vector baseDatas = mitk::IOUtil::Load(seriesToLoad.front()); for (const auto &data : baseDatas) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(data); std::string nodeName = mitk::DataNode::NO_NAME_VALUE(); auto nameDataProp = data->GetProperty("name"); if (nameDataProp.IsNotNull()) { //if data has a name property set by reader, use this name nodeName = nameDataProp->GetValueAsString(); } else { //reader didn't specify a name, generate one. nodeName = mitk::GenerateNameFromDICOMProperties(node); } node->SetName(nodeName); dataStorage->Add(node); } } } else { MITK_INFO << "There are no files for the current series"; } } void DicomEventHandler::OnSignalRemoveSeriesFromStorage(const ctkEvent& /*ctkEvent*/) { } void DicomEventHandler::SubscribeSlots() { ctkServiceReference ref = mitk::PluginActivator::getContext()->getServiceReference(); if (ref) { ctkEventAdmin* eventAdmin = mitk::PluginActivator::getContext()->getService(ref); ctkDictionary properties; properties[ctkEventConstants::EVENT_TOPIC] = "org/mitk/gui/qt/dicom/ADD"; eventAdmin->subscribeSlot(this, SLOT(OnSignalAddSeriesToDataManager(ctkEvent)), properties); properties[ctkEventConstants::EVENT_TOPIC] = "org/mitk/gui/qt/dicom/DELETED"; eventAdmin->subscribeSlot(this, SLOT(OnSignalRemoveSeriesFromStorage(ctkEvent)), properties); } } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp index 1007251dc0..98a6bb780f 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp @@ -1,874 +1,877 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkMeasurementView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctkDoubleSpinBox.h" #include "mitkPluginActivator.h" #include "usModuleRegistry.h" #include "mitkInteractionEventObserver.h" #include "mitkDisplayInteractor.h" #include "usGetModuleContext.h" #include "usModuleContext.h" #include US_INITIALIZE_MODULE struct QmitkPlanarFigureData { QmitkPlanarFigureData() : m_EndPlacementObserverTag(0), m_SelectObserverTag(0), m_StartInteractionObserverTag(0), m_EndInteractionObserverTag(0) { } mitk::PlanarFigure::Pointer m_Figure; unsigned int m_EndPlacementObserverTag; unsigned int m_SelectObserverTag; unsigned int m_StartInteractionObserverTag; unsigned int m_EndInteractionObserverTag; }; struct QmitkMeasurementViewData { QmitkMeasurementViewData() : m_LineCounter(0), m_PathCounter(0), m_AngleCounter(0), m_FourPointAngleCounter(0), m_CircleCounter(0), m_EllipseCounter(0), m_DoubleEllipseCounter(0), m_RectangleCounter(0), m_PolygonCounter(0), m_BezierCurveCounter(0), m_SubdivisionPolygonCounter(0), m_UnintializedPlanarFigure(false), m_ScrollEnabled(true), m_Parent(nullptr), m_SingleNodeSelectionWidget(nullptr), m_DrawLine(nullptr), m_DrawPath(nullptr), m_DrawAngle(nullptr), m_DrawFourPointAngle(nullptr), m_DrawRectangle(nullptr), m_DrawPolygon(nullptr), m_DrawCircle(nullptr), m_DrawEllipse(nullptr), m_DrawDoubleEllipse(nullptr), m_DrawBezierCurve(nullptr), m_DrawSubdivisionPolygon(nullptr), m_DrawActionsToolBar(nullptr), m_DrawActionsGroup(nullptr), m_SelectedPlanarFiguresText(nullptr), m_CopyToClipboard(nullptr), m_Layout(nullptr), m_Radius(nullptr), m_Thickness(nullptr), m_FixedParameterBox(nullptr) { } unsigned int m_LineCounter; unsigned int m_PathCounter; unsigned int m_AngleCounter; unsigned int m_FourPointAngleCounter; unsigned int m_CircleCounter; unsigned int m_EllipseCounter; unsigned int m_DoubleEllipseCounter; unsigned int m_RectangleCounter; unsigned int m_PolygonCounter; unsigned int m_BezierCurveCounter; unsigned int m_SubdivisionPolygonCounter; QList m_CurrentSelection; std::map m_DataNodeToPlanarFigureData; mitk::DataNode::Pointer m_SelectedImageNode; bool m_UnintializedPlanarFigure; bool m_ScrollEnabled; QWidget* m_Parent; + QLabel* m_SelectedImageLabel; QmitkSingleNodeSelectionWidget* m_SingleNodeSelectionWidget; QAction* m_DrawLine; QAction* m_DrawPath; QAction* m_DrawAngle; QAction* m_DrawFourPointAngle; QAction* m_DrawRectangle; QAction* m_DrawPolygon; QAction* m_DrawCircle; QAction* m_DrawEllipse; QAction* m_DrawDoubleEllipse; QAction* m_DrawBezierCurve; QAction* m_DrawSubdivisionPolygon; QToolBar* m_DrawActionsToolBar; QActionGroup* m_DrawActionsGroup; QTextBrowser* m_SelectedPlanarFiguresText; QPushButton* m_CopyToClipboard; QGridLayout* m_Layout; ctkDoubleSpinBox* m_Radius; ctkDoubleSpinBox* m_Thickness; QGroupBox* m_FixedParameterBox; }; const std::string QmitkMeasurementView::VIEW_ID = "org.mitk.views.measurement"; QmitkMeasurementView::QmitkMeasurementView() : d(new QmitkMeasurementViewData) { } QmitkMeasurementView::~QmitkMeasurementView() { auto planarFigures = this->GetAllPlanarFigures(); for (auto it = planarFigures->Begin(); it != planarFigures->End(); ++it) this->NodeRemoved(it.Value()); delete d; } void QmitkMeasurementView::CreateQtPartControl(QWidget* parent) { d->m_Parent = parent; d->m_SingleNodeSelectionWidget = new QmitkSingleNodeSelectionWidget(); d->m_SingleNodeSelectionWidget->SetDataStorage(GetDataStorage()); d->m_SingleNodeSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); d->m_SingleNodeSelectionWidget->SetSelectionIsOptional(true); d->m_SingleNodeSelectionWidget->SetEmptyInfo(QStringLiteral("Please select a reference image")); d->m_SingleNodeSelectionWidget->SetPopUpTitel(QStringLiteral("Select a reference image")); d->m_DrawActionsToolBar = new QToolBar; d->m_DrawActionsGroup = new QActionGroup(this); d->m_DrawActionsGroup->setExclusive(true); auto* currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/line.png"), tr("Draw Line")); currentAction->setCheckable(true); d->m_DrawLine = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/path.png"), tr("Draw Path")); currentAction->setCheckable(true); d->m_DrawPath = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/angle.png"), tr("Draw Angle")); currentAction->setCheckable(true); d->m_DrawAngle = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/four-point-angle.png"), tr("Draw Four Point Angle")); currentAction->setCheckable(true); d->m_DrawFourPointAngle = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/circle.png"), tr("Draw Circle")); currentAction->setCheckable(true); d->m_DrawCircle = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/ellipse.png"), tr("Draw Ellipse")); currentAction->setCheckable(true); d->m_DrawEllipse = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/doubleellipse.png"), tr("Draw Double Ellipse")); currentAction->setCheckable(true); d->m_DrawDoubleEllipse = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/rectangle.png"), tr("Draw Rectangle")); currentAction->setCheckable(true); d->m_DrawRectangle = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/polygon.png"), tr("Draw Polygon")); currentAction->setCheckable(true); d->m_DrawPolygon = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/beziercurve.png"), tr("Draw Bezier Curve")); currentAction->setCheckable(true); d->m_DrawBezierCurve = currentAction; currentAction = d->m_DrawActionsToolBar->addAction(QIcon(":/measurement/subdivisionpolygon.png"), tr("Draw Subdivision Polygon")); currentAction->setCheckable(true); d->m_DrawSubdivisionPolygon = currentAction; d->m_DrawActionsToolBar->setEnabled(false); // fixed parameter section auto fixedLayout = new QGridLayout(); d->m_FixedParameterBox = new QGroupBox(); d->m_FixedParameterBox->setCheckable(true); d->m_FixedParameterBox->setChecked(false); d->m_FixedParameterBox->setTitle("Fixed sized circle/double ellipse"); d->m_FixedParameterBox->setToolTip("If activated, circles and double ellipses (as rings) figures will always be created with the set parameters as fixed size."); d->m_FixedParameterBox->setAlignment(Qt::AlignLeft); auto labelRadius1 = new QLabel(QString("Radius")); d->m_Radius = new ctkDoubleSpinBox(); d->m_Radius->setMinimum(0); d->m_Radius->setValue(10); d->m_Radius->setSuffix(" mm"); d->m_Radius->setAlignment(Qt::AlignLeft); d->m_Radius->setToolTip("Sets the radius for following planar figures: circle, double ellipse (as ring)."); auto labelThickness = new QLabel(QString("Thickness")); d->m_Thickness = new ctkDoubleSpinBox(); d->m_Thickness->setMinimum(0); d->m_Thickness->setMaximum(10); d->m_Thickness->setValue(5); d->m_Thickness->setSuffix(" mm"); d->m_Thickness->setAlignment(Qt::AlignLeft); d->m_Thickness->setToolTip("Sets the thickness for following planar figures: double ellipse (as ring)."); fixedLayout->addWidget(labelRadius1,0,0); fixedLayout->addWidget(d->m_Radius,0,1); fixedLayout->addWidget(labelThickness,1,0); fixedLayout->addWidget(d->m_Thickness,1,1); d->m_FixedParameterBox->setLayout(fixedLayout); // planar figure details text d->m_SelectedPlanarFiguresText = new QTextBrowser; // copy to clipboard button d->m_CopyToClipboard = new QPushButton(tr("Copy to Clipboard")); + d->m_SelectedImageLabel = new QLabel("Selected Image"); d->m_Layout = new QGridLayout; - d->m_Layout->addWidget(d->m_SingleNodeSelectionWidget, 0, 0, 1, 2); + d->m_Layout->addWidget(d->m_SelectedImageLabel, 0, 0); + d->m_Layout->addWidget(d->m_SingleNodeSelectionWidget, 0, 1, 1, 1); d->m_Layout->addWidget(d->m_DrawActionsToolBar, 1, 0, 1, 2); d->m_Layout->addWidget(d->m_FixedParameterBox, 2, 0, 1, 2); d->m_Layout->addWidget(d->m_SelectedPlanarFiguresText, 3, 0, 1, 2); d->m_Layout->addWidget(d->m_CopyToClipboard, 4, 0, 1, 2); d->m_Parent->setLayout(d->m_Layout); this->CreateConnections(); this->AddAllInteractors(); // placed after CreateConnections to trigger update of the current selection d->m_SingleNodeSelectionWidget->SetAutoSelectNewNodes(true); } void QmitkMeasurementView::CreateConnections() { connect(d->m_SingleNodeSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMeasurementView::OnCurrentSelectionChanged); connect(d->m_DrawLine, SIGNAL(triggered(bool)), this, SLOT(OnDrawLineTriggered(bool))); connect(d->m_DrawPath, SIGNAL(triggered(bool)), this, SLOT(OnDrawPathTriggered(bool))); connect(d->m_DrawAngle, SIGNAL(triggered(bool)), this, SLOT(OnDrawAngleTriggered(bool))); connect(d->m_DrawFourPointAngle, SIGNAL(triggered(bool)), this, SLOT(OnDrawFourPointAngleTriggered(bool))); connect(d->m_DrawCircle, SIGNAL(triggered(bool)), this, SLOT(OnDrawCircleTriggered(bool))); connect(d->m_DrawEllipse, SIGNAL(triggered(bool)), this, SLOT(OnDrawEllipseTriggered(bool))); connect(d->m_DrawDoubleEllipse, SIGNAL(triggered(bool)), this, SLOT(OnDrawDoubleEllipseTriggered(bool))); connect(d->m_DrawRectangle, SIGNAL(triggered(bool)), this, SLOT(OnDrawRectangleTriggered(bool))); connect(d->m_DrawPolygon, SIGNAL(triggered(bool)), this, SLOT(OnDrawPolygonTriggered(bool))); connect(d->m_DrawBezierCurve, SIGNAL(triggered(bool)), this, SLOT(OnDrawBezierCurveTriggered(bool))); connect(d->m_DrawSubdivisionPolygon, SIGNAL(triggered(bool)), this, SLOT(OnDrawSubdivisionPolygonTriggered(bool))); connect(d->m_CopyToClipboard, SIGNAL(clicked(bool)), this, SLOT(OnCopyToClipboard(bool))); connect(d->m_Radius, QOverload::of(&ctkDoubleSpinBox::valueChanged), d->m_Thickness, &ctkDoubleSpinBox::setMaximum); } void QmitkMeasurementView::OnCurrentSelectionChanged(QList nodes) { if (nodes.empty() || nodes.front().IsNull()) { d->m_SelectedImageNode = nullptr; d->m_DrawActionsToolBar->setEnabled(false); } else { d->m_SelectedImageNode = nodes.front(); d->m_DrawActionsToolBar->setEnabled(true); } } void QmitkMeasurementView::NodeAdded(const mitk::DataNode* node) { // add observer for selection in renderwindow mitk::PlanarFigure::Pointer planarFigure = dynamic_cast(node->GetData()); auto isPositionMarker = false; node->GetBoolProperty("isContourMarker", isPositionMarker); if (planarFigure.IsNotNull() && !isPositionMarker) { auto nonConstNode = const_cast(node); mitk::PlanarFigureInteractor::Pointer interactor = dynamic_cast(node->GetDataInteractor().GetPointer()); if (interactor.IsNull()) { interactor = mitk::PlanarFigureInteractor::New(); auto planarFigureModule = us::ModuleRegistry::GetModule("MitkPlanarFigure"); interactor->LoadStateMachine("PlanarFigureInteraction.xml", planarFigureModule); interactor->SetEventConfig("PlanarFigureConfig.xml", planarFigureModule); } interactor->SetDataNode(nonConstNode); QmitkPlanarFigureData data; data.m_Figure = planarFigure; typedef itk::SimpleMemberCommand SimpleCommandType; typedef itk::MemberCommand MemberCommandType; // add observer for event when figure has been placed auto initializationCommand = SimpleCommandType::New(); initializationCommand->SetCallbackFunction(this, &QmitkMeasurementView::PlanarFigureInitialized); data.m_EndPlacementObserverTag = planarFigure->AddObserver(mitk::EndPlacementPlanarFigureEvent(), initializationCommand); // add observer for event when figure is picked (selected) auto selectCommand = MemberCommandType::New(); selectCommand->SetCallbackFunction(this, &QmitkMeasurementView::PlanarFigureSelected); data.m_SelectObserverTag = planarFigure->AddObserver(mitk::SelectPlanarFigureEvent(), selectCommand); // add observer for event when interaction with figure starts auto startInteractionCommand = SimpleCommandType::New(); startInteractionCommand->SetCallbackFunction(this, &QmitkMeasurementView::DisableCrosshairNavigation); data.m_StartInteractionObserverTag = planarFigure->AddObserver(mitk::StartInteractionPlanarFigureEvent(), startInteractionCommand); // add observer for event when interaction with figure starts auto endInteractionCommand = SimpleCommandType::New(); endInteractionCommand->SetCallbackFunction(this, &QmitkMeasurementView::EnableCrosshairNavigation); data.m_EndInteractionObserverTag = planarFigure->AddObserver(mitk::EndInteractionPlanarFigureEvent(), endInteractionCommand); // adding to the map of tracked planarfigures d->m_DataNodeToPlanarFigureData[nonConstNode] = data; } } void QmitkMeasurementView::NodeChanged(const mitk::DataNode* node) { auto it = std::find(d->m_CurrentSelection.begin(), d->m_CurrentSelection.end(), node); if (it != d->m_CurrentSelection.end()) { this->UpdateMeasurementText(); } } void QmitkMeasurementView::NodeRemoved(const mitk::DataNode* node) { auto nonConstNode = const_cast(node); auto it = d->m_DataNodeToPlanarFigureData.find(nonConstNode); auto isFigureFinished = false; auto isPlaced = false; if (it != d->m_DataNodeToPlanarFigureData.end()) { QmitkPlanarFigureData& data = it->second; data.m_Figure->RemoveObserver(data.m_EndPlacementObserverTag); data.m_Figure->RemoveObserver(data.m_SelectObserverTag); data.m_Figure->RemoveObserver(data.m_StartInteractionObserverTag); data.m_Figure->RemoveObserver(data.m_EndInteractionObserverTag); isFigureFinished = data.m_Figure->GetPropertyList()->GetBoolProperty("initiallyplaced", isPlaced); if (!isFigureFinished) // if the property does not yet exist or is false, drop the datanode this->PlanarFigureInitialized(); // normally called when a figure is finished, to reset all buttons d->m_DataNodeToPlanarFigureData.erase( it ); } if (nonConstNode != nullptr) nonConstNode->SetDataInteractor(nullptr); auto isPlanarFigure = mitk::TNodePredicateDataType::New(); auto nodes = this->GetDataStorage()->GetDerivations(node, isPlanarFigure); for (unsigned int x = 0; x < nodes->size(); ++x) { mitk::PlanarFigure::Pointer planarFigure = dynamic_cast(nodes->at(x)->GetData()); if (planarFigure.IsNotNull()) { isFigureFinished = planarFigure->GetPropertyList()->GetBoolProperty("initiallyplaced",isPlaced); if (!isFigureFinished) // if the property does not yet exist or is false, drop the datanode { this->GetDataStorage()->Remove(nodes->at(x)); if (!d->m_DataNodeToPlanarFigureData.empty()) { it = d->m_DataNodeToPlanarFigureData.find(nodes->at(x)); if (it != d->m_DataNodeToPlanarFigureData.end()) { d->m_DataNodeToPlanarFigureData.erase(it); this->PlanarFigureInitialized(); // normally called when a figure is finished, to reset all buttons this->EnableCrosshairNavigation(); } } } } } } void QmitkMeasurementView::PlanarFigureSelected(itk::Object* object, const itk::EventObject&) { d->m_CurrentSelection.clear(); auto lambda = [&object](const std::pair& element) { return element.second.m_Figure == object; }; auto it = std::find_if(d->m_DataNodeToPlanarFigureData.begin(), d->m_DataNodeToPlanarFigureData.end(), lambda); if (it != d->m_DataNodeToPlanarFigureData.end()) { d->m_CurrentSelection.push_back(it->first); } this->UpdateMeasurementText(); this->RequestRenderWindowUpdate(); } void QmitkMeasurementView::PlanarFigureInitialized() { d->m_UnintializedPlanarFigure = false; d->m_DrawActionsToolBar->setEnabled(true); d->m_DrawLine->setChecked(false); d->m_DrawPath->setChecked(false); d->m_DrawAngle->setChecked(false); d->m_DrawFourPointAngle->setChecked(false); d->m_DrawCircle->setChecked(false); d->m_DrawEllipse->setChecked(false); d->m_DrawDoubleEllipse->setChecked(false); d->m_DrawRectangle->setChecked(false); d->m_DrawPolygon->setChecked(false); d->m_DrawBezierCurve->setChecked(false); d->m_DrawSubdivisionPolygon->setChecked(false); } void QmitkMeasurementView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& nodes) { d->m_CurrentSelection = nodes; this->UpdateMeasurementText(); // bug 16600: deselecting all planarfigures by clicking on datamanager when no node is selected if (d->m_CurrentSelection.size() == 0) { auto isPlanarFigure = mitk::TNodePredicateDataType::New(); auto planarFigures = this->GetDataStorage()->GetSubset(isPlanarFigure); // setting all planar figures which are not helper objects not selected for (mitk::DataStorage::SetOfObjects::ConstIterator it = planarFigures->Begin(); it != planarFigures->End(); ++it) { auto node = it.Value(); auto isHelperObject = false; node->GetBoolProperty("helper object", isHelperObject); if (!isHelperObject) node->SetSelected(false); } } for (int i = d->m_CurrentSelection.size() - 1; i >= 0; --i) { auto node = d->m_CurrentSelection[i]; mitk::PlanarFigure::Pointer planarFigure = dynamic_cast(node->GetData()); // the last selected planar figure if (planarFigure.IsNotNull() && planarFigure->GetPlaneGeometry()) { auto planarFigureInitializedWindow = false; auto linkedRenderWindow = dynamic_cast(this->GetRenderWindowPart()); QmitkRenderWindow* selectedRenderWindow; if (!linkedRenderWindow) return; auto axialRenderWindow = linkedRenderWindow->GetQmitkRenderWindow("axial"); auto sagittalRenderWindow = linkedRenderWindow->GetQmitkRenderWindow("sagittal"); auto coronalRenderWindow = linkedRenderWindow->GetQmitkRenderWindow("coronal"); auto threeDimRenderWindow = linkedRenderWindow->GetQmitkRenderWindow("3d"); if (node->GetBoolProperty("planarFigureInitializedWindow", planarFigureInitializedWindow, axialRenderWindow->GetRenderer())) { selectedRenderWindow = axialRenderWindow; } else if (node->GetBoolProperty("planarFigureInitializedWindow", planarFigureInitializedWindow, sagittalRenderWindow->GetRenderer())) { selectedRenderWindow = sagittalRenderWindow; } else if (node->GetBoolProperty("planarFigureInitializedWindow", planarFigureInitializedWindow, coronalRenderWindow->GetRenderer())) { selectedRenderWindow = coronalRenderWindow; } else if (node->GetBoolProperty("planarFigureInitializedWindow", planarFigureInitializedWindow, threeDimRenderWindow->GetRenderer())) { selectedRenderWindow = threeDimRenderWindow; } else { selectedRenderWindow = nullptr; } auto planeGeometry = dynamic_cast(planarFigure->GetPlaneGeometry()); auto normal = planeGeometry->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer axialPlane = axialRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); auto axialNormal = axialPlane->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer sagittalPlane = sagittalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); auto sagittalNormal = sagittalPlane->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer coronalPlane = coronalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); auto coronalNormal = coronalPlane->GetNormalVnl(); normal[0] = fabs(normal[0]); normal[1] = fabs(normal[1]); normal[2] = fabs(normal[2]); axialNormal[0] = fabs(axialNormal[0]); axialNormal[1] = fabs(axialNormal[1]); axialNormal[2] = fabs(axialNormal[2]); sagittalNormal[0] = fabs(sagittalNormal[0]); sagittalNormal[1] = fabs(sagittalNormal[1]); sagittalNormal[2] = fabs(sagittalNormal[2]); coronalNormal[0] = fabs(coronalNormal[0]); coronalNormal[1] = fabs(coronalNormal[1]); coronalNormal[2] = fabs(coronalNormal[2]); auto ang1 = angle(normal, axialNormal); auto ang2 = angle(normal, sagittalNormal); auto ang3 = angle(normal, coronalNormal); if (ang1 < ang2 && ang1 < ang3) { selectedRenderWindow = axialRenderWindow; } else { if (ang2 < ang3) { selectedRenderWindow = sagittalRenderWindow; } else { selectedRenderWindow = coronalRenderWindow; } } // re-orient view if (selectedRenderWindow) selectedRenderWindow->GetSliceNavigationController()->ReorientSlices(planeGeometry->GetOrigin(), planeGeometry->GetNormal()); } break; } this->RequestRenderWindowUpdate(); } void QmitkMeasurementView::OnDrawLineTriggered(bool) { this->AddFigureToDataStorage( mitk::PlanarLine::New(), QString("Line%1").arg(++d->m_LineCounter)); } void QmitkMeasurementView::OnDrawPathTriggered(bool) { mitk::CoreServicePointer propertyFilters(mitk::CoreServices::GetPropertyFilters()); mitk::PropertyFilter filter; filter.AddEntry("ClosedPlanarPolygon", mitk::PropertyFilter::Blacklist); propertyFilters->AddFilter(filter, "PlanarPolygon"); mitk::PlanarPolygon::Pointer planarFigure = mitk::PlanarPolygon::New(); planarFigure->ClosedOff(); auto node = this->AddFigureToDataStorage( planarFigure, QString("Path%1").arg(++d->m_PathCounter)); node->SetProperty("ClosedPlanarPolygon", mitk::BoolProperty::New(false)); node->SetProperty("planarfigure.isextendable", mitk::BoolProperty::New(true)); } void QmitkMeasurementView::OnDrawAngleTriggered(bool) { this->AddFigureToDataStorage( mitk::PlanarAngle::New(), QString("Angle%1").arg(++d->m_AngleCounter)); } void QmitkMeasurementView::OnDrawFourPointAngleTriggered(bool) { this->AddFigureToDataStorage( mitk::PlanarFourPointAngle::New(), QString("Four Point Angle%1").arg(++d->m_FourPointAngleCounter)); } void QmitkMeasurementView::OnDrawCircleTriggered(bool) { auto circle = (d->m_FixedParameterBox->isChecked()) ? mitk::PlanarCircle::New(d->m_Radius->value()) : mitk::PlanarCircle::New(); this->AddFigureToDataStorage(circle, QString("Circle%1").arg(++d->m_CircleCounter)); } void QmitkMeasurementView::OnDrawEllipseTriggered(bool) { this->AddFigureToDataStorage( mitk::PlanarEllipse::New(), QString("Ellipse%1").arg(++d->m_EllipseCounter)); } void QmitkMeasurementView::OnDrawDoubleEllipseTriggered(bool) { auto ellipse = (d->m_FixedParameterBox->isChecked()) ? mitk::PlanarDoubleEllipse::New(d->m_Radius->value(),d->m_Thickness->value()) : mitk::PlanarDoubleEllipse::New(); this->AddFigureToDataStorage(ellipse, QString("DoubleEllipse%1").arg(++d->m_DoubleEllipseCounter)); } void QmitkMeasurementView::OnDrawBezierCurveTriggered(bool) { this->AddFigureToDataStorage( mitk::PlanarBezierCurve::New(), QString("BezierCurve%1").arg(++d->m_BezierCurveCounter)); } void QmitkMeasurementView::OnDrawSubdivisionPolygonTriggered(bool) { this->AddFigureToDataStorage( mitk::PlanarSubdivisionPolygon::New(), QString("SubdivisionPolygon%1").arg(++d->m_SubdivisionPolygonCounter)); } void QmitkMeasurementView::OnDrawRectangleTriggered(bool) { this->AddFigureToDataStorage( mitk::PlanarRectangle::New(), QString("Rectangle%1").arg(++d->m_RectangleCounter)); } void QmitkMeasurementView::OnDrawPolygonTriggered(bool) { auto planarFigure = mitk::PlanarPolygon::New(); planarFigure->ClosedOn(); auto node = this->AddFigureToDataStorage( planarFigure, QString("Polygon%1").arg(++d->m_PolygonCounter)); node->SetProperty("planarfigure.isextendable", mitk::BoolProperty::New(true)); } void QmitkMeasurementView::OnCopyToClipboard(bool) { QApplication::clipboard()->setText(d->m_SelectedPlanarFiguresText->toPlainText(), QClipboard::Clipboard); } mitk::DataNode::Pointer QmitkMeasurementView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name) { auto newNode = mitk::DataNode::New(); newNode->SetName(name.toStdString()); newNode->SetData(figure); newNode->SetSelected(true); if (d->m_SelectedImageNode.IsNotNull()) { this->GetDataStorage()->Add(newNode, d->m_SelectedImageNode); } else { this->GetDataStorage()->Add(newNode); } for (auto &node : d->m_CurrentSelection) node->SetSelected(false); d->m_CurrentSelection.clear(); d->m_CurrentSelection.push_back(newNode); this->UpdateMeasurementText(); this->DisableCrosshairNavigation(); d->m_DrawActionsToolBar->setEnabled(false); d->m_UnintializedPlanarFigure = true; return newNode; } void QmitkMeasurementView::UpdateMeasurementText() { d->m_SelectedPlanarFiguresText->clear(); QString infoText; QString plainInfoText; int j = 1; mitk::PlanarFigure::Pointer planarFigure; mitk::PlanarAngle::Pointer planarAngle; mitk::PlanarFourPointAngle::Pointer planarFourPointAngle; mitk::DataNode::Pointer node; for (int i = 0; i < d->m_CurrentSelection.size(); ++i, ++j) { plainInfoText.clear(); node = d->m_CurrentSelection[i]; planarFigure = dynamic_cast(node->GetData()); if (planarFigure.IsNull()) continue; if (j > 1) infoText.append("
"); infoText.append(QString("%1
").arg(QString::fromStdString(node->GetName()))); plainInfoText.append(QString("%1").arg(QString::fromStdString(node->GetName()))); planarAngle = dynamic_cast (planarFigure.GetPointer()); if (planarAngle.IsNull()) planarFourPointAngle = dynamic_cast (planarFigure.GetPointer()); double featureQuantity = 0.0; for (unsigned int k = 0; k < planarFigure->GetNumberOfFeatures(); ++k) { if (!planarFigure->IsFeatureActive(k)) continue; featureQuantity = planarFigure->GetQuantity(k); if ((planarAngle.IsNotNull() && k == planarAngle->FEATURE_ID_ANGLE) || (planarFourPointAngle.IsNotNull() && k == planarFourPointAngle->FEATURE_ID_ANGLE)) featureQuantity = featureQuantity * 180 / vnl_math::pi; infoText.append(QString("%1: %2 %3") .arg(QString(planarFigure->GetFeatureName(k))) .arg(featureQuantity, 0, 'f', 2) .arg(QString(planarFigure->GetFeatureUnit(k)))); plainInfoText.append(QString("\n%1: %2 %3") .arg(QString(planarFigure->GetFeatureName(k))) .arg(featureQuantity, 0, 'f', 2) .arg(QString(planarFigure->GetFeatureUnit(k)))); if (k + 1 != planarFigure->GetNumberOfFeatures()) infoText.append("
"); } if (j != d->m_CurrentSelection.size()) infoText.append("
"); } d->m_SelectedPlanarFiguresText->setHtml(infoText); } void QmitkMeasurementView::AddAllInteractors() { auto planarFigures = this->GetAllPlanarFigures(); for (auto it = planarFigures->Begin(); it != planarFigures->End(); ++it) this->NodeAdded(it.Value()); } void QmitkMeasurementView::EnableCrosshairNavigation() { // enable the crosshair navigation // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (const auto& displayInteractorConfig : m_DisplayInteractorConfigs) { if (displayInteractorConfig.first) { auto displayInteractor = static_cast(us::GetModuleContext()->GetService(displayInteractorConfig.first)); if (displayInteractor != nullptr) { // here the regular configuration is loaded again displayInteractor->SetEventConfig(displayInteractorConfig.second); } } } m_DisplayInteractorConfigs.clear(); d->m_ScrollEnabled = true; } void QmitkMeasurementView::DisableCrosshairNavigation() { // dont deactivate twice, else we will clutter the config list ... if (d->m_ScrollEnabled == false) return; // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction will still be enabled m_DisplayInteractorConfigs.clear(); auto eventObservers = us::GetModuleContext()->GetServiceReferences(); for (const auto& eventObserver : eventObservers) { auto displayInteractor = dynamic_cast(us::GetModuleContext()->GetService(eventObserver)); if (displayInteractor != nullptr) { // remember the original configuration m_DisplayInteractorConfigs.insert(std::make_pair(eventObserver, displayInteractor->GetEventConfig())); // here the alternative configuration is loaded displayInteractor->SetEventConfig("DisplayConfigMITKLimited.xml"); } } d->m_ScrollEnabled = false; } mitk::DataStorage::SetOfObjects::ConstPointer QmitkMeasurementView::GetAllPlanarFigures() const { auto isPlanarFigure = mitk::TNodePredicateDataType::New(); auto isNotHelperObject = mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(false)); auto isNotHelperButPlanarFigure = mitk::NodePredicateAnd::New( isPlanarFigure, isNotHelperObject ); return this->GetDataStorage()->GetSubset(isPlanarFigure); } diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui index d055c5a2d9..7bffe9ff5f 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui @@ -1,806 +1,827 @@ QmitkMultiLabelSegmentationControls 0 0 459 844 0 0 0 0 MS Shell Dlg 2 8 50 false false false false QmitkSegmentation 0 0 Data Selection - Patient Image + Selected Image 0 40 Create a new segmentation session ... :/multilabelsegmentation/NewSegmentationSession_48x48.png:/multilabelsegmentation/NewSegmentationSession_48x48.png 28 28 N true Segmentation 0 40 Layers true false Add a layer to the current segmentation session ... - + :/Qmitk/AddLayer_48x48.png:/Qmitk/AddLayer_48x48.png 28 28 true Delete the active layer ... - + :/Qmitk/DeleteLayer_48x48.png:/Qmitk/DeleteLayer_48x48.png 28 28 true Qt::Horizontal 0 20 Change to the previous available layer ... - + :/Qmitk/PreviousLayer_48x48.png:/Qmitk/PreviousLayer_48x48.png 28 28 true Change to the next available layer ... - + :/Qmitk/NextLayer_48x48.png:/Qmitk/NextLayer_48x48.png 28 28 true 0 0 50 30 40 30 + MS Shell Dlg 2 12 + 50 + false + false + false + false Switch to a layer 0 Labels true Add a new label to the current segmentation session ... :/multilabelsegmentation/NewLabel_48x48.png:/multilabelsegmentation/NewLabel_48x48.png 28 28 N true Lock/Unlock exterior ... - + :/Qmitk/UnlockExterior_48x48.png :/Qmitk/LockExterior_48x48.png:/Qmitk/UnlockExterior_48x48.png 28 28 true true Save LabelSet Preset ... - + :/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg:/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg 28 28 true Load LabelSet Preset ... - + :/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg:/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg 28 28 true Qt::Horizontal 0 20 0 34 Show a table with all labels in the current segmentation session >> 28 28 true false Qt::NoArrow 0 0 0 20 0 0 QTabWidget::tab-bar { alignment: middle; } 0 true false 2D Tools 0 0 50 false 0 0 50 false Qt::Vertical 20 40 3D Tools 0 0 50 false 0 0 50 false Qt::Vertical 20 40 0 0 Interpolation 2 QLayout::SetMinimumSize 2 2 2 2 0 0 Disabled 2D Interpolation 3D Interpolation 0 0 1 0 0 QLayout::SetMinimumSize 0 0 0 0 0 0 0 0 + MS Shell Dlg 2 + 8 50 + false false + false + false 0 0 QLayout::SetMinimumSize 0 0 0 0 0 0 0 50 + MS Shell Dlg 2 + 8 50 + false false + false + false 0 0 Qt::Vertical 20 40 m_LabelSetWidget groupBox_DataSelection m_tw2DTools m_gbInterpolation groupBox_Layer groupBox_Labels QmitkSingleNodeSelectionWidget QWidget
QmitkSingleNodeSelectionWidget.h
1
QmitkToolSelectionBox QWidget
QmitkToolSelectionBox.h
QmitkToolGUIArea QWidget
QmitkToolGUIArea.h
QmitkLabelSetWidget QWidget
Qmitk/QmitkLabelSetWidget.h
1
QmitkSliceBasedInterpolatorWidget QWidget
QmitkSliceBasedInterpolatorWidget.h
1
QmitkSurfaceBasedInterpolatorWidget QWidget
QmitkSurfaceBasedInterpolatorWidget.h
1
QmitkToolGUIArea.h QmitkToolSelectionBox.h + + + + +
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationControls.ui index 1b84357ca9..09ab641aa5 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationControls.ui @@ -1,496 +1,529 @@ QmitkSegmentationControls 0 0 237 591 0 0 0 0 MS Shell Dlg 2 8 50 false false false false QmitkSegmentation QLayout::SetMinimumSize 6 6 6 6 0 0 Data Selection 6 6 6 6 QLayout::SetMinimumSize 4 0 0 - Patient Image + Selected Image 0 0 Segmentation 0 0 Create a new segmentation &New... :/segmentation/btnNew.png:/segmentation/btnNew.png Qt::ToolButtonTextOnly - - - - 0 - 40 - - - + + + + 0 + 40 + + + - - - - 0 - 40 - - - + + + + 0 + 40 + + + 0 0 200 0 0 200 0 0 200 0 0 + + + + 200 + 0 + 0 + + + 200 0 0 200 0 0 200 0 0 + + + + 200 + 0 + 0 + + + 84 82 78 84 82 78 84 82 78 + + + + 200 + 0 + 0 + + + + MS Shell Dlg 2 + 8 50 + false false + false + false Please load an image! true 0 0 Qt::LeftToRight QTabWidget::tab-bar { alignment: middle; } QTabWidget::North QTabWidget::Triangular 0 0 0 Qt::LeftToRight false 2D Tools 6 6 6 6 0 0 50 false 0 0 50 false 0 0 50 false Qt::Vertical 20 40 0 0 3D Tools 6 6 6 6 0 0 50 false 0 0 50 false Qt::Vertical 20 40 - - QmitkSingleNodeSelectionWidget - QWidget -
QmitkSingleNodeSelectionWidget.h
- 1 -
- + + QmitkSingleNodeSelectionWidget + QWidget +
QmitkSingleNodeSelectionWidget.h
+ 1 +
+ QmitkToolSelectionBox QWidget
QmitkToolSelectionBox.h
QmitkSlicesInterpolator QWidget
QmitkSlicesInterpolator.h
QmitkToolGUIArea QWidget
QmitkToolGUIArea.h
QmitkToolGUIArea.h QmitkToolSelectionBox.h QmitkSlicesInterpolator.h +