diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/files.cmake b/BlueBerry/Bundles/org.blueberry.ui.qt.help/files.cmake index eb0cb277fe..3481e5662d 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.help/files.cmake +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.help/files.cmake @@ -1,58 +1,59 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES berryHelpContentView.cpp berryHelpEditor.cpp berryHelpEditorFindWidget.cpp berryHelpEditorInput.cpp berryHelpIndexView.cpp berryHelpPerspective.cpp berryHelpPluginActivator.cpp berryHelpSearchView.cpp berryHelpTopicChooser.cpp berryHelpWebView.cpp berryQHelpEngineConfiguration.cpp berryQHelpEngineWrapper.cpp ) set(CPP_FILES ) set(MOC_H_FILES src/internal/berryHelpContentView.h src/internal/berryHelpEditor.h src/internal/berryHelpEditorFindWidget.h src/internal/berryHelpIndexView.h src/internal/berryHelpPerspective.h src/internal/berryHelpPluginActivator.h src/internal/berryHelpSearchView.h src/internal/berryHelpTopicChooser.h src/internal/berryHelpWebView.h src/internal/berryQHelpEngineConfiguration.h src/internal/berryQHelpEngineWrapper.h ) set(CACHED_RESOURCE_FILES plugin.xml - resources/help.png + resources/helpIndex.png + resources/helpSearch.png ) set(QRC_FILES resources/org_blueberry_ui_qt_help.qrc ) set(UI_FILES src/internal/berryHelpTopicChooser.ui ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/plugin.xml b/BlueBerry/Bundles/org.blueberry.ui.qt.help/plugin.xml index 54cf7c71a3..7d7ec224db 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.help/plugin.xml +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.help/plugin.xml @@ -1,43 +1,43 @@ + icon="resources/helpIndex.png" /> + icon="resources/helpSearch.png" /> diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpIndex.png b/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpIndex.png new file mode 100644 index 0000000000..88f80ca0ab Binary files /dev/null and b/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpIndex.png differ diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpSearch.png b/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpSearch.png new file mode 100644 index 0000000000..63334a10c8 Binary files /dev/null and b/BlueBerry/Bundles/org.blueberry.ui.qt.help/resources/helpSearch.png differ diff --git a/CMake/MITKDashboardSetup.cmake b/CMake/MITKDashboardSetup.cmake index fca7a6ddd5..dcf4a81171 100644 --- a/CMake/MITKDashboardSetup.cmake +++ b/CMake/MITKDashboardSetup.cmake @@ -1,160 +1,159 @@ # This file is intended to be included at the end of a custom MITKDashboardScript.TEMPLATE.cmake file list(APPEND CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") # # Automatically determined properties # set(MY_OPERATING_SYSTEM "${CMAKE_HOST_SYSTEM}") # Windows 7, Linux-2.6.32, Darwin... site_name(CTEST_SITE) if(QT_BINARY_DIR) set(QT_QMAKE_EXECUTABLE "${QT_BINARY_DIR}/qmake") else() set(QT_QMAKE_EXECUTABLE "qmake") endif() execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} --version OUTPUT_VARIABLE MY_QT_VERSION RESULT_VARIABLE qmake_error) if(qmake_error) message(FATAL_ERROR "Error when executing ${QT_QMAKE_EXECUTABLE} --version\n${qmake_error}") endif() string(REGEX REPLACE ".*Qt version ([0-9.]+) .*" "\\1" MY_QT_VERSION ${MY_QT_VERSION}) # # Project specific properties # if(NOT CTEST_BUILD_NAME) set(CTEST_BUILD_NAME "${MY_OPERATING_SYSTEM}-${MY_COMPILER}-Qt-${MY_QT_VERSION}-${CTEST_BUILD_CONFIGURATION}") endif() set(PROJECT_BUILD_DIR "MITK-build") set(CTEST_PATH "$ENV{PATH}") if(WIN32) set(ANN_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ANN-build/${CTEST_BUILD_CONFIGURATION}") set(CPPUNIT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/CppUnit-build/${CTEST_BUILD_CONFIGURATION}") set(GLUT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GLUT-build/${CTEST_BUILD_CONFIGURATION}") set(GLEW_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GLEW-build/${CTEST_BUILD_CONFIGURATION}") set(TINYXML_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/tinyxml-build/${CTEST_BUILD_CONFIGURATION}") set(QWT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Qwt-build/${CTEST_BUILD_CONFIGURATION}") set(QXT_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Qxt-build/${CTEST_BUILD_CONFIGURATION}") set(VTK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/VTK-build/bin/${CTEST_BUILD_CONFIGURATION}") set(ACVD_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ACVD-build/bin/${CTEST_BUILD_CONFIGURATION}") set(ITK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ITK-build/bin/${CTEST_BUILD_CONFIGURATION}") set(BOOST_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Boost-install/lib") set(GDCM_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GDCM-build/bin/${CTEST_BUILD_CONFIGURATION}") set(OPENCV_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/OpenCV-build/bin/${CTEST_BUILD_CONFIGURATION}") set(POCO_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Poco-install/lib") set(SOFA_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/SOFA-build/bin/${CTEST_BUILD_CONFIGURATION}") set(BLUEBERRY_OSGI_DIR "${CTEST_BINARY_DIRECTORY}/MITK-build/bin/BlueBerry/org.blueberry.osgi/bin/${CTEST_BUILD_CONFIGURATION}") set(CTEST_PATH "${CTEST_PATH};${CPPUNIT_BINARY_DIR};${QT_BINARY_DIR};${VTK_BINARY_DIR};${ANN_BINARY_DIR};${GLUT_BINARY_DIR};${GLEW_BINARY_DIR};${TINYXML_BINARY_DIR};${QWT_BINARY_DIR};${QXT_BINARY_DIR};${ACVD_BINARY_DIR};${ITK_BINARY_DIR};${BOOST_BINARY_DIR};${GDCM_BINARY_DIR};${OPENCV_BINARY_DIR};${POCO_BINARY_DIR};${SOFA_BINARY_DIR};${BLUEBERRY_OSGI_DIR}") endif() set(ENV{PATH} "${CTEST_PATH}") set(SUPERBUILD_TARGETS "") # If the dashscript doesn't define a GIT_REPOSITORY variable, let's define it here. if(NOT DEFINED GIT_REPOSITORY OR GIT_REPOSITORY STREQUAL "") set(GIT_REPOSITORY "http://git.mitk.org/MITK.git") endif() # # Display build info # message("Site name: ${CTEST_SITE}") message("Build name: ${CTEST_BUILD_NAME}") message("Script Mode: ${SCRIPT_MODE}") message("Coverage: ${WITH_COVERAGE}, MemCheck: ${WITH_MEMCHECK}") # # Set initial cache options # -if(CMAKE_GENERATOR MATCHES "[Mm]ake") +if(${CMAKE_VERSION} VERSION_GREATER "2.8.9") set(CTEST_USE_LAUNCHERS 1) -else() - set(CTEST_USE_LAUNCHERS 0) + set(ENV{CTEST_USE_LAUNCHERS_DEFAULT} 1) endif() # Remove this if block after all dartclients work if(DEFINED ADDITIONNAL_CMAKECACHE_OPTION) message(WARNING "Rename ADDITIONNAL to ADDITIONAL in your dartlclient script: ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") set(ADDITIONAL_CMAKECACHE_OPTION ${ADDITIONNAL_CMAKECACHE_OPTION}) endif() if(NOT DEFINED MITK_USE_ACVD) set(MITK_USE_ACVD 1) endif() if(NOT DEFINED MITK_USE_Boost) set(MITK_USE_Boost 1) endif() if(NOT DEFINED MITK_USE_OpenCV) set(MITK_USE_OpenCV 1) endif() if(NOT DEFINED MITK_USE_Poco) set(MITK_USE_Poco 1) endif() if(NOT DEFINED MITK_USE_SOFA) set(MITK_USE_SOFA 1) endif() if(NOT DEFINED MITK_BUILD_ALL_APPS) set(MITK_BUILD_ALL_APPS TRUE) endif() if(NOT DEFINED BLUEBERRY_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS TRUE) endif() if(NOT DEFINED MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS TRUE) endif() if(NOT DEFINED MITK_BUILD_EXAMPLES) set(MITK_BUILD_EXAMPLES TRUE) endif() if(NOT BUILD_DiffusionMiniApps) set(BUILD_DiffusionMiniApps TRUE) endif() set(INITIAL_CMAKECACHE_OPTIONS " BLUEBERRY_BUILD_ALL_PLUGINS:BOOL=${MITK_BUILD_ALL_PLUGINS} MITK_BUILD_ALL_PLUGINS:BOOL=${MITK_BUILD_ALL_PLUGINS} MITK_BUILD_ALL_APPS:BOOL=${MITK_BUILD_ALL_APPS} MITK_BUILD_EXAMPLES:BOOL=${MITK_BUILD_EXAMPLES} SUPERBUILD_EXCLUDE_MITKBUILD_TARGET:BOOL=TRUE MITK_USE_ACVD:BOOL=${MITK_USE_ACVD} MITK_USE_Boost:BOOL=${MITK_USE_Boost} MITK_USE_OpenCV:BOOL=${MITK_USE_OpenCV} MITK_USE_Poco:BOOL=${MITK_USE_Poco} MITK_USE_SOFA:BOOL=${MITK_USE_SOFA} ${ADDITIONAL_CMAKECACHE_OPTION} ") # Write a cache file for populating the MITK initial cache (not the superbuild cache). # This can be used to provide variables which are not passed through the # superbuild process to the MITK configure step) if(MITK_INITIAL_CACHE) set(mitk_cache_file "${CTEST_SCRIPT_DIRECTORY}/mitk_initial_cache.txt") file(WRITE "${mitk_cache_file}" "${MITK_INITIAL_CACHE}") set(INITIAL_CMAKECACHE_OPTIONS "${INITIAL_CMAKECACHE_OPTIONS} MITK_INITIAL_CACHE_FILE:INTERNAL=${mitk_cache_file} ") endif() # # Download and include dashboard driver script # set(url "http://mitk.org/git/?p=MITK.git;a=blob_plain;f=CMake/MITKDashboardDriverScript.cmake;hb=${hb}") set(dest ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}.driver) downloadFile("${url}" "${dest}") include(${dest}) diff --git a/CMake/mitkMacroCreateModule.cmake b/CMake/mitkMacroCreateModule.cmake index ca54646499..749a056748 100644 --- a/CMake/mitkMacroCreateModule.cmake +++ b/CMake/mitkMacroCreateModule.cmake @@ -1,428 +1,429 @@ ################################################################## # # MITK_CREATE_MODULE # #! Creates a module for the automatic module dependency system within MITK. #! Configurations are generated in the moduleConf directory. #! #! USAGE: #! #! \code #! MITK_CREATE_MODULE( #! [INCLUDE_DIRS ] #! [INTERNAL_INCLUDE_DIRS ] #! [DEPENDS ] #! [PACKAGE_DEPENDS ] #! [TARGET_DEPENDS #! [EXPORT_DEFINE ] #! [QT_MODULE] #! [HEADERS_ONLY] #! [WARNINGS_AS_ERRORS] #! \endcode #! #! \param MODULE_NAME_IN The name for the new module #! \param HEADERS_ONLY specify this if the modules just contains header files. ################################################################## macro(MITK_CREATE_MODULE MODULE_NAME_IN) set(_macro_params SUBPROJECTS # list of CDash labels VERSION # module version number, e.g. "1.2.0" INCLUDE_DIRS # exported include dirs (used in mitkMacroCreateModuleConf.cmake) INTERNAL_INCLUDE_DIRS # include dirs internal to this module DEPENDS # list of modules this module depends on DEPENDS_INTERNAL # list of modules this module internally depends on PACKAGE_DEPENDS # list of "packages this module depends on (e.g. Qt, VTK, etc.) TARGET_DEPENDS # list of CMake targets this module should depend on EXPORT_DEFINE # export macro name for public symbols of this module AUTOLOAD_WITH # a module target name identifying the module which will trigger the # automatic loading of this module ADDITIONAL_LIBS # list of addidtional libraries linked to this module GENERATED_CPP # not used (?) ) set(_macro_options QT_MODULE # the module makes use of Qt features and needs moc and ui generated files FORCE_STATIC # force building this module as a static library HEADERS_ONLY # this module is a headers-only library GCC_DEFAULT_VISIBILITY # do not use gcc visibility flags - all symbols will be exported NO_INIT # do not create CppMicroServices initialization code WARNINGS_AS_ERRORS # treat all compiler warnings as errors ) MACRO_PARSE_ARGUMENTS(MODULE "${_macro_params}" "${_macro_options}" ${ARGN}) set(MODULE_NAME ${MODULE_NAME_IN}) if(MODULE_HEADERS_ONLY) set(MODULE_PROVIDES ) if(MODULE_AUTOLOAD_WITH) message(SEND_ERROR "A headers only module cannot be auto-loaded") endif() else() set(MODULE_PROVIDES ${MODULE_NAME}) if(NOT MODULE_NO_INIT AND NOT MODULE_NAME STREQUAL "Mitk") # Add a dependency to the "Mitk" module #list(APPEND MODULE_DEPENDS Mitk) endif() endif() if(NOT MODULE_SUBPROJECTS) if(MITK_DEFAULT_SUBPROJECTS) set(MODULE_SUBPROJECTS ${MITK_DEFAULT_SUBPROJECTS}) endif() endif() # check if the subprojects exist as targets if(MODULE_SUBPROJECTS) foreach(subproject ${MODULE_SUBPROJECTS}) if(NOT TARGET ${subproject}) message(SEND_ERROR "The subproject ${subproject} does not have a corresponding target") endif() endforeach() endif() # check and set-up auto-loading if(MODULE_AUTOLOAD_WITH) if(NOT TARGET "${MODULE_AUTOLOAD_WITH}") message(SEND_ERROR "The module target \"${MODULE_AUTOLOAD_WITH}\" specified as the auto-loading module for \"${MODULE_NAME}\" does not exist") endif() # create a meta-target if it does not already exist set(_module_autoload_meta_target "${MODULE_AUTOLOAD_WITH}-universe") if(NOT TARGET ${_module_autoload_meta_target}) add_custom_target(${_module_autoload_meta_target}) endif() endif() # assume worst case set(MODULE_IS_ENABLED 0) # first we check if we have an explicit module build list if(MITK_MODULES_TO_BUILD) list(FIND MITK_MODULES_TO_BUILD ${MODULE_NAME} _MOD_INDEX) if(_MOD_INDEX EQUAL -1) set(MODULE_IS_EXCLUDED 1) endif() endif() if(NOT MODULE_IS_EXCLUDED AND NOT (MODULE_QT_MODULE AND NOT MITK_USE_QT)) # first of all we check for the dependencies MITK_CHECK_MODULE(_MISSING_DEP ${MODULE_DEPENDS}) if(_MISSING_DEP) message("Module ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}") set(MODULE_IS_ENABLED 0) else(_MISSING_DEP) set(MODULE_IS_ENABLED 1) # now check for every package if it is enabled. This overlaps a bit with # MITK_CHECK_MODULE ... foreach(_package ${MODULE_PACKAGE_DEPENDS}) if((DEFINED MITK_USE_${_package}) AND NOT (MITK_USE_${_package})) message("Module ${MODULE_NAME} won't be built. Turn on MITK_USE_${_package} if you want to use it.") set(MODULE_IS_ENABLED 0) endif() endforeach() if(MODULE_IS_ENABLED) # clear variables defined in files.cmake set(RESOURCE_FILES ) set(CPP_FILES ) set(H_FILES ) set(TXX_FILES ) set(DOX_FILES ) set(UI_FILES ) set(MOC_H_FILES ) set(QRC_FILES ) # clear other variables set(Q${KITNAME}_GENERATED_MOC_CPP ) set(Q${KITNAME}_GENERATED_QRC_CPP ) set(Q${KITNAME}_GENERATED_UI_CPP ) + set(Q${KITNAME}_GENERATED_CPP ) _MITK_CREATE_MODULE_CONF() if(NOT MODULE_EXPORT_DEFINE) set(MODULE_EXPORT_DEFINE ${MODULE_NAME}_EXPORT) endif(NOT MODULE_EXPORT_DEFINE) if(MITK_GENERATE_MODULE_DOT) message("MODULEDOTNAME ${MODULE_NAME}") foreach(dep ${MODULE_DEPENDS}) message("MODULEDOT \"${MODULE_NAME}\" -> \"${dep}\" ; ") endforeach(dep) endif(MITK_GENERATE_MODULE_DOT) set(DEPENDS "${MODULE_DEPENDS}") if(NOT MODULE_NO_INIT) # Add a CppMicroServices dependency implicitly, since it is # needed for the generated "module initialization" code. set(DEPENDS "CppMicroServices;${DEPENDS}") endif() set(DEPENDS_BEFORE "not initialized") set(PACKAGE_DEPENDS "${MODULE_PACKAGE_DEPENDS}") MITK_USE_MODULE(${DEPENDS}) # ok, now create the module itself include_directories(. ${ALL_INCLUDE_DIRECTORIES}) include(files.cmake) set(module_c_flags ) set(module_c_flags_debug ) set(module_c_flags_release ) set(module_cxx_flags ) set(module_cxx_flags_debug ) set(module_cxx_flags_release ) if(MODULE_GCC_DEFAULT_VISIBILITY) set(use_visibility_flags 0) else() # We only support hidden visibility for gcc for now. Clang 3.0 still has troubles with # correctly marking template declarations and explicit template instantiations as exported. # See http://comments.gmane.org/gmane.comp.compilers.clang.scm/50028 # and http://llvm.org/bugs/show_bug.cgi?id=10113 if(CMAKE_COMPILER_IS_GNUCXX) set(use_visibility_flags 1) else() # set(use_visibility_flags 0) endif() endif() if(CMAKE_COMPILER_IS_GNUCXX) # MinGW does not export all symbols automatically, so no need to set flags. # # With gcc < 4.5, RTTI symbols from classes declared in third-party libraries # which are not "gcc visibility aware" are marked with hidden visibility in # DSOs which include the class declaration and which are compiled with # hidden visibility. This leads to dynamic_cast and exception handling problems. # While this problem could be worked around by sandwiching the include # directives for the third-party headers between "#pragma visibility push/pop" # statements, it is generally safer to just use default visibility with # gcc < 4.5. if(${GCC_VERSION} VERSION_LESS "4.5" OR MINGW) set(use_visibility_flags 0) endif() endif() if(use_visibility_flags) mitkFunctionCheckCAndCXXCompilerFlags("-fvisibility=hidden" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-fvisibility-inlines-hidden" module_c_flags module_cxx_flags) endif() configure_file(${MITK_SOURCE_DIR}/CMake/moduleExports.h.in ${CMAKE_BINARY_DIR}/${MODULES_CONF_DIRNAME}/${MODULE_NAME}Exports.h @ONLY) if(MODULE_WARNINGS_AS_ERRORS) if(MSVC_VERSION) mitkFunctionCheckCAndCXXCompilerFlags("/WX" module_c_flags module_cxx_flags) else() mitkFunctionCheckCAndCXXCompilerFlags("-Werror" module_c_flags module_cxx_flags) # The flag "c++0x-static-nonintegral-init" has been renamed in newer Clang # versions to "static-member-init", see # http://clang-developers.42468.n3.nabble.com/Wc-0x-static-nonintegral-init-gone-td3999651.html # # Also, older Clang and seemingly all gcc versions do not warn if unknown # "-no-*" flags are used, so CMake will happily append any -Wno-* flag to the # command line. This may get confusing if unrelated compiler errors happen and # the error output then additionally contains errors about unknown flags (which # is not the case if there were no compile errors). # # So instead of using -Wno-* we use -Wno-error=*, which will be properly rejected by # the compiler and if applicable, prints the specific warning as a real warning and # not as an error (although -Werror was given). mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=c++0x-static-nonintegral-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=static-member-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unknown-warning" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=gnu" module_c_flags module_cxx_flags) # VNL headers throw a lot of these, not fixable for us at least in ITK 3 mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unused-parameter" module_c_flags module_cxx_flags) # Some DICOM header file in ITK mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=cast-align" module_c_flags module_cxx_flags) endif() endif(MODULE_WARNINGS_AS_ERRORS) if(MODULE_FORCE_STATIC) set(_STATIC STATIC) else() set(_STATIC ) endif(MODULE_FORCE_STATIC) if(NOT MODULE_NO_INIT AND NOT MODULE_HEADERS_ONLY) set(MODULE_LIBNAME ${MODULE_PROVIDES}) usFunctionGenerateModuleInit(CPP_FILES NAME ${MODULE_NAME} LIBRARY_NAME ${MODULE_LIBNAME} DEPENDS ${MODULE_DEPENDS} ${MODULE_DEPENDS_INTERNAL} ${MODULE_PACKAGE_DEPENDS} #VERSION ${MODULE_VERSION} ) if(RESOURCE_FILES) set(res_dir Resources) set(binary_res_files ) set(source_res_files ) foreach(res_file ${RESOURCE_FILES}) if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}/${res_file}) list(APPEND binary_res_files "${res_file}") else() list(APPEND source_res_files "${res_file}") endif() endforeach() set(res_macro_args ) if(binary_res_files) list(APPEND res_macro_args ROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${res_dir} FILES ${binary_res_files}) endif() if(source_res_files) list(APPEND res_macro_args ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir} FILES ${source_res_files}) endif() usFunctionEmbedResources(CPP_FILES LIBRARY_NAME ${MODULE_LIBNAME} ${res_macro_args}) endif() endif() if(MODULE_QT_MODULE) if(UI_FILES) QT4_WRAP_UI(Q${KITNAME}_GENERATED_UI_CPP ${UI_FILES}) endif(UI_FILES) if(MOC_H_FILES) QT4_WRAP_CPP(Q${KITNAME}_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) endif(MOC_H_FILES) if(QRC_FILES) QT4_ADD_RESOURCES(Q${KITNAME}_GENERATED_QRC_CPP ${QRC_FILES}) endif(QRC_FILES) set(Q${KITNAME}_GENERATED_CPP ${Q${KITNAME}_GENERATED_CPP} ${Q${KITNAME}_GENERATED_UI_CPP} ${Q${KITNAME}_GENERATED_MOC_CPP} ${Q${KITNAME}_GENERATED_QRC_CPP}) endif() ORGANIZE_SOURCES(SOURCE ${CPP_FILES} HEADER ${H_FILES} TXX ${TXX_FILES} DOC ${DOX_FILES} UI ${UI_FILES} QRC ${QRC_FILES} MOC ${Q${KITNAME}_GENERATED_MOC_CPP} GEN_QRC ${Q${KITNAME}_GENERATED_QRC_CPP} GEN_UI ${Q${KITNAME}_GENERATED_UI_CPP}) set(coverage_sources ${CPP_FILES} ${H_FILES} ${GLOBBED__H_FILES} ${CORRESPONDING__H_FILES} ${TXX_FILES} ${TOOL_CPPS} ${TOOL_GUI_CPPS}) if(MODULE_SUBPROJECTS) set_property(SOURCE ${coverage_sources} APPEND PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() if(NOT MODULE_HEADERS_ONLY) if(ALL_LIBRARY_DIRS) # LINK_DIRECTORIES applies only to targets which are added after the call to LINK_DIRECTORIES link_directories(${ALL_LIBRARY_DIRS}) endif(ALL_LIBRARY_DIRS) add_library(${MODULE_PROVIDES} ${_STATIC} ${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES}) if(MODULE_TARGET_DEPENDS) add_dependencies(${MODULE_PROVIDES} ${MODULE_TARGET_DEPENDS}) endif() if(MODULE_SUBPROJECTS) set_property(TARGET ${MODULE_PROVIDES} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) foreach(subproject ${MODULE_SUBPROJECTS}) add_dependencies(${subproject} ${MODULE_PROVIDES}) endforeach() endif() if(ALL_LIBRARIES) target_link_libraries(${MODULE_PROVIDES} ${ALL_LIBRARIES}) endif(ALL_LIBRARIES) if(MODULE_QT_MODULE AND QT_LIBRARIES) target_link_libraries(${MODULE_PROVIDES} ${QT_LIBRARIES}) endif() if(MINGW) target_link_libraries(${MODULE_PROVIDES} ssp) # add stack smash protection lib endif() # Apply properties to the module target. # We cannot use set_target_properties like below since there is no way to # differentiate C/C++ and Releas/Debug flags using target properties. # See http://www.cmake.org/Bug/view.php?id=6493 #set_target_properties(${MODULE_PROVIDES} PROPERTIES # COMPILE_FLAGS "${module_compile_flags}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${module_c_flags}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${module_c_flags_debug}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${module_c_flags_release}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${module_cxx_flags}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${module_cxx_flags_debug}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${module_cxx_flags_release}") # Add additional library search directories to a global property which # can be evaluated by other CMake macros, e.g. our install scripts. if(MODULE_ADDITIONAL_LIBS) get_property(_mitk_additional_library_search_paths GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) foreach(_lib_filepath ${MODULE_ADDITIONAL_LIBS}) get_filename_component(_search_path "${_lib_filepath}" PATH) if(_search_path) list(APPEND _mitk_additional_library_search_paths "${_search_path}") endif() endforeach() if(_mitk_additional_library_search_paths) list(REMOVE_DUPLICATES _mitk_additional_library_search_paths) set_property(GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS ${_mitk_additional_library_search_paths}) endif() endif() # add the target name to a global property which is used in the top-level # CMakeLists.txt file to export the target set_property(GLOBAL APPEND PROPERTY MITK_MODULE_TARGETS ${MODULE_PROVIDES}) if(MODULE_AUTOLOAD_WITH) # for auto-loaded modules, adapt the output directory add_dependencies(${_module_autoload_meta_target} ${MODULE_PROVIDES}) if(WIN32) set(_module_output_prop RUNTIME_OUTPUT_DIRECTORY) else() set(_module_output_prop LIBRARY_OUTPUT_DIRECTORY) endif() set(_module_output_dir ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) get_target_property(_module_is_imported ${MODULE_AUTOLOAD_WITH} IMPORTED) if(NOT _module_is_imported) # if the auto-loading module is not imported, get its location # and put the auto-load module relative to it. get_target_property(_module_output_dir ${MODULE_AUTOLOAD_WITH} ${_module_output_prop}) set_target_properties(${MODULE_PROVIDES} PROPERTIES ${_module_output_prop} ${_module_output_dir}/${MODULE_AUTOLOAD_WITH}) else() set_target_properties(${MODULE_PROVIDES} PROPERTIES ${_module_output_prop} ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) endif() set_target_properties(${MODULE_PROVIDES} PROPERTIES MITK_AUTOLOAD_DIRECTORY ${MODULE_AUTOLOAD_WITH}) # add the auto-load module name as a property set_property(TARGET ${MODULE_AUTOLOAD_WITH} APPEND PROPERTY MITK_AUTOLOAD_TARGETS ${MODULE_PROVIDES}) else() # Add meta dependencies (e.g. on auto-load modules from depending modules) if(ALL_META_DEPENDENCIES) add_dependencies(${MODULE_PROVIDES} ${ALL_META_DEPENDENCIES}) endif() endif() endif() endif(MODULE_IS_ENABLED) endif(_MISSING_DEP) endif(NOT MODULE_IS_EXCLUDED AND NOT (MODULE_QT_MODULE AND NOT MITK_USE_QT)) if(NOT MODULE_IS_ENABLED) _MITK_CREATE_MODULE_CONF() endif(NOT MODULE_IS_ENABLED) endmacro(MITK_CREATE_MODULE) diff --git a/CMakeExternals/Patchtinyxml-2.6.2.cmake b/CMakeExternals/Patchtinyxml-2.6.2.cmake index 058bf87612..0ecf539b30 100644 --- a/CMakeExternals/Patchtinyxml-2.6.2.cmake +++ b/CMakeExternals/Patchtinyxml-2.6.2.cmake @@ -1,15 +1,18 @@ # Called by tinyxml.cmake (ExternalProject_Add) as a patch for tinyxml. # Adds #define TIXML_USE_STL to enable STL string support +configure_file(${CMAKE_CURRENT_LIST_DIR}/Patchtinyxml-2.6.2.h tinyxml.h COPYONLY) +configure_file(${CMAKE_CURRENT_LIST_DIR}/Patchtinyxml-2.6.2.cpp tinyxml.cpp COPYONLY) + # read whole file file(STRINGS tinyxml.h sourceCode NEWLINE_CONSUME) # Add the TIXML_USE_STL define string(REGEX REPLACE "#define TINYXML_INCLUDED" "#define TINYXML_INCLUDED\n\n#ifndef TIXML_USE_STL\n #define TIXML_USE_STL\n#endif" sourceCode ${sourceCode}) # set variable CONTENTS, which is substituted in TEMPLATE_FILE set(CONTENTS ${sourceCode}) configure_file(${CMAKE_CURRENT_LIST_DIR}/EmptyFileForPatching.dummy tinyxml.h @ONLY) # Add the default CMake build system include(${CMAKE_CURRENT_LIST_DIR}/GenerateDefaultCMakeBuildSystem.cmake) diff --git a/CMakeExternals/Patchtinyxml-2.6.2.cpp b/CMakeExternals/Patchtinyxml-2.6.2.cpp new file mode 100644 index 0000000000..df8c540b47 --- /dev/null +++ b/CMakeExternals/Patchtinyxml-2.6.2.cpp @@ -0,0 +1,1950 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + */ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + +FILE* TiXmlFOpen( const char* filename, const char* mode ); + +bool TiXmlBase::condenseWhiteSpace = true; +static unsigned int required_decimal_places = 14+1; // Need 14 for mitk default accuracy plus 1 to make sure we're within tolerance. + + + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; +#else + return fopen( filename, mode ); +#endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + +#if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); +#else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); +#endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; + target->location = location; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + delete node; + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( !replaceThis ) + return 0; + + if ( replaceThis->parent != this ) + return 0; + + if ( withThis.ToDocument() ) { + // A document can never be a child. Thanks to Noam. + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( !removeThis ) { + return false; + } + + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ +#ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); +#else + TiXmlAttribute* node = attributeSet.Find( name ); +#endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) +: TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) +: TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) +: TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); + return *this; +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( attrib ) + return &attrib->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int ival = 0; + int result = node->QueryIntValue( &ival ); + *value = (unsigned)ival; + return result; +} + + +int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int result = TIXML_WRONG_TYPE; + if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = true; + result = TIXML_SUCCESS; + } + else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = false; + result = TIXML_SUCCESS; + } + return result; +} + + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val, const unsigned int requiredDecimalPlaces ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val, requiredDecimalPlaces ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetDoubleAttribute( const std::string& name, double val, const unsigned int requiredDecimalPlaces ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val, requiredDecimalPlaces ); + } +} +#endif + + + + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); + if ( attrib ) { + attrib->SetValue( cvalue ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); + if ( attrib ) { + attrib->SetValue( _value ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Process the buffer in place to normalize new lines. (See comment above.) + // Copies from the 'p' to 'q' pointer, where p can advance faster if + // a newline-carriage return is hit. + // + // Wikipedia: + // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or + // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... + // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others + // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS + // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 + + const char* p = buf; // the read head + char* q = buf; // the write head + const char CR = 0x0d; + const char LF = 0x0a; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + assert( q <= (buf+length) ); + assert( q <= p ); + + if ( *p == CR ) { + *q++ = LF; + p++; + if ( *p == LF ) { // check for CR+LF (and skip LF) + p++; + } + } + else { + *q++ = *p++; + } + } + assert( q <= (buf+length) ); + *q = 0; + + Parse( buf, 0, encoding ); + + delete [] buf; + return !Error(); +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + */ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + */ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; +#if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); +#else + sprintf (buf, "%d", _value); +#endif + SetValue (buf); +} + + + + +void TiXmlAttribute::SetDoubleValue( double _value, const unsigned int requiredDecimalPlaces ) +{ +#if defined(TIXML_USE_STL) + std::ostringstream ss; + ss.imbue(std::locale("C")); + ss.precision(TiXmlBase::Precision(_value, requiredDecimalPlaces)); + ss << _value; + SetValue( ss.str() ); +#else + char buf [256]; +#if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), TiXmlBase::Format(_value, requiredDecimalPlaces).c_str(), _value); +#else + sprintf (buf, TiXmlBase::Format(_value, requiredDecimalPlaces).c_str(), _value); +#endif + SetValue (buf); +#endif + +} + + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ +#if defined(TIXML_USE_STL) + std::istringstream ss(value); + ss.imbue(std::locale("C")); + double dval; + ss >> dval; + return dval; +#else + return atof (value.c_str ()); +#endif +} + + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); + return *this; +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) +: TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) +: TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) +: TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + copy.CopyTo( this ); +} + + +TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ +#ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. +#else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. +#endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} +#endif + + +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} + + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + +unsigned int TiXmlBase::Precision( const double value, const unsigned int requiredDecimalPlaces ) const +{ + unsigned int lhs = 0; + unsigned int one_for_plus_minus_sign = 1; + + lhs = 0; + double temp(value); + while (temp >= 1.0) + { + lhs++; + temp /= 10.0; + } + + return( lhs + requiredDecimalPlaces + one_for_plus_minus_sign ); +} + +/** + Return the printf format string that will ensure that the double value + passed in will be stored with 'required_decimal_places' worth of decimal + points as well as enough digits for the left hand side of the decimal point. + */ +TIXML_STRING TiXmlBase::Format( const double value, const unsigned int requiredDecimalPlaces ) const +{ + char buf[ 32 ]; + +#if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%%%d.%dlf", Precision(value, requiredDecimalPlaces), requiredDecimalPlaces); +#else + sprintf( buf, "%%%d.%dlf", Precision(value, requiredDecimalPlaces), requiredDecimalPlaces); +#endif + + return(TIXML_STRING(buf)); +} + + + diff --git a/CMakeExternals/Patchtinyxml-2.6.2.h b/CMakeExternals/Patchtinyxml-2.6.2.h new file mode 100644 index 0000000000..3be7e726ee --- /dev/null +++ b/CMakeExternals/Patchtinyxml-2.6.2.h @@ -0,0 +1,1823 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + */ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifndef TIXML_USE_STL +#define TIXML_USE_STL +#endif + +#ifndef TIXML_USE_STL +#define TIXML_USE_STL +#endif + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL +#include +#include +#include +#define TIXML_STRING std::string +#else +#include "tinystr.h" +#define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) +// Microsoft visual studio, version 2005 and higher. +#define TIXML_SNPRINTF _snprintf_s +#define TIXML_SSCANF sscanf_s +#elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) +// Microsoft visual studio, version 6 and higher. +//#pragma message( "Using _sn* functions." ) +#define TIXML_SNPRINTF _snprintf +#define TIXML_SSCANF sscanf +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) +// GCC version 3 and higher.s +//#warning( "Using sn* functions." ) +#define TIXML_SNPRINTF snprintf +#define TIXML_SSCANF sscanf +#else +#define TIXML_SNPRINTF snprintf +#define TIXML_SSCANF sscanf +#endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 6; +const int TIXML_PATCH_VERSION = 2; + +#define DEFAULT_REQUIRED_DECIMAL_PLACES 14 + +/* Internal structure for tracking location of items + in the XML file. + */ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() + */ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknown node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim + */ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + + unsigned int Precision( const double value, const unsigned int requiredDecimalPlaces ) const ; + + TIXML_STRING Format( const double value, const unsigned int requiredDecimalPlaces ) const; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + +#ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); +#endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; + + +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. + */ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: +#ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + +#endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + TINYXML_DOCUMENT, + TINYXML_ELEMENT, + TINYXML_COMMENT, + TINYXML_UNKNOWN, + TINYXML_TEXT, + TINYXML_DECLARATION, + TINYXML_TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + +#ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } +#endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + +#ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } +#endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + +#ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. +#endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + +#ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. +#endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + +#ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. +#endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + +#ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. +#endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + +#ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. +#endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, + TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + +#ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; +#endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. + */ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() +{ + document = 0; + prev = next = 0; +} + +#ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } +#endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. +#ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. +#endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value, const unsigned int requiredDecimalPlaces = DEFAULT_REQUIRED_DECIMAL_PLACES ); ///< Set the value from a double. + +#ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } +#endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. + */ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* FindOrCreate( const char* _name ); + +# ifdef TIXML_USE_STL + TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* FindOrCreate( const std::string& _name ); +# endif + + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. + */ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + +#ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); +#endif + + TiXmlElement( const TiXmlElement& ); + + TiXmlElement& operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute(). + int QueryUnsignedAttribute( const char* name, unsigned* _value ) const; + /** QueryBoolAttribute examines the attribute - see QueryIntAttribute(). + Note that '1', 'true', or 'yes' are considered true, while '0', 'false' + and 'no' are considered false. + */ + int QueryBoolAttribute( const char* name, bool* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + +#ifdef TIXML_USE_STL + /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). + int QueryStringAttribute( const char* name, std::string* _value ) const { + const char* cstr = Attribute( name ); + if ( cstr ) { + *_value = std::string( cstr ); + return TIXML_SUCCESS; + } + return TIXML_NO_ATTRIBUTE; + } + + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types that contain spaces. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + + int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } +#endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + +#ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + ///< STL std::string form. + void SetDoubleAttribute( const std::string& name, double value, const unsigned int requiredDecimalPlaces = DEFAULT_REQUIRED_DECIMAL_PLACES ); +#endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value, const unsigned int requiredDecimalPlaces = DEFAULT_REQUIRED_DECIMAL_PLACES ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); +#ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. +#endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] +#ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); +#endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. + */ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + TiXmlComment& operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public +#ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); +#endif + // virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). + */ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) +{ + SetValue( initValue ); + cdata = false; +} + virtual ~TiXmlText() {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } +#endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } + TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] +#ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); +#endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. + */ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + TiXmlDeclaration& operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public +#ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); +#endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. + */ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } + TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + +#ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); +#endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. + */ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); +#endif + + TiXmlDocument( const TiXmlDocument& copy ); + TiXmlDocument& operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + +#ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + return SaveFile( filename.c_str() ); + } +#endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; +#ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); +#endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim + */ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + +#ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } +#endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim + */ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + +#ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } +#endif + +private: + void DoIndent() { + for( int i=0; i ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # See http://cmake.org/cmake/help/cmake-2-8-docs.html#section_Policies for details #----------------------------------------------------------------------------- set(project_policies CMP0001 # NEW: CMAKE_BACKWARDS_COMPATIBILITY should no longer be used. CMP0002 # NEW: Logical target names must be globally unique. CMP0003 # NEW: Libraries linked via full path no longer produce linker search paths. CMP0004 # NEW: Libraries linked may NOT have leading or trailing whitespace. CMP0005 # NEW: Preprocessor definition values are now escaped automatically. CMP0006 # NEW: Installing MACOSX_BUNDLE targets requires a BUNDLE DESTINATION. CMP0007 # NEW: List command no longer ignores empty elements. CMP0008 # NEW: Libraries linked by full-path must have a valid library file name. CMP0009 # NEW: FILE GLOB_RECURSE calls should not follow symlinks by default. CMP0010 # NEW: Bad variable reference syntax is an error. CMP0011 # NEW: Included scripts do automatic cmake_policy PUSH and POP. CMP0012 # NEW: if() recognizes numbers and boolean constants. CMP0013 # NEW: Duplicate binary directories are not allowed. CMP0014 # NEW: Input directories must have CMakeLists.txt ) foreach(policy ${project_policies}) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) endif() endforeach() #----------------------------------------------------------------------------- # Update CMake module path #------------------------------------------------------------------------------ set(CMAKE_MODULE_PATH ${MITK_SOURCE_DIR}/CMake ${CMAKE_MODULE_PATH} ) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(mitkMacroEmptyExternalProject) include(mitkFunctionGenerateProjectXml) include(mitkFunctionSuppressWarnings) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_USE_SUPERBUILD) set(output_dir ${MITK_BINARY_DIR}/bin) if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_BINARY_DIR}/MITK-build/bin) endif() else() if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(output_dir ${MITK_BINARY_DIR}/bin) else() set(output_dir ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) endif() endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${output_dir} CACHE INTERNAL "Single output directory for building all libraries.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) set(MITK_BUILD_TUTORIAL OFF CACHE INTERNAL "Deprecated! Use MITK_BUILD_EXAMPLES instead!") option(MITK_BUILD_EXAMPLES "Build the MITK Examples" ${MITK_BUILD_TUTORIAL}) option(MITK_USE_ACVD "Use Approximated Centroidal Voronoi Diagrams" OFF) option(MITK_USE_GLEW "Use the GLEW library" ON) option(MITK_USE_Boost "Use the Boost C++ library" OFF) option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_CTK "Use CTK in MITK" ${MITK_USE_BLUEBERRY}) option(MITK_USE_QT "Use Nokia's Qt library" ${MITK_USE_CTK}) option(MITK_USE_DCMTK "EXPERIMENTAL, superbuild only: Use DCMTK in MITK" ${MITK_USE_CTK}) option(MITK_USE_OpenCV "Use Intel's OpenCV library" OFF) option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) option(MITK_USE_Poco "Use the Poco library" ON) option(MITK_USE_SOFA "Use Simulation Open Framework Architecture" OFF) option(MITK_USE_Python "Use Python wrapping in MITK" OFF) set(MITK_USE_CableSwig ${MITK_USE_Python}) if(MITK_USE_Python) FIND_PACKAGE(PythonLibs REQUIRED) FIND_PACKAGE(PythonInterp REQUIRED) endif() mark_as_advanced(MITK_BUILD_ALL_APPS MITK_USE_GLEW MITK_USE_CTK MITK_USE_DCMTK ) if(MITK_USE_Boost) option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() if(MITK_USE_CTK) if(NOT MITK_USE_QT) message("Forcing MITK_USE_QT to ON because of MITK_USE_CTK") set(MITK_USE_QT ON CACHE BOOL "Use Nokia's Qt library in MITK" FORCE) endif() if(NOT MITK_USE_DCMTK) message("Setting MITK_USE_DCMTK to ON because DCMTK needs to be build for CTK") set(MITK_USE_DCMTK ON CACHE BOOL "Use DCMTK in MITK" FORCE) endif() endif() if(MITK_USE_QT) # find the package at the very beginning, so that QT4_FOUND is available find_package(Qt4 4.6.2 REQUIRED) endif() if(MITK_USE_SOFA) # SOFA requires at least CMake 2.8.8 set(SOFA_CMAKE_VERSION 2.8.8) if(${CMAKE_VERSION} VERSION_LESS ${SOFA_CMAKE_VERSION}) set(MITK_USE_SOFA OFF CACHE BOOL "" FORCE) message(WARNING "Switched off MITK_USE_SOFA\n Minimum required CMake version: ${SOFA_CMAKE_VERSION}\n Installed CMake version: ${CMAKE_VERSION}") endif() # SOFA doesn't support Clang if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(MITK_USE_SOFA OFF CACHE BOOL "" FORCE) message(WARNING "Switched off MITK_USE_SOFA\n Clang is not supported, use GCC instead.") endif() # SOFA/ITK combination requires at least MSVC 2010 if(MSVC_VERSION AND MSVC_VERSION LESS 1600) set(MITK_USE_SOFA OFF CACHE BOOL "" FORCE) message(WARNING "Switched off MITK_USE_SOFA\n MSVC versions less than 2010 are not supported.") endif() # SOFA requires boost library if(MITK_USE_SOFA AND NOT MITK_USE_Boost) message("Forcing MITK_USE_Boost to ON because of MITK_USE_SOFA") set(MITK_USE_Boost ON CACHE BOOL "" FORCE) endif() # SOFA requires boost system library list(FIND MITK_USE_Boost_LIBRARIES system _result) if(_result LESS 0) message("Adding 'system' to MITK_USE_Boost_LIBRARIES.") list(APPEND MITK_USE_Boost_LIBRARIES system) endif() # SOFA requires boost thread library list(FIND MITK_USE_Boost_LIBRARIES thread _result) if(_result LESS 0) message("Adding 'thread' to MITK_USE_Boost_LIBRARIES.") list(APPEND MITK_USE_Boost_LIBRARIES thread) endif() set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "" FORCE) endif() # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() #----------------------------------------------------------------------------- # Project.xml #----------------------------------------------------------------------------- # A list of topologically ordered targets set(CTEST_PROJECT_SUBPROJECTS) if(MITK_USE_BLUEBERRY) list(APPEND CTEST_PROJECT_SUBPROJECTS BlueBerry) endif() list(APPEND CTEST_PROJECT_SUBPROJECTS MITK-Core MITK-CoreUI MITK-IGT MITK-ToF MITK-DTI MITK-Registration MITK-Modules # all modules not contained in a specific subproject MITK-Plugins # all plugins not contained in a specific subproject MITK-Examples Unlabeled # special "subproject" catching all unlabeled targets and tests ) # Configure CTestConfigSubProject.cmake that could be used by CTest scripts configure_file(${MITK_SOURCE_DIR}/CTestConfigSubProject.cmake.in ${MITK_BINARY_DIR}/CTestConfigSubProject.cmake) if(CTEST_PROJECT_ADDITIONAL_TARGETS) # those targets will be executed at the end of the ctest driver script # and they also get their own subproject label set(subproject_list "${CTEST_PROJECT_SUBPROJECTS};${CTEST_PROJECT_ADDITIONAL_TARGETS}") else() set(subproject_list "${CTEST_PROJECT_SUBPROJECTS}") endif() # Generate Project.xml file expected by the CTest driver script mitkFunctionGenerateProjectXml(${MITK_BINARY_DIR} MITK "${subproject_list}" ${MITK_USE_SUPERBUILD}) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(CheckCXXSourceCompiles) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionGetGccVersion) include(MacroParseArguments) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkFunctionOrganizeSources) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionCreateWindowsBatchScript) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionCompileSnippets) include(mitkMacroCreateModuleConf) include(mitkMacroCreateModule) include(mitkMacroCheckModule) include(mitkMacroCreateModuleTests) include(mitkFunctionAddCustomModuleTest) include(mitkMacroUseModule) include(mitkMacroMultiplexPicType) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) #----------------------------------------------------------------------------- # Prerequesites #----------------------------------------------------------------------------- find_package(ITK REQUIRED) find_package(VTK REQUIRED) find_package(GDCM PATHS ${ITK_GDCM_DIR} REQUIRED) #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- # ASK THE USER TO SHOW THE CONSOLE WINDOW FOR CoreApp and mitkWorkbench option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) # TODO: check if necessary option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON) mark_as_advanced(USE_ITKZLIB) if(NOT MITK_FAST_TESTING) if(DEFINED MITK_CTEST_SCRIPT_MODE AND (MITK_CTEST_SCRIPT_MODE STREQUAL "continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "experimental") ) set(MITK_FAST_TESTING 1) endif() endif() #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on Mac OSX all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} ${app_name}) endif() endforeach() endif() #----------------------------------------------------------------------------- # Set symbol visibility Flags #----------------------------------------------------------------------------- # MinGW does not export all symbols automatically, so no need to set flags if(CMAKE_COMPILER_IS_GNUCXX AND NOT MINGW) set(VISIBILITY_CXX_FLAGS ) #"-fvisibility=hidden -fvisibility-inlines-hidden") endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${VISIBILITY_CXX_FLAGS} ${COVERAGE_CXX_FLAGS}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) include(mitkSetupC++0xVariables) if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -D_WIN32_WINNT=0x0501 -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN") set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} /wd4231") # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo # the following two lines should be removed after ITK-3097 has # been resolved, see also MITK bug 15279 -Wno-unused-local-typedefs -Wno-array-bounds -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) if(MITK_USE_C++0x) mitkFunctionCheckCompilerFlags("-std=c++0x" MITK_CXX_FLAGS) endif() mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) # With older version of gcc supporting the flag -fstack-protector-all, an extra dependency to libssp.so # is introduced. If gcc is smaller than 4.4.0 and the build type is Release let's not include the flag. # Doing so should allow to build package made for distribution using older linux distro. if(${GCC_VERSION} VERSION_GREATER "4.4.0" OR (CMAKE_BUILD_TYPE STREQUAL "Debug" AND ${GCC_VERSION} VERSION_LESS "4.4.0")) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) endif() if(MINGW) # suppress warnings about auto imported symbols set(MITK_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import ${MITK_SHARED_LINKER_FLAGS}") endif() set(MITK_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) enable_testing() include(CTest) mark_as_advanced(TCL_TCLSH DART_ROOT) option(MITK_ENABLE_RENDERING_TESTING OFF "Enable the MITK rendering tests. Requires x-server in Linux.") #Rendering testing does not work for Linux nightlies, thus it is disabled per default #and activated for Mac and Windows. if(WIN32 OR APPLE) set(MITK_ENABLE_RENDERING_TESTING ON) endif() mark_as_advanced( MITK_ENABLE_RENDERING_TESTING ) # Setup file for setting custom ctest vars configure_file( CMake/CTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch( std::exception & excp ) { fprintf(stderr,\"%s\\n\",excp.what()); return EXIT_FAILURE; } catch( ... ) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; } ") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the external project template if(MITK_USE_BLUEBERRY) include(mitkTestProjectTemplate) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Compile Utilities and set-up MITK variables #----------------------------------------------------------------------------- include(mitkSetupVariables) #----------------------------------------------------------------------------- # Cleanup #----------------------------------------------------------------------------- file(GLOB _MODULES_CONF_FILES ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake) if(_MODULES_CONF_FILES) file(REMOVE ${_MODULES_CONF_FILES}) endif() add_subdirectory(Utilities) if(MITK_USE_BLUEBERRY) # We need to hack a little bit because MITK applications may need # to enable certain BlueBerry plug-ins. However, these plug-ins # are validated separately from the MITK plug-ins and know nothing # about potential MITK plug-in dependencies of the applications. Hence # we cannot pass the MITK application list to the BlueBerry # ctkMacroSetupPlugins call but need to extract the BlueBerry dependencies # from the applications and set them explicitly. include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) # check if the application is enabled and if target_libraries.cmake exists if((${option_name} OR MITK_BUILD_ALL_APPS) AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake") foreach(_target_dep ${target_libraries}) if(_target_dep MATCHES org_blueberry_) string(REPLACE _ . _app_bb_dep ${_target_dep}) # explicitly set the build option for the BlueBerry plug-in set(BLUEBERRY_BUILD_${_app_bb_dep} ON CACHE BOOL "Build the ${_app_bb_dep} plug-in") endif() endforeach() endif() endforeach() set(mbilog_DIR "${mbilog_BINARY_DIR}") if(MITK_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS ON) endif() set(BLUEBERRY_XPDOC_OUTPUT_DIR ${MITK_DOXYGEN_OUTPUT_DIR}/html/extension-points/html/) add_subdirectory(BlueBerry) set(BlueBerry_DIR ${CMAKE_CURRENT_BINARY_DIR}/BlueBerry CACHE PATH "The directory containing a CMake configuration file for BlueBerry" FORCE) include(mitkMacroCreateCTKPlugin) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add custom targets representing CDash subprojects #----------------------------------------------------------------------------- foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) if(NOT TARGET ${subproject} AND NOT subproject MATCHES "Unlabeled") add_custom_target(${subproject}) endif() endforeach() #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- link_directories(${MITK_LINK_DIRECTORIES}) add_subdirectory(Core) add_subdirectory(Modules) if(MITK_USE_BLUEBERRY) find_package(BlueBerry REQUIRED) set(MITK_DEFAULT_SUBPROJECTS MITK-Plugins) # Plug-in testing (needs some work to be enabled again) if(BUILD_TESTING) include(berryTestingHelpers) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp") get_target_property(_is_macosx_bundle CoreApp MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp.app/Contents/MacOS/CoreApp") endif() set(BLUEBERRY_TEST_APP_ID "org.mitk.qt.coreapplication") endif() include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") set(mitk_plugins_fullpath ) foreach(mitk_plugin ${MITK_EXT_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") set(mitk_apps_fullpath ) foreach(mitk_app ${MITK_APPS}) list(APPEND mitk_apps_fullpath "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${mitk_app}") endforeach() ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() # 11.3.13, change, muellerm: activate python bundle if python and blueberry is active if( MITK_USE_Python ) set(MITK_BUILD_org.mitk.gui.qt.python ON) endif() endif() #----------------------------------------------------------------------------- # Python Wrapping #----------------------------------------------------------------------------- option(MITK_USE_Python "Build Python integration for MITK (requires CableSwig)." OFF) #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- add_subdirectory(Documentation) #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) # MITK_APPS is set in Applications/AppList.cmake (included somewhere above # if MITK_USE_BLUEBERRY is set to ON). if(MITK_APPS) set(activated_apps_no 0) list(LENGTH MITK_APPS app_count) # Check how many apps have been enabled # If more than one app has been activated, the we use the # default CPack configuration. Otherwise that apps configuration # will be used, if present. foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/Applications/${target_dir}/CPackConfig.cmake") configure_file(${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${target_dir}") endif() endforeach() endif() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # This is for installation support of external projects depending on # MITK plugins and modules. The export file should not be used for linking to MITK # libraries without using LINK_DIRECTORIES, since the exports are incomplete # yet (depending libraries are not exported). set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() endforeach() get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) set(VISIBILITY_AVAILABLE 0) set(visibility_test_flag "") mitkFunctionCheckCompilerFlags("-fvisibility=hidden" visibility_test_flag) if(visibility_test_flag) # The compiler understands -fvisiblity=hidden (probably gcc >= 4 or Clang) set(VISIBILITY_AVAILABLE 1) endif() configure_file(mitkExportMacros.h.in ${MITK_BINARY_DIR}/mitkExportMacros.h) configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) file(GLOB _MODULES_CONF_FILES RELATIVE ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME} ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake) set(MITK_MODULE_NAMES) foreach(_module ${_MODULES_CONF_FILES}) string(REPLACE Config.cmake "" _module_name ${_module}) list(APPEND MITK_MODULE_NAMES ${_module_name}) endforeach() configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) # If we are under Windows, create two batch files which correctly # set up the environment for the application and for Visual Studio if(WIN32) include(mitkFunctionCreateWindowsBatchScript) set(VS_SOLUTION_FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.sln") foreach(VS_BUILD_TYPE debug release) mitkFunctionCreateWindowsBatchScript("${MITK_SOURCE_DIR}/CMake/StartVS.bat.in" ${PROJECT_BINARY_DIR}/StartVS_${VS_BUILD_TYPE}.bat ${VS_BUILD_TYPE}) endforeach() endif(WIN32) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() - diff --git a/Core/Code/Algorithms/mitkImageTimeSelector.cpp b/Core/Code/Algorithms/mitkImageTimeSelector.cpp index 7fa5352ae7..57a8952a17 100644 --- a/Core/Code/Algorithms/mitkImageTimeSelector.cpp +++ b/Core/Code/Algorithms/mitkImageTimeSelector.cpp @@ -1,78 +1,99 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageTimeSelector.h" mitk::ImageTimeSelector::ImageTimeSelector() : m_TimeNr(0), m_ChannelNr(0) { } mitk::ImageTimeSelector::~ImageTimeSelector() { } void mitk::ImageTimeSelector::GenerateOutputInformation() { Image::ConstPointer input = this->GetInput(); Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); int dim=(input->GetDimension()<3?input->GetDimension():3); output->Initialize(input->GetPixelType(), dim, input->GetDimensions()); if( (unsigned int) m_TimeNr >= input->GetDimension(3) ) { m_TimeNr = input->GetDimension(3)-1; } // initialize geometry - output->SetGeometry(dynamic_cast(input->GetSlicedGeometry(m_TimeNr)->Clone().GetPointer())); + mitk::SlicedGeometry3D::Pointer sliced_geo = input->GetSlicedGeometry(m_TimeNr); + if( sliced_geo.IsNull() ) + { + mitkThrow() << "Failed to retrieve SlicedGeometry from input at timestep " << m_TimeNr; + } + + mitk::SlicedGeometry3D::Pointer sliced_geo_clone = sliced_geo->Clone(); + if( sliced_geo_clone.IsNull() ) + { + mitkThrow() << "Failed to clone the retrieved sliced geometry."; + } + + mitk::Geometry3D::Pointer geom_3d = dynamic_cast(sliced_geo_clone.GetPointer()); + if( geom_3d.IsNotNull() ) + { + output->SetGeometry(geom_3d.GetPointer() ); + } + else + { + mitkThrow() << "Failed to cast the retrieved SlicedGeometry to a Geometry3D object."; + } + output->SetPropertyList(input->GetPropertyList()->Clone()); } void mitk::ImageTimeSelector::GenerateData() { const Image::RegionType& requestedRegion = this->GetOutput()->GetRequestedRegion(); //do we really need a complete volume at a time? if(requestedRegion.GetSize(2)>1) this->SetVolumeItem( this->GetVolumeData(m_TimeNr, m_ChannelNr), 0 ); else //no, so take just a slice! this->SetSliceItem( this->GetSliceData(requestedRegion.GetIndex(2), m_TimeNr, m_ChannelNr), requestedRegion.GetIndex(2), 0 ); } void mitk::ImageTimeSelector::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); ImageToImageFilter::InputImagePointer input = const_cast< mitk::ImageToImageFilter::InputImageType * > ( this->GetInput() ); Image::Pointer output = this->GetOutput(); Image::RegionType requestedRegion; requestedRegion = output->GetRequestedRegion(); requestedRegion.SetIndex(3, m_TimeNr); requestedRegion.SetIndex(4, m_ChannelNr); requestedRegion.SetSize(3, 1); requestedRegion.SetSize(4, 1); input->SetRequestedRegion( & requestedRegion ); } diff --git a/Core/Code/DataManagement/mitkImage.h b/Core/Code/DataManagement/mitkImage.h index cfc35a788e..16e2642efc 100644 --- a/Core/Code/DataManagement/mitkImage.h +++ b/Core/Code/DataManagement/mitkImage.h @@ -1,712 +1,712 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #include #include "mitkSlicedData.h" #include "mitkBaseData.h" #include "mitkLevelWindow.h" #include "mitkPlaneGeometry.h" #include #include "mitkImageDataItem.h" #include "mitkImageDescriptor.h" #include "mitkImageAccessorBase.h" #include "mitkImageVtkAccessor.h" //DEPRECATED #include #ifndef __itkHistogram_h #include #endif class vtkImageData; namespace mitk { class SubImageSelector; class ImageTimeSelector; class ImageStatisticsHolder; //##Documentation //## @brief Image class for storing images //## //## Can be asked for header information, the data vector, //## the mitkIpPicDescriptor struct or vtkImageData objects. If not the complete //## data is required, the appropriate SubImageSelector class should be used //## for access. //## Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n //## x ND). Channels are for different kind of data, e.g., morphology in //## channel 0, velocities in channel 1. All channels must have the same Geometry! In //## particular, the dimensions of all channels are the same, only the pixel-type //## may differ between channels. //## //## For importing ITK images use of mitk::ITKImageImport is recommended, see //## \ref Adaptor. //## //## For ITK v3.8 and older: Converting coordinates from the ITK physical //## coordinate system (which does not support rotated images) to the MITK world //## coordinate system should be performed via the Geometry3D of the Image, see //## Geometry3D::WorldToItkPhysicalPoint. //## @ingroup Data class MITK_CORE_EXPORT Image : public SlicedData { friend class SubImageSelector; friend class ImageAccessorBase; friend class ImageVtkAccessor; friend class ImageReadAccessor; friend class ImageWriteAccessor; public: mitkClassMacro(Image, SlicedData); itkNewMacro(Self); mitkCloneMacro(Image); /** Smart Pointer type to a ImageDataItem. */ typedef itk::SmartPointer ImageDataItemPointer; typedef itk::Statistics::Histogram HistogramType; typedef mitk::ImageStatisticsHolder* StatisticsHolderPointer; //## @param ImportMemoryManagementType This parameter is evaluated when setting new data to an image. //## The different options are: //## CopyMemory: Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image. //## MamageMemory: Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. //## Reference Memory: Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. //## DontManageMemory = ReferenceMemory. enum ImportMemoryManagementType { CopyMemory, ManageMemory, ReferenceMemory, DontManageMemory = ReferenceMemory }; //##Documentation //## @brief Vector container of SmartPointers to ImageDataItems; //## Class is only for internal usage to allow convenient access to all slices over iterators; //## See documentation of ImageDataItem for details. typedef std::vector ImageDataItemPointerArray; public: //##Documentation //## @brief Returns the PixelType of channel @a n. const mitk::PixelType GetPixelType(int n = 0) const; //##Documentation //## @brief Get dimension of the image //## unsigned int GetDimension() const; //##Documentation //## @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction). //## //## @sa GetDimensions() unsigned int GetDimension(int i) const; /** @brief Get the data vector of the complete image, i.e., of all channels linked together. If you only want to access a slice, volume at a specific time or single channel use one of the SubImageSelector classes. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by ImageWriteAccessor::GetData() or ImageReadAccessor::GetData() */ DEPRECATED(virtual void* GetData()); public: /** @brief Get the pixel value at one specific index position. The pixel type is always being converted to double. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */ DEPRECATED(double GetPixelValueByIndex(const mitk::Index3D& position, unsigned int timestep = 0)); /** @brief Get the pixel value at one specific world position. The pixel type is always being converted to double. \deprecatedSince{2012_09} Please use image accessors instead: See Doxygen/Related-Pages/Concepts/Image. This method can be replaced by a method from ImagePixelWriteAccessor or ImagePixelReadAccessor */ DEPRECATED(double GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep = 0)); //##Documentation //## @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData. virtual ImageVtkAccessor* GetVtkImageData(int t = 0, int n = 0); //##Documentation //## @brief Get the complete image, i.e., all channels linked together, as a @a mitkIpPicDescriptor. //## //## If you only want to access a slice, volume at a specific time or single channel //## use one of the SubImageSelector classes. //virtual mitkIpPicDescriptor* GetPic(); //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is set virtual bool IsSliceSet(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is set virtual bool IsVolumeSet(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is set virtual bool IsChannelSet(int n = 0) const; //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportSlice with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicSlice, SetImportSlice, SetImportVolume virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportVolume with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicVolume, SetImportVolume virtual bool SetVolume(const void *data, int t = 0, int n = 0); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportChannel with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicChannel, SetImportChannel virtual bool SetChannel(const void *data, int n = 0); //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicSlice virtual bool SetImportSlice(void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicVolume virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicChannel virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## initialize new (or re-initialize) image information //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). virtual void Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels = 1); //##Documentation //## initialize new (or re-initialize) image information by a Geometry3D //## //## @param tDim defines the number of time steps for which the Image should be initialized virtual void Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels = 1, int tDim=1); /** * initialize new (or re-initialize) image information by a Geometry3D * * @param tDim defines the number of time steps for which the Image should be initialized * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ - DEPRECATED(virtual void Initialize(const mitk::PixelType& type, const mitk::TimeSlicedGeometry* geometry, unsigned int channels = 1, int tDim=1)){} + DEPRECATED(virtual void Initialize(const mitk::PixelType& /*type*/, const mitk::TimeSlicedGeometry* /*geometry*/, unsigned int /*channels = 1*/, int /*tDim=1*/)){} /** * \brief Initialize new (or re-initialize) image information by a TimeGeometry * * \param tDim override time dimension if the value is bigger than 0 (Default -1) */ virtual void Initialize(const mitk::PixelType& type, const mitk::TimeGeometry& geometry, unsigned int channels = 1, int tDim=-1 ); //##Documentation //## initialize new (or re-initialize) image information by a Geometry2D and number of slices //## //## Initializes the bounding box according to the width/height of the //## Geometry2D and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. //## The spacing is calculated from the Geometry2D. //## \sa SlicedGeometry3D::InitializeEvenlySpaced virtual void Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped = false, unsigned int channels = 1, int tDim=1); //##Documentation //## initialize new (or re-initialize) image information by another //## mitk-image. //## Only the header is used, not the data vector! //## virtual void Initialize(const mitk::Image* image); virtual void Initialize(const mitk::ImageDescriptor::Pointer inDesc); //##Documentation //## initialize new (or re-initialize) image information by @a pic. //## Dimensions and @a Geometry3D /@a Geometry2D are set according //## to the tags in @a pic. //## Only the header is used, not the data vector! Use SetPicVolume(pic) //## to set the data vector. //## //## @param tDim override time dimension (@a n[3]) in @a pic (if >0) //## @param sDim override z-space dimension (@a n[2]) in @a pic (if >0) //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). //virtual void Initialize(const mitkIpPicDescriptor* pic, int channels = 1, int tDim = -1, int sDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a vtkimagedata, //## a vtk-image. //## Only the header is used, not the data vector! Use //## SetVolume(vtkimage->GetScalarPointer()) to set the data vector. //## //## @param tDim override time dimension in @a vtkimagedata (if >0 and <) //## @param sDim override z-space dimension in @a vtkimagedata (if >0 and <) //## @param pDim override y-space dimension in @a vtkimagedata (if >0 and <) virtual void Initialize(vtkImageData* vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1, int pDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a itkimage, //## a templated itk-image. //## Only the header is used, not the data vector! Use //## SetVolume(itkimage->GetBufferPointer()) to set the data vector. //## //## @param tDim override time dimension in @a itkimage (if >0 and <) //## @param sDim override z-space dimension in @a itkimage (if >0 and <) template void InitializeByItk(const itkImageType* itkimage, int channels = 1, int tDim = -1, int sDim=-1) { if(itkimage==NULL) return; MITK_DEBUG << "Initializing MITK image from ITK image."; // build array with dimensions in each direction with at least 4 entries m_Dimension=itkimage->GetImageDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetLargestPossibleRegion().GetSize().GetSize()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } // overwrite number of slices if sDim is set if((m_Dimension>2) && (sDim>=0)) tmpDimensions[2]=sDim; // overwrite number of time points if tDim is set if((m_Dimension>3) && (tDim>=0)) tmpDimensions[3]=tDim; // rough initialization of Image // mitk::PixelType importType = ImportItkPixelType( itkimage::PixelType ); Initialize(MakePixelType(), m_Dimension, tmpDimensions, channels); const typename itkImageType::SpacingType & itkspacing = itkimage->GetSpacing(); MITK_DEBUG << "ITK spacing " << itkspacing; // access spacing of itk::Image Vector3D spacing; FillVector3D(spacing, itkspacing[0], 1.0, 1.0); if(m_Dimension >= 2) spacing[1]=itkspacing[1]; if(m_Dimension >= 3) spacing[2]=itkspacing[2]; // access origin of itk::Image Point3D origin; const typename itkImageType::PointType & itkorigin = itkimage->GetOrigin(); MITK_DEBUG << "ITK origin " << itkorigin; FillVector3D(origin, itkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=itkorigin[1]; if(m_Dimension>=3) origin[2]=itkorigin[2]; // access direction of itk::Imagm_PixelType = new mitk::PixelType(type);e and include spacing const typename itkImageType::DirectionType & itkdirection = itkimage->GetDirection(); MITK_DEBUG << "ITK direction " << itkdirection; mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (m_Dimension >= 3? 3 : m_Dimension); // check if spacing has no zero entry and itkdirection has no zero columns bool itkdirectionOk = true; mitk::ScalarType columnSum; for( j=0; j < itkDimMax3; ++j ) { columnSum = 0.0; for ( i=0; i < itkDimMax3; ++i) { columnSum += fabs(itkdirection[i][j]); } if(columnSum < mitk::eps) { itkdirectionOk = false; } if ( (spacing[j] < - mitk::eps) // (normally sized) negative value && (j==2) && (m_Dimensions[2] == 1) ) { // Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO // In these cases spacing is not determind by ITK correctly (because it distinguishes correctly // between slice thickness and inter slice distance -- slice distance is meaningless for // single slices). // I experienced that ITK produced something meaningful nonetheless because is is // evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not // reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html) // but gives at least a hint. // In real world cases I experienced that this tag contained the correct inter slice distance // with a negative sign, so we just invert such negative spacings. MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using inverted value " << -spacing[j]; spacing[j] = -spacing[j]; } else if (spacing[j] < mitk::eps) // value near zero { MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using 1.0 instead."; spacing[j] = 1.0; } } if(itkdirectionOk == false) { MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead."; for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) if ( i == j ) matrix[i][j] = spacing[j]; else matrix[i][j] = 0.0; } else { for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = itkdirection[i][j]*spacing[j]; } // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(GetSlicedGeometry(0)->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]); slicedGeometry->SetSpacing(spacing); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); // clean-up delete [] tmpDimensions; this->Initialize(); }; //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidVolume(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidChannel(int n = 0) const; //##Documentation //## @brief Returns true if an image is rotated, i.e. its geometry's //## transformation matrix has nonzero elements besides the diagonal. //## Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace. bool IsRotated() const; //##Documentation //## @brief Get the sizes of all dimensions as an integer-array. //## //## @sa GetDimension(int i); unsigned int* GetDimensions() const; ImageDescriptor::Pointer GetImageDescriptor() const { return m_ImageDescriptor; } ChannelDescriptor GetChannelDescriptor( int id = 0 ) const { return m_ImageDescriptor->GetChannelDescriptor(id); } /** \brief Sets a geometry to an image. */ virtual void SetGeometry(Geometry3D* aGeometry3D); /** * @warning for internal use only */ virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @warning for internal use only */ virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @warning for internal use only */ virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** \brief (DEPRECATED) Get the minimum for scalar images */ DEPRECATED (ScalarType GetScalarValueMin(int t=0) const); /** \brief (DEPRECATED) Get the maximum for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMax(int t=0) const); /** \brief (DEPRECATED) Get the second smallest value for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMin(int t=0) const); /** \brief (DEPRECATED) Get the smallest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMinNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the second smallest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMinNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the second largest value for scalar images \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMax(int t=0) const); /** \brief (DEPRECATED) Get the largest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValueMaxNoRecompute( unsigned int t = 0 ) const ); /** \brief (DEPRECATED) Get the second largest value for scalar images, but do not recompute it first \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetScalarValue2ndMaxNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetCountOfMinValuedVoxels(int t = 0) const); /** \brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (ScalarType GetCountOfMaxValuedVoxels(int t = 0) const); /** \brief (DEPRECATED) Get the count of voxels with the largest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (unsigned int GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t = 0 ) const); /** \brief (DEPRECATED) Get the count of voxels with the smallest scalar value in the dataset \warning This method is deprecated and will not be available in the future. Use the \a GetStatistics instead */ DEPRECATED (unsigned int GetCountOfMinValuedVoxelsNoRecompute( unsigned int t = 0 ) const); /** \brief Returns a pointer to the ImageStatisticsHolder object that holds all statistics information for the image. All Get-methods for statistics properties formerly accessible directly from an Image object are now moved to the new \a ImageStatisticsHolder object. */ StatisticsHolderPointer GetStatistics() const { return m_ImageStatistics; } protected: int GetSliceIndex(int s = 0, int t = 0, int n = 0) const; int GetVolumeIndex(int t = 0, int n = 0) const; void ComputeOffsetTable(); virtual bool IsValidTimeStep(int t) const; virtual void Expand( unsigned int timeSteps ); virtual ImageDataItemPointer AllocateSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); Image(); Image(const Image &other); virtual ~Image(); virtual void Clear(); //## @warning Has to be called by every Initialize method! virtual void Initialize(); virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; mutable ImageDataItemPointerArray m_Channels; mutable ImageDataItemPointerArray m_Volumes; mutable ImageDataItemPointerArray m_Slices; unsigned int m_Dimension; unsigned int* m_Dimensions; ImageDescriptor::Pointer m_ImageDescriptor; size_t *m_OffsetTable; ImageDataItemPointer m_CompleteData; // Image statistics Holder replaces the former implementation directly inside this class friend class ImageStatisticsHolder; StatisticsHolderPointer m_ImageStatistics; private: /** Stores all existing ImageReadAccessors */ std::vector m_Readers; /** Stores all existing ImageWriteAccessors */ std::vector m_Writers; /** Stores all existing ImageVtkAccessors */ std::vector m_VtkReaders; /** A mutex, which needs to be locked to manage m_Readers and m_Writers */ itk::SimpleFastMutexLock m_ReadWriteLock; /** A mutex, which needs to be locked to manage m_VtkReaders */ itk::SimpleFastMutexLock m_VtkReadersLock; }; /** * @brief Equal A function comparing two images for beeing equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - dimension of the images * - size of the images * - pixel type * - pixel values : pixel values are expected to be identical at each position ( for other options see mitk::CompareImageFilter ) * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITK_CORE_EXPORT bool Equal( const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose ); //} //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const itk::SmartPointer& itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage.GetPointer()); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const ItkOutputImageType* itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } } // namespace mitk #endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Core/Code/IO/mitkDicomSR_GantryTiltInformation.cpp b/Core/Code/IO/mitkDicomSR_GantryTiltInformation.cpp new file mode 100644 index 0000000000..6a324d2b64 --- /dev/null +++ b/Core/Code/IO/mitkDicomSR_GantryTiltInformation.cpp @@ -0,0 +1,201 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +namespace mitk +{ + +DicomSeriesReader::GantryTiltInformation::GantryTiltInformation() +: m_ShiftUp(0.0) +, m_ShiftRight(0.0) +, m_ShiftNormal(0.0) +, m_ITKAssumedSliceSpacing(0.0) +, m_NumberOfSlicesApart(1) +{ +} + + +#define doublepoint(x) \ + Point3Dd x; \ + x[0] = x ## f[0]; \ + x[1] = x ## f[1]; \ + x[2] = x ## f[2]; + + +#define doublevector(x) \ + Vector3Dd x; \ + x[0] = x ## f[0]; \ + x[1] = x ## f[1]; \ + x[2] = x ## f[2]; + +DicomSeriesReader::GantryTiltInformation::GantryTiltInformation( + const Point3D& origin1f, const Point3D& origin2f, + const Vector3D& rightf, const Vector3D& upf, + unsigned int numberOfSlicesApart) +: m_ShiftUp(0.0) +, m_ShiftRight(0.0) +, m_ShiftNormal(0.0) +, m_NumberOfSlicesApart(numberOfSlicesApart) +{ + assert(numberOfSlicesApart); + + doublepoint(origin1); + doublepoint(origin2); + doublevector(right); + doublevector(up); + + // determine if slice 1 (imagePosition1 and imageOrientation1) and slice 2 can be in one orthogonal slice stack: + // calculate a line from origin 1, directed along the normal of slice (calculated as the cross product of orientation 1) + // check if this line passes through origin 2 + + /* + Determine if line (imagePosition2 + l * normal) contains imagePosition1. + Done by calculating the distance of imagePosition1 from line (imagePosition2 + l *normal) + + E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html + + squared distance = | (pointAlongNormal - origin2) x (origin2 - origin1) | ^ 2 + / + |pointAlongNormal - origin2| ^ 2 + + ( x meaning the cross product ) + */ + + Vector3Dd normal = itk::CrossProduct(right, up); + Point3Dd pointAlongNormal = origin2 + normal; + + double numerator = itk::CrossProduct( pointAlongNormal - origin2 , origin2 - origin1 ).GetSquaredNorm(); + double denominator = (pointAlongNormal - origin2).GetSquaredNorm(); + + double distance = sqrt(numerator / denominator); + + if ( distance > 0.001 ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt + { + MITK_DEBUG << " Series seems to contain a tilted (or sheared) geometry"; + MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance; + MITK_DEBUG << " ==> storing this shift for later analysis:"; + MITK_DEBUG << " v right: " << right; + MITK_DEBUG << " v up: " << up; + MITK_DEBUG << " v normal: " << normal; + + Point3Dd projectionRight = projectPointOnLine( origin1, origin2, right ); + Point3Dd projectionNormal = projectPointOnLine( origin1, origin2, normal ); + + m_ShiftRight = (projectionRight - origin2).GetNorm(); + m_ShiftNormal = (projectionNormal - origin2).GetNorm(); + + /* + now also check to which side the image is shifted. + + Calculation e.g. from + http://mathworld.wolfram.com/Point-PlaneDistance.html + */ + + Point3Dd testPoint = origin1; + Vector3Dd planeNormal = up; + + double signedDistance = ( + planeNormal[0] * testPoint[0] + + planeNormal[1] * testPoint[1] + + planeNormal[2] * testPoint[2] + - ( + planeNormal[0] * origin2[0] + + planeNormal[1] * origin2[1] + + planeNormal[2] * origin2[2] + ) + ) + / + sqrt( planeNormal[0] * planeNormal[0] + + planeNormal[1] * planeNormal[1] + + planeNormal[2] * planeNormal[2] + ); + + m_ShiftUp = signedDistance; + + m_ITKAssumedSliceSpacing = (origin2 - origin1).GetNorm(); + // How do we now this is assumed? See header documentation for ITK code references + //double itkAssumedSliceSpacing = sqrt( m_ShiftUp * m_ShiftUp + m_ShiftNormal * m_ShiftNormal ); + + MITK_DEBUG << " shift normal: " << m_ShiftNormal; + MITK_DEBUG << " shift normal assumed by ITK: " << m_ITKAssumedSliceSpacing; + MITK_DEBUG << " shift up: " << m_ShiftUp; + MITK_DEBUG << " shift right: " << m_ShiftRight; + + MITK_DEBUG << " tilt angle (deg): " << atan( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535; + } +} + +Point3D +DicomSeriesReader::GantryTiltInformation::projectPointOnLine( Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection ) +{ + /** + See illustration at http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage472/ + + vector(lineOrigin,p) = normal * ( innerproduct((p - lineOrigin),normal) / squared-length(normal) ) + */ + + Vector3Dd lineOriginToP = p - lineOrigin; + double innerProduct = lineOriginToP * lineDirection; + + double factor = innerProduct / lineDirection.GetSquaredNorm(); + Point3Dd projection = lineOrigin + factor * lineDirection; + + return projection; +} + +double +DicomSeriesReader::GantryTiltInformation::GetTiltCorrectedAdditionalSize() const +{ + return fabs(m_ShiftUp); +} + +double +DicomSeriesReader::GantryTiltInformation::GetTiltAngleInDegrees() const +{ + return atan( fabs(m_ShiftUp) / m_ShiftNormal ) * 180.0 / 3.1415926535; +} + +double +DicomSeriesReader::GantryTiltInformation::GetMatrixCoefficientForCorrectionInWorldCoordinates() const +{ + // so many mm need to be shifted per slice! + return m_ShiftUp / static_cast(m_NumberOfSlicesApart); +} + +double +DicomSeriesReader::GantryTiltInformation::GetRealZSpacing() const +{ + return m_ShiftNormal / static_cast(m_NumberOfSlicesApart); +} + + +bool +DicomSeriesReader::GantryTiltInformation::IsSheared() const +{ + return ( fabs(m_ShiftRight) > 0.001 + || fabs(m_ShiftUp) > 0.001); +} + + +bool +DicomSeriesReader::GantryTiltInformation::IsRegularGantryTilt() const +{ + return ( fabs(m_ShiftRight) < 0.001 + && fabs(m_ShiftUp) > 0.001); +} + +} // end namespace mitk diff --git a/Core/Code/IO/mitkDicomSR_ImageBlockDescriptor.cpp b/Core/Code/IO/mitkDicomSR_ImageBlockDescriptor.cpp new file mode 100644 index 0000000000..92ecbe1763 --- /dev/null +++ b/Core/Code/IO/mitkDicomSR_ImageBlockDescriptor.cpp @@ -0,0 +1,242 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +#include + +namespace mitk +{ + +DicomSeriesReader::ImageBlockDescriptor::ImageBlockDescriptor() +:m_HasGantryTiltCorrected(false) +,m_HasMultipleTimePoints(false) +,m_IsMultiFrameImage(false) +{ +} + +DicomSeriesReader::ImageBlockDescriptor::~ImageBlockDescriptor() +{ + // nothing +} + +DicomSeriesReader::ImageBlockDescriptor::ImageBlockDescriptor(const StringContainer& files) +:m_HasGantryTiltCorrected(false) +,m_HasMultipleTimePoints(false) +,m_IsMultiFrameImage(false) +{ + m_Filenames = files; +} + + +void DicomSeriesReader::ImageBlockDescriptor::AddFile(const std::string& filename) +{ + m_Filenames.push_back( filename ); +} + +void DicomSeriesReader::ImageBlockDescriptor::AddFiles(const StringContainer& files) +{ + m_Filenames.insert( m_Filenames.end(), files.begin(), files.end() ); +} + +DicomSeriesReader::StringContainer DicomSeriesReader::ImageBlockDescriptor::GetFilenames() const +{ + return m_Filenames; +} + +std::string DicomSeriesReader::ImageBlockDescriptor::GetImageBlockUID() const +{ + return m_ImageBlockUID; +} + +std::string DicomSeriesReader::ImageBlockDescriptor::GetSeriesInstanceUID() const +{ + return m_SeriesInstanceUID; +} + +std::string DicomSeriesReader::ImageBlockDescriptor::GetModality() const +{ + return m_Modality; +} + +std::string DicomSeriesReader::ImageBlockDescriptor::GetSOPClassUIDAsString() const +{ + gdcm::UIDs uidKnowledge; + uidKnowledge.SetFromUID( m_SOPClassUID.c_str() ); + return uidKnowledge.GetName(); +} + +std::string DicomSeriesReader::ImageBlockDescriptor::GetSOPClassUID() const +{ + return m_SOPClassUID; +} + +bool DicomSeriesReader::ImageBlockDescriptor::IsMultiFrameImage() const +{ + return m_IsMultiFrameImage; +} + +DicomSeriesReader::ReaderImplementationLevel DicomSeriesReader::ImageBlockDescriptor::GetReaderImplementationLevel() const +{ + if ( this->IsMultiFrameImage() ) + return ReaderImplementationLevel_Unsupported; + + gdcm::UIDs uidKnowledge; + uidKnowledge.SetFromUID( m_SOPClassUID.c_str() ); + + gdcm::UIDs::TSType uid = uidKnowledge; + + switch (uid) + { + case gdcm::UIDs::CTImageStorage: + case gdcm::UIDs::MRImageStorage: + case gdcm::UIDs::PositronEmissionTomographyImageStorage: + case gdcm::UIDs::ComputedRadiographyImageStorage: + case gdcm::UIDs::DigitalXRayImageStorageForPresentation: + case gdcm::UIDs::DigitalXRayImageStorageForProcessing: + return ReaderImplementationLevel_Supported; + + case gdcm::UIDs::NuclearMedicineImageStorage: + return ReaderImplementationLevel_PartlySupported; + + case gdcm::UIDs::SecondaryCaptureImageStorage: + return ReaderImplementationLevel_Implemented; + + default: + return ReaderImplementationLevel_Unsupported; + } +} + +bool DicomSeriesReader::ImageBlockDescriptor::HasGantryTiltCorrected() const +{ + return m_HasGantryTiltCorrected; +} + +/* + PS defined IPS defined PS==IPS + 0 0 --> UNKNOWN spacing, loader will invent + 0 1 --> spacing as at detector surface + 1 0 --> spacing as in patient + 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient + 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector +*/ +DicomSeriesReader::PixelSpacingInterpretation DicomSeriesReader::ImageBlockDescriptor::GetPixelSpacingType() const +{ + if (m_PixelSpacing.empty()) + { + if (m_ImagerPixelSpacing.empty()) + { + return PixelSpacingInterpretation_SpacingUnknown; + } + else + { + return PixelSpacingInterpretation_SpacingAtDetector; + } + } + else // Pixel Spacing defined + { + if (m_ImagerPixelSpacing.empty()) + { + return PixelSpacingInterpretation_SpacingInPatient; + } + else if (m_PixelSpacing != m_ImagerPixelSpacing) + { + return PixelSpacingInterpretation_SpacingInPatient; + } + else + { + return PixelSpacingInterpretation_SpacingAtDetector; + } + } +} + +bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingRelatesToPatient() const +{ + return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingInPatient; +} + +bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingRelatesToDetector() const +{ + return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingAtDetector; +} + +bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingIsUnknown() const +{ + return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingUnknown; +} + +void DicomSeriesReader::ImageBlockDescriptor::SetPixelSpacingInformation(const std::string& pixelSpacing, const std::string& imagerPixelSpacing) +{ + m_PixelSpacing = pixelSpacing; + m_ImagerPixelSpacing = imagerPixelSpacing; +} + +void DicomSeriesReader::ImageBlockDescriptor::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY) const +{ + // preference for "in patient" pixel spacing + if ( !DICOMStringToSpacing( m_PixelSpacing, spacingX, spacingY ) ) + { + // fallback to "on detector" spacing + if ( !DICOMStringToSpacing( m_ImagerPixelSpacing, spacingX, spacingY ) ) + { + // last resort: invent something + spacingX = spacingY = 1.0; + } + } +} + +bool DicomSeriesReader::ImageBlockDescriptor::HasMultipleTimePoints() const +{ + return m_HasMultipleTimePoints; +} + +void DicomSeriesReader::ImageBlockDescriptor::SetImageBlockUID(const std::string& uid) +{ + m_ImageBlockUID = uid; +} + +void DicomSeriesReader::ImageBlockDescriptor::SetSeriesInstanceUID(const std::string& uid) +{ + m_SeriesInstanceUID = uid; +} + +void DicomSeriesReader::ImageBlockDescriptor::SetModality(const std::string& modality) +{ + m_Modality = modality; +} + +void DicomSeriesReader::ImageBlockDescriptor::SetNumberOfFrames(const std::string& numberOfFrames) +{ + m_IsMultiFrameImage = !numberOfFrames.empty(); +} + +void DicomSeriesReader::ImageBlockDescriptor::SetSOPClassUID(const std::string& sopClassUID) +{ + m_SOPClassUID = sopClassUID; +} + + +void DicomSeriesReader::ImageBlockDescriptor::SetHasGantryTiltCorrected(bool on) +{ + m_HasGantryTiltCorrected = on; +} + +void DicomSeriesReader::ImageBlockDescriptor::SetHasMultipleTimePoints(bool on) +{ + m_HasMultipleTimePoints = on; +} + +} // end namespace mitk diff --git a/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel.cpp b/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel.cpp new file mode 100644 index 0000000000..55e4e38ddd --- /dev/null +++ b/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel.cpp @@ -0,0 +1,54 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkDicomSeriesReader.txx" + +namespace mitk +{ + +Image::Pointer +DicomSeriesReader +::MultiplexLoadDICOMByITKRGBPixel(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) +{ + switch (io->GetComponentType()) + { + case DcmIoType::UCHAR: + return LoadDICOMByITK< itk::RGBPixel >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::CHAR: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::USHORT: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::SHORT: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::UINT: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::INT: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::ULONG: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::LONG: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::FLOAT: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::DOUBLE: + return LoadDICOMByITK >(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + default: + MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); + return NULL; + } +} + +} // end namespace mitk diff --git a/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp b/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp new file mode 100644 index 0000000000..caad59e3e2 --- /dev/null +++ b/Core/Code/IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp @@ -0,0 +1,54 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkDicomSeriesReader.txx" + +namespace mitk +{ + +Image::Pointer +DicomSeriesReader +::MultiplexLoadDICOMByITK4DRGBPixel( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) +{ + switch (io->GetComponentType()) + { + case DcmIoType::UCHAR: + return LoadDICOMByITK4D< itk::RGBPixel >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::CHAR: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::USHORT: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::SHORT: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::UINT: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::INT: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::ULONG: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::LONG: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::FLOAT: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::DOUBLE: + return LoadDICOMByITK4D >(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + default: + MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); + return NULL; + } +} + +} // end namespace mitk diff --git a/Core/Code/IO/mitkDicomSR_LoadDICOMScalar.cpp b/Core/Code/IO/mitkDicomSR_LoadDICOMScalar.cpp new file mode 100644 index 0000000000..b469205b30 --- /dev/null +++ b/Core/Code/IO/mitkDicomSR_LoadDICOMScalar.cpp @@ -0,0 +1,57 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkDicomSeriesReader.txx" + +namespace mitk +{ + +Image::Pointer +DicomSeriesReader +::MultiplexLoadDICOMByITKScalar(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) +{ + switch (io->GetComponentType()) + { + case DcmIoType::UCHAR: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::CHAR: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::USHORT: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::SHORT: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::UINT: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::INT: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::ULONG: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::LONG: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::FLOAT: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::DOUBLE: + return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + default: + MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); + return NULL; + } +} + +} // end namespace mitk + +#include + diff --git a/Core/Code/IO/mitkDicomSR_LoadDICOMScalar4D.cpp b/Core/Code/IO/mitkDicomSR_LoadDICOMScalar4D.cpp new file mode 100644 index 0000000000..953d9a32d0 --- /dev/null +++ b/Core/Code/IO/mitkDicomSR_LoadDICOMScalar4D.cpp @@ -0,0 +1,57 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkDicomSeriesReader.txx" + +namespace mitk +{ + +Image::Pointer +DicomSeriesReader +::MultiplexLoadDICOMByITK4DScalar( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) +{ + switch (io->GetComponentType()) + { + case DcmIoType::UCHAR: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::CHAR: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::USHORT: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::SHORT: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::UINT: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::INT: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::ULONG: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::LONG: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::FLOAT: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + case DcmIoType::DOUBLE: + return LoadDICOMByITK4D(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + default: + MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); + return NULL; + } +} + +} // end namespace mitk + +#include + diff --git a/Core/Code/IO/mitkDicomSR_SliceGroupingResult.cpp b/Core/Code/IO/mitkDicomSR_SliceGroupingResult.cpp new file mode 100644 index 0000000000..e853bb5604 --- /dev/null +++ b/Core/Code/IO/mitkDicomSR_SliceGroupingResult.cpp @@ -0,0 +1,69 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +namespace mitk +{ + +DicomSeriesReader::SliceGroupingAnalysisResult::SliceGroupingAnalysisResult() +:m_GantryTilt(false) +{ +} + +DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetBlockFilenames() +{ + return m_GroupedFiles; +} + +DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetUnsortedFilenames() +{ + return m_UnsortedFiles; +} + +bool DicomSeriesReader::SliceGroupingAnalysisResult::ContainsGantryTilt() +{ + return m_GantryTilt; +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToSortedBlock(const std::string& filename) +{ + m_GroupedFiles.push_back( filename ); +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToUnsortedBlock(const std::string& filename) +{ + m_UnsortedFiles.push_back( filename ); +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::AddFilesToUnsortedBlock(const StringContainer& filenames) +{ + m_UnsortedFiles.insert( m_UnsortedFiles.end(), filenames.begin(), filenames.end() ); +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::FlagGantryTilt() +{ + m_GantryTilt = true; +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::UndoPrematureGrouping() +{ + assert( !m_GroupedFiles.empty() ); + m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() ); + m_GroupedFiles.pop_back(); +} + +} // end namespace mitk diff --git a/Core/Code/IO/mitkDicomSeriesReader.cpp b/Core/Code/IO/mitkDicomSeriesReader.cpp index 19c1a9e0bb..bf3b680f4d 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.cpp +++ b/Core/Code/IO/mitkDicomSeriesReader.cpp @@ -1,1981 +1,1779 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { -typedef itk::GDCMSeriesFileNames DcmFileNamesGeneratorType; - -DicomSeriesReader::ImageBlockDescriptor::ImageBlockDescriptor() -:m_HasGantryTiltCorrected(false) -,m_HasMultipleTimePoints(false) -,m_IsMultiFrameImage(false) -{ -} - -DicomSeriesReader::ImageBlockDescriptor::~ImageBlockDescriptor() -{ - // nothing -} - -DicomSeriesReader::ImageBlockDescriptor::ImageBlockDescriptor(const StringContainer& files) -:m_HasGantryTiltCorrected(false) -,m_HasMultipleTimePoints(false) -,m_IsMultiFrameImage(false) -{ - m_Filenames = files; -} - - -void DicomSeriesReader::ImageBlockDescriptor::AddFile(const std::string& filename) -{ - m_Filenames.push_back( filename ); -} - -void DicomSeriesReader::ImageBlockDescriptor::AddFiles(const StringContainer& files) -{ - m_Filenames.insert( m_Filenames.end(), files.begin(), files.end() ); -} - -DicomSeriesReader::StringContainer DicomSeriesReader::ImageBlockDescriptor::GetFilenames() const -{ - return m_Filenames; -} - -std::string DicomSeriesReader::ImageBlockDescriptor::GetImageBlockUID() const -{ - return m_ImageBlockUID; -} - -std::string DicomSeriesReader::ImageBlockDescriptor::GetSeriesInstanceUID() const -{ - return m_SeriesInstanceUID; -} - -std::string DicomSeriesReader::ImageBlockDescriptor::GetModality() const -{ - return m_Modality; -} - -std::string DicomSeriesReader::ImageBlockDescriptor::GetSOPClassUIDAsString() const -{ - gdcm::UIDs uidKnowledge; - uidKnowledge.SetFromUID( m_SOPClassUID.c_str() ); - return uidKnowledge.GetName(); -} - -std::string DicomSeriesReader::ImageBlockDescriptor::GetSOPClassUID() const -{ - return m_SOPClassUID; -} - -bool DicomSeriesReader::ImageBlockDescriptor::IsMultiFrameImage() const -{ - return m_IsMultiFrameImage; -} - -DicomSeriesReader::ReaderImplementationLevel DicomSeriesReader::ImageBlockDescriptor::GetReaderImplementationLevel() const -{ - if ( this->IsMultiFrameImage() ) - return ReaderImplementationLevel_Unsupported; - - gdcm::UIDs uidKnowledge; - uidKnowledge.SetFromUID( m_SOPClassUID.c_str() ); - - gdcm::UIDs::TSType uid = uidKnowledge; - - switch (uid) - { - case gdcm::UIDs::CTImageStorage: - case gdcm::UIDs::MRImageStorage: - case gdcm::UIDs::PositronEmissionTomographyImageStorage: - case gdcm::UIDs::ComputedRadiographyImageStorage: - case gdcm::UIDs::DigitalXRayImageStorageForPresentation: - case gdcm::UIDs::DigitalXRayImageStorageForProcessing: - return ReaderImplementationLevel_Supported; - - case gdcm::UIDs::NuclearMedicineImageStorage: - return ReaderImplementationLevel_PartlySupported; - - case gdcm::UIDs::SecondaryCaptureImageStorage: - return ReaderImplementationLevel_Implemented; - - default: - return ReaderImplementationLevel_Unsupported; - } -} - std::string DicomSeriesReader::ReaderImplementationLevelToString( const ReaderImplementationLevel& enumValue ) { switch (enumValue) { case ReaderImplementationLevel_Supported: return "Supported"; case ReaderImplementationLevel_PartlySupported: return "PartlySupported"; case ReaderImplementationLevel_Implemented: return "Implemented"; case ReaderImplementationLevel_Unsupported: return "Unsupported"; default: return ""; }; } std::string DicomSeriesReader::PixelSpacingInterpretationToString( const PixelSpacingInterpretation& enumValue ) { switch (enumValue) { case PixelSpacingInterpretation_SpacingInPatient: return "In Patient"; case PixelSpacingInterpretation_SpacingAtDetector: return "At Detector"; case PixelSpacingInterpretation_SpacingUnknown: return "Unknown spacing"; default: return ""; }; } - -bool DicomSeriesReader::ImageBlockDescriptor::HasGantryTiltCorrected() const -{ - return m_HasGantryTiltCorrected; -} - -/* - PS defined IPS defined PS==IPS - 0 0 --> UNKNOWN spacing, loader will invent - 0 1 --> spacing as at detector surface - 1 0 --> spacing as in patient - 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient - 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector -*/ -DicomSeriesReader::PixelSpacingInterpretation DicomSeriesReader::ImageBlockDescriptor::GetPixelSpacingType() const -{ - if (m_PixelSpacing.empty()) - { - if (m_ImagerPixelSpacing.empty()) - { - return PixelSpacingInterpretation_SpacingUnknown; - } - else - { - return PixelSpacingInterpretation_SpacingAtDetector; - } - } - else // Pixel Spacing defined - { - if (m_ImagerPixelSpacing.empty()) - { - return PixelSpacingInterpretation_SpacingInPatient; - } - else if (m_PixelSpacing != m_ImagerPixelSpacing) - { - return PixelSpacingInterpretation_SpacingInPatient; - } - else - { - return PixelSpacingInterpretation_SpacingAtDetector; - } - } -} - -bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingRelatesToPatient() const -{ - return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingInPatient; -} - -bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingRelatesToDetector() const -{ - return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingAtDetector; -} - -bool DicomSeriesReader::ImageBlockDescriptor::PixelSpacingIsUnknown() const -{ - return GetPixelSpacingType() == PixelSpacingInterpretation_SpacingUnknown; -} - -void DicomSeriesReader::ImageBlockDescriptor::SetPixelSpacingInformation(const std::string& pixelSpacing, const std::string& imagerPixelSpacing) -{ - m_PixelSpacing = pixelSpacing; - m_ImagerPixelSpacing = imagerPixelSpacing; -} - -void DicomSeriesReader::ImageBlockDescriptor::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY) const -{ - // preference for "in patient" pixel spacing - if ( !DICOMStringToSpacing( m_PixelSpacing, spacingX, spacingY ) ) - { - // fallback to "on detector" spacing - if ( !DICOMStringToSpacing( m_ImagerPixelSpacing, spacingX, spacingY ) ) - { - // last resort: invent something - spacingX = spacingY = 1.0; - } - } -} - -bool DicomSeriesReader::ImageBlockDescriptor::HasMultipleTimePoints() const -{ - return m_HasMultipleTimePoints; -} - -void DicomSeriesReader::ImageBlockDescriptor::SetImageBlockUID(const std::string& uid) -{ - m_ImageBlockUID = uid; -} - -void DicomSeriesReader::ImageBlockDescriptor::SetSeriesInstanceUID(const std::string& uid) -{ - m_SeriesInstanceUID = uid; -} - -void DicomSeriesReader::ImageBlockDescriptor::SetModality(const std::string& modality) -{ - m_Modality = modality; -} - -void DicomSeriesReader::ImageBlockDescriptor::SetNumberOfFrames(const std::string& numberOfFrames) -{ - m_IsMultiFrameImage = !numberOfFrames.empty(); -} - -void DicomSeriesReader::ImageBlockDescriptor::SetSOPClassUID(const std::string& sopClassUID) -{ - m_SOPClassUID = sopClassUID; -} - - -void DicomSeriesReader::ImageBlockDescriptor::SetHasGantryTiltCorrected(bool on) -{ - m_HasGantryTiltCorrected = on; -} - -void DicomSeriesReader::ImageBlockDescriptor::SetHasMultipleTimePoints(bool on) -{ - m_HasMultipleTimePoints = on; -} - - - -DicomSeriesReader::SliceGroupingAnalysisResult::SliceGroupingAnalysisResult() -:m_GantryTilt(false) -{ -} - -DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetBlockFilenames() -{ - return m_GroupedFiles; -} - -DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetUnsortedFilenames() -{ - return m_UnsortedFiles; -} - -bool DicomSeriesReader::SliceGroupingAnalysisResult::ContainsGantryTilt() -{ - return m_GantryTilt; -} - -void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToSortedBlock(const std::string& filename) -{ - m_GroupedFiles.push_back( filename ); -} - -void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToUnsortedBlock(const std::string& filename) -{ - m_UnsortedFiles.push_back( filename ); -} - -void DicomSeriesReader::SliceGroupingAnalysisResult::AddFilesToUnsortedBlock(const StringContainer& filenames) -{ - m_UnsortedFiles.insert( m_UnsortedFiles.end(), filenames.begin(), filenames.end() ); -} - -void DicomSeriesReader::SliceGroupingAnalysisResult::FlagGantryTilt() -{ - m_GantryTilt = true; -} - -void DicomSeriesReader::SliceGroupingAnalysisResult::UndoPrematureGrouping() -{ - assert( !m_GroupedFiles.empty() ); - m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() ); - m_GroupedFiles.pop_back(); -} - - - - - - - const DicomSeriesReader::TagToPropertyMapType& DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; // Image Pixel module dictionary["0028|0004"] = "dicom.pixel.PhotometricInterpretation"; dictionary["0028|0010"] = "dicom.pixel.Rows"; dictionary["0028|0011"] = "dicom.pixel.Columns"; // Image Plane module dictionary["0028|0030"] = "dicom.PixelSpacing"; dictionary["0018|1164"] = "dicom.ImagerPixelSpacing"; initialized = true; } return dictionary; } DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, correctTilt, callback, preLoadedImageBlock)) { if( filenames.empty() ) { return NULL; } return node; } else { return NULL; } } bool DicomSeriesReader::LoadDicomSeries( const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { if( filenames.empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(NULL); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); - if (io->GetPixelType() == itk::ImageIOBase::SCALAR) + if (io->GetPixelType() == itk::ImageIOBase::SCALAR || + io->GetPixelType() == itk::ImageIOBase::RGB) { - switch (io->GetComponentType()) - { - case DcmIoType::UCHAR: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::CHAR: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::USHORT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::SHORT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::UINT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::INT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::ULONG: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::LONG: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::FLOAT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::DOUBLE: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - default: - MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); - } - } - else if (io->GetPixelType() == itk::ImageIOBase::RGB) - { - switch (io->GetComponentType()) - { - case DcmIoType::UCHAR: - DicomSeriesReader::LoadDicom< itk::RGBPixel >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::CHAR: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::USHORT: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::SHORT: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::UINT: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::INT: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::ULONG: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::LONG: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::FLOAT: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - case DcmIoType::DOUBLE: - DicomSeriesReader::LoadDicom >(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); - break; - default: - MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); - } + LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); } if (node.GetData()) { return true; } } } catch(itk::MemoryAllocationError& e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch(std::exception& e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch(...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { //Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // axial gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018,0x602e> spaceTagY; gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018,0x6026> physicalTagY; gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); imageItk->SetSpacing(mySpacing); imageItk->SetRegions( myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX*dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane + timeCount*numberOfSlices*planeSize; // add time offset iterator.Set( new_pixels[ adressedPixel ] ); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } -DicomSeriesReader::GantryTiltInformation::GantryTiltInformation() -: m_ShiftUp(0.0) -, m_ShiftRight(0.0) -, m_ShiftNormal(0.0) -, m_ITKAssumedSliceSpacing(0.0) -, m_NumberOfSlicesApart(1) -{ -} - - -#define doublepoint(x) \ - Point3Dd x; \ - x[0] = x ## f[0]; \ - x[1] = x ## f[1]; \ - x[2] = x ## f[2]; - - -#define doublevector(x) \ - Vector3Dd x; \ - x[0] = x ## f[0]; \ - x[1] = x ## f[1]; \ - x[2] = x ## f[2]; - -DicomSeriesReader::GantryTiltInformation::GantryTiltInformation( - const Point3D& origin1f, const Point3D& origin2f, - const Vector3D& rightf, const Vector3D& upf, - unsigned int numberOfSlicesApart) -: m_ShiftUp(0.0) -, m_ShiftRight(0.0) -, m_ShiftNormal(0.0) -, m_NumberOfSlicesApart(numberOfSlicesApart) -{ - assert(numberOfSlicesApart); - - doublepoint(origin1); - doublepoint(origin2); - doublevector(right); - doublevector(up); - - // determine if slice 1 (imagePosition1 and imageOrientation1) and slice 2 can be in one orthogonal slice stack: - // calculate a line from origin 1, directed along the normal of slice (calculated as the cross product of orientation 1) - // check if this line passes through origin 2 - - /* - Determine if line (imagePosition2 + l * normal) contains imagePosition1. - Done by calculating the distance of imagePosition1 from line (imagePosition2 + l *normal) - - E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html - - squared distance = | (pointAlongNormal - origin2) x (origin2 - origin1) | ^ 2 - / - |pointAlongNormal - origin2| ^ 2 - - ( x meaning the cross product ) - */ - - Vector3Dd normal = itk::CrossProduct(right, up); - Point3Dd pointAlongNormal = origin2 + normal; - - double numerator = itk::CrossProduct( pointAlongNormal - origin2 , origin2 - origin1 ).GetSquaredNorm(); - double denominator = (pointAlongNormal - origin2).GetSquaredNorm(); - - double distance = sqrt(numerator / denominator); - - if ( distance > 0.001 ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt - { - MITK_DEBUG << " Series seems to contain a tilted (or sheared) geometry"; - MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance; - MITK_DEBUG << " ==> storing this shift for later analysis:"; - MITK_DEBUG << " v right: " << right; - MITK_DEBUG << " v up: " << up; - MITK_DEBUG << " v normal: " << normal; - - Point3Dd projectionRight = projectPointOnLine( origin1, origin2, right ); - Point3Dd projectionNormal = projectPointOnLine( origin1, origin2, normal ); - - m_ShiftRight = (projectionRight - origin2).GetNorm(); - m_ShiftNormal = (projectionNormal - origin2).GetNorm(); - - /* - now also check to which side the image is shifted. - - Calculation e.g. from - http://mathworld.wolfram.com/Point-PlaneDistance.html - */ - - Point3Dd testPoint = origin1; - Vector3Dd planeNormal = up; - - double signedDistance = ( - planeNormal[0] * testPoint[0] - + planeNormal[1] * testPoint[1] - + planeNormal[2] * testPoint[2] - - ( - planeNormal[0] * origin2[0] - + planeNormal[1] * origin2[1] - + planeNormal[2] * origin2[2] - ) - ) - / - sqrt( planeNormal[0] * planeNormal[0] - + planeNormal[1] * planeNormal[1] - + planeNormal[2] * planeNormal[2] - ); - - m_ShiftUp = signedDistance; - - m_ITKAssumedSliceSpacing = (origin2 - origin1).GetNorm(); - // How do we now this is assumed? See header documentation for ITK code references - //double itkAssumedSliceSpacing = sqrt( m_ShiftUp * m_ShiftUp + m_ShiftNormal * m_ShiftNormal ); - - MITK_DEBUG << " shift normal: " << m_ShiftNormal; - MITK_DEBUG << " shift normal assumed by ITK: " << m_ITKAssumedSliceSpacing; - MITK_DEBUG << " shift up: " << m_ShiftUp; - MITK_DEBUG << " shift right: " << m_ShiftRight; - - MITK_DEBUG << " tilt angle (deg): " << atan( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535; - } -} - -Point3D -DicomSeriesReader::GantryTiltInformation::projectPointOnLine( Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection ) -{ - /** - See illustration at http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage472/ - - vector(lineOrigin,p) = normal * ( innerproduct((p - lineOrigin),normal) / squared-length(normal) ) - */ - - Vector3Dd lineOriginToP = p - lineOrigin; - double innerProduct = lineOriginToP * lineDirection; - - double factor = innerProduct / lineDirection.GetSquaredNorm(); - Point3Dd projection = lineOrigin + factor * lineDirection; - - return projection; -} - -double -DicomSeriesReader::GantryTiltInformation::GetTiltCorrectedAdditionalSize() const -{ - return fabs(m_ShiftUp); -} - -double -DicomSeriesReader::GantryTiltInformation::GetTiltAngleInDegrees() const -{ - return atan( fabs(m_ShiftUp) / m_ShiftNormal ) * 180.0 / 3.1415926535; -} - -double -DicomSeriesReader::GantryTiltInformation::GetMatrixCoefficientForCorrectionInWorldCoordinates() const -{ - // so many mm need to be shifted per slice! - return m_ShiftUp / static_cast(m_NumberOfSlicesApart); -} - -double -DicomSeriesReader::GantryTiltInformation::GetRealZSpacing() const -{ - return m_ShiftNormal / static_cast(m_NumberOfSlicesApart); -} - - -bool -DicomSeriesReader::GantryTiltInformation::IsSheared() const -{ - return ( fabs(m_ShiftRight) > 0.001 - || fabs(m_ShiftUp) > 0.001); -} - - -bool -DicomSeriesReader::GantryTiltInformation::IsRegularGantryTilt() const -{ - return ( fabs(m_ShiftRight) < 0.001 - && fabs(m_ShiftUp) > 0.001); -} - - std::string DicomSeriesReader::ConstCharStarToString(const char* s) { return s ? std::string(s) : std::string(); } bool DicomSeriesReader::DICOMStringToSpacing(const std::string& s, ScalarType& spacingX, ScalarType& spacingY) { bool successful = false; std::istringstream spacingReader(s); std::string spacing; if ( std::getline( spacingReader, spacing, '\\' ) ) { spacingY = atof( spacing.c_str() ); if ( std::getline( spacingReader, spacing, '\\' ) ) { spacingX = atof( spacing.c_str() ); successful = true; } } return successful; } Point3D DicomSeriesReader::DICOMStringToPoint3D(const std::string& s, bool& successful) { Point3D p; successful = true; std::istringstream originReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( originReader, coordinate, '\\' ) && dim < 3) { p[dim++]= atof(coordinate.c_str()); } if (dim && dim != 3) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; } else if (dim == 0) { successful = false; p.Fill(0.0); // assume default (0,0,0) } return p; } void DicomSeriesReader::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) { successful = true; std::istringstream orientationReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( orientationReader, coordinate, '\\' ) && dim < 6 ) { if (dim<3) { right[dim++] = atof(coordinate.c_str()); } else { up[dim++ - 3] = atof(coordinate.c_str()); } } if (dim && dim != 6) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; } else if (dim == 0) { // fill with defaults right.Fill(0.0); right[0] = 1.0; up.Fill(0.0); up[1] = 1.0; successful = false; } } DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer& files, bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType& tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (StringContainer::const_iterator fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagImagePositionPatient] ); if (thisOriginString.empty()) { // don't let such files be in a common group. Everything without position information will be loaded as a single slice: // with standard DICOM files this can happen to: CR, DX, SC MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis (no position information)"; // we already have one occupying this position if ( result.GetBlockFilenames().empty() ) // nothing WITH position information yet { // ==> this is a group of its own, stop processing, come back later result.AddFileToSortedBlock( *fileIter ); StringContainer remainingFiles; remainingFiles.insert( remainingFiles.end(), fileIter+1, files.end() ); result.AddFilesToUnsortedBlock( remainingFiles ); fileFitsIntoPattern = false; break; // no files anymore } else { // ==> this does not match, consider later result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; continue; // next file } } bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " /* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError ); GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) { // check if this is at least roughly the same angle as recorded in DICOM tags if ( tagValueMappings[fileIter->c_str()].find(tagGantryTilt) != tagValueMappings[fileIter->c_str()].end() ) { // read value, compare to calculated angle std::string tiltStr = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagGantryTilt] ); double angle = atof(tiltStr.c_str()); MITK_DEBUG << "Comparing recorded tilt angle " << angle << " against calculated value " << tiltInfo.GetTiltAngleInDegrees(); // TODO we probably want the signs correct, too (that depends: this is just a rough check, nothing serious) if ( fabs(angle) - tiltInfo.GetTiltAngleInDegrees() > 0.25) { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else // tilt angle from header is less than 0.25 degrees different from what we calculated, assume this is fine { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // we cannot check the calculated tilt angle against the one from the dicom header (so we assume we are right) { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // caller does not want tilt compensation OR shearing is more complicated than tilt { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } } else // not sheared { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if ( result.ContainsGantryTilt() ) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if ( result.GetBlockFilenames().size() == 2 ) { result.UndoPrematureGrouping(); } } return result; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer& /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID scanner.AddTag( tagSOPClassUID ); const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID scanner.AddTag( tagSeriesInstanceUID ); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag( tagImageOrientation ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing scanner.AddTag( tagImagerPixelSpacing ); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag( tagSliceThickness ); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag( tagNumberOfRows ); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag( tagNumberOfColumns ); const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt scanner.AddTag( tagGantryTilt ); const gdcm::Tag tagModality(0x0008, 0x0060); // modality scanner.AddTag( tagModality ); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag( tagNumberOfFrames ); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) scanner.AddTag( tagImagePositionPatient ); // TODO add further restrictions from arguments (when anybody asks for it) FileNamesGrouping result; // let GDCM scan files if ( !scanner.Scan( files ) ) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return result; } // assign files IDs that will separate them for loading into image blocks for (gdcm::Scanner::ConstIterator fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries if ( std::string(fileIter->first) == std::string("DICOMDIR") ) continue; /* sort out multi-frame if ( scanner.GetValue( fileIter->first , tagNumberOfFrames ) ) { MITK_INFO << "Ignoring " << fileIter->first << " because we cannot handle multi-frame images."; continue; } */ // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) ); result[ moreUniqueSeriesId ].AddFile( fileIter->first ); } // PART II: sort slices spatially (or at least consistently if this is NOT possible, see method) for ( FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter ) { try { result[ groupIter->first ] = ImageBlockDescriptor( SortSeriesSlices( groupIter->second.GetFilenames() ) ); // sort each slice group spatially } catch(...) { MITK_ERROR << "Caught something."; } } // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks // * this includes images which DO NOT PROVIDE spatial information, i.e. all images w/o ImagePositionPatient will be loaded separately FileNamesGrouping groupsOf3DPlusTBlocks; // final result of this function for ( FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter ) { FileNamesGrouping groupsOf3DBlocks; // intermediate result for only this group(!) std::map mapOf3DBlockAnalysisResults; StringContainer filesStillToAnalyze = groupIter->second.GetFilenames(); std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, groupImagesWithGantryTilt, scanner.GetMappings() ); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; ImageBlockDescriptor thisBlock( analysisResult.GetBlockFilenames() ); std::string firstFileInBlock = thisBlock.GetFilenames().front(); thisBlock.SetImageBlockUID( newGroupUID.str() ); thisBlock.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSeriesInstanceUID ) ) ); thisBlock.SetHasGantryTiltCorrected( analysisResult.ContainsGantryTilt() ); thisBlock.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSOPClassUID ) ) ); thisBlock.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagNumberOfFrames ) ) ); thisBlock.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagModality ) ) ); thisBlock.SetPixelSpacingInformation( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagPixelSpacing ) ), DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagImagerPixelSpacing ) ) ); thisBlock.SetHasMultipleTimePoints( false ); groupsOf3DBlocks[ newGroupUID.str() ] = thisBlock; //MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files"; MITK_DEBUG << "Result: sorted 3D group with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files"; StringContainer debugOutputFiles = analysisResult.GetBlockFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " IN " << *siter; ++subgroup; filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis for (StringContainer::const_iterator siter = filesStillToAnalyze.begin(); siter != filesStillToAnalyze.end(); ++siter) MITK_DEBUG << " OUT " << *siter; } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of groupsOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if ( !sortTo3DPlust ) { // copy 3D blocks to output groupsOf3DPlusTBlocks.insert( groupsOf3DBlocks.begin(), groupsOf3DBlocks.end() ); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for ( FileNamesGrouping::const_iterator block3DIter = groupsOf3DBlocks.begin(); block3DIter != groupsOf3DBlocks.end(); ++block3DIter ) { unsigned int numberOfFilesInThisBlock = block3DIter->second.GetFilenames().size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; MITK_DEBUG << " 3D+t group " << thisBlockKey; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ), *previous_origin_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ), *destination_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ), *previous_destination_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { std::string thisOriginString = ConstCharStarToString( origin_value ); std::string previousOriginString = ConstCharStarToString( previous_origin_value ); // also compare last origin, because this might differ if z-spacing is different std::string thisDestinationString = ConstCharStarToString( destination_value ); std::string previousDestinationString = ConstCharStarToString( previous_destination_value ); identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); } } catch(...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block groupsOf3DPlusTBlocks[previousBlockKey].AddFiles( block3DIter->second.GetFilenames() ); groupsOf3DPlusTBlocks[previousBlockKey].SetHasMultipleTimePoints(true); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; int numberOfTimeSteps = groupsOf3DPlusTBlocks[previousBlockKey].GetFilenames().size() / numberOfFilesInPreviousBlock; MITK_DEBUG << " ==> group closed with " << numberOfTimeSteps << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for ( FileNamesGrouping::const_iterator groupIter = groupsOf3DPlusTBlocks.begin(); groupIter != groupsOf3DPlusTBlocks.end(); ++groupIter ) { ImageBlockDescriptor block = groupIter->second; MITK_DEBUG << " " << block.GetFilenames().size() << " '" << block.GetModality() << "' images (" << block.GetSOPClassUIDAsString() << ") in volume " << block.GetImageBlockUID(); MITK_DEBUG << " (gantry tilt : " << (block.HasGantryTiltCorrected()?"Yes":"No") << "; " "pixel spacing : " << PixelSpacingInterpretationToString( block.GetPixelSpacingType() ) << "; " "3D+t: " << (block.HasMultipleTimePoints()?"Yes":"No") << "; " "reader support: " << ReaderImplementationLevelToString( block.GetReaderImplementationLevel() ) << ")"; StringContainer debugOutputFiles = block.GetFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " F " << *siter; } MITK_DEBUG << "================================================================================"; return groupsOf3DPlusTBlocks; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load( dir.c_str(), false ); // non-recursive return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ) { std::string result; try { result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") ); } catch (std::exception&) { // we are happy with even nothing, this will just group images of a series //MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ) { const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames const char* tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; if (!tagSeriesInstanceUid) { mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image, so stopping here."; } std::string constructedID = tagSeriesInstanceUid; constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImagerPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfFrames ); // be a bit tolerant for orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263) // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); if (tagValueMap.find(tagImageOrientation) != tagValueMap.end()) { bool conversionError(false); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); DICOMStringToOrientationVectors( tagValueMap[tagImageOrientation], right, up, conversionError ); //string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], right[1], right[2], up[0], up[1], up[2]); std::ostringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(5); ss << right[0] << "\\" << right[1] << "\\" << right[2] << "\\" << up[0] << "\\" << up[1] << "\\" << up[2]; std::string simplifiedOrientationString(ss.str()); constructedID += IDifyTagValue( simplifiedOrientationString ); } constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string& value) { std::string IDifiedValue( value ); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { FileNamesGrouping allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for ( FileNamesGrouping::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter ) { if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid { return idIter->second.GetFilenames(); } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns (each piece of information except the rows/columns might be missing) sorting with GdcmSortFunction tries its best by sorting by spatial position and more hints (acquisition number, acquisition time, trigger time) but will always produce a sorting by falling back to SOP Instance UID. */ gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { if (sorter.Sort(unsortedFilenames)) { return sorter.GetFilenames(); } else { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } catch(std::logic_error&) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { // This method MUST accept missing location and position information (and all else, too) // because we cannot rely on anything // (restriction on the sentence before: we have to provide consistent sorting, so we // rely on the minimum information all DICOM files need to provide: SOP Instance UID) /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns */ static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) static const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation // see if we have Image Position and Orientation if ( ds1.FindDataElement(tagImagePositionPatient) && ds1.FindDataElement(tagImageOrientation) && ds2.FindDataElement(tagImagePositionPatient) && ds2.FindDataElement(tagImageOrientation) ) { gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020,0x0032> image_pos2; gdcm::Attribute<0x0020,0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); /* we tolerate very small differences in image orientation, since we got to know about acquisitions where these values change across a single series (7th decimal digit) (http://bugs.mitk.org/show_bug.cgi?id=12263) still, we want to check if our assumption of 'almost equal' orientations is valid */ for (unsigned int dim = 0; dim < 6; ++dim) { if ( fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001 ) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; // this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } // if we can sort by just comparing the distance, we do exactly that if ( fabs(dist1 - dist2) >= mitk::eps) { // default: compare position return dist1 < dist2; } else // we need to check more properties to distinguish slices { // try to sort by Acquisition Number static const gdcm::Tag tagAcquisitionNumber(0x0020, 0x0012); if (ds1.FindDataElement(tagAcquisitionNumber) && ds2.FindDataElement(tagAcquisitionNumber)) { gdcm::Attribute<0x0020,0x0012> acquisition_number1; // Acquisition number gdcm::Attribute<0x0020,0x0012> acquisition_number2; acquisition_number1.Set(ds1); acquisition_number2.Set(ds2); if (acquisition_number1 != acquisition_number2) { return acquisition_number1 < acquisition_number2; } else // neither position nor acquisition number are good for sorting, so check more { // try to sort by Acquisition Time static const gdcm::Tag tagAcquisitionTime(0x0008, 0x0032); if (ds1.FindDataElement(tagAcquisitionTime) && ds2.FindDataElement(tagAcquisitionTime)) { gdcm::Attribute<0x0008,0x0032> acquisition_time1; // Acquisition time gdcm::Attribute<0x0008,0x0032> acquisition_time2; acquisition_time1.Set(ds1); acquisition_time2.Set(ds2); if (acquisition_time1 != acquisition_time2) { return acquisition_time1 < acquisition_time2; } else // we gave up on image position, acquisition number and acquisition time now { // let's try trigger time static const gdcm::Tag tagTriggerTime(0x0018, 0x1060); if (ds1.FindDataElement(tagTriggerTime) && ds2.FindDataElement(tagTriggerTime)) { gdcm::Attribute<0x0018,0x1060> trigger_time1; // Trigger time gdcm::Attribute<0x0018,0x1060> trigger_time2; trigger_time1.Set(ds1); trigger_time2.Set(ds2); if (trigger_time1 != trigger_time2) { return trigger_time1 < trigger_time2; } // ELSE! // for this and many previous ifs we fall through if nothing lets us sort } // . } // . } // . } } } } // . // LAST RESORT: all valuable information for sorting is missing. // Sort by some meaningless but unique identifiers to satisfy the sort function static const gdcm::Tag tagSOPInstanceUID(0x0008, 0x0018); if (ds1.FindDataElement(tagSOPInstanceUID) && ds2.FindDataElement(tagSOPInstanceUID)) { MITK_DEBUG << "Dicom images are missing attributes for a meaningful sorting, falling back to SOP instance UID comparison."; gdcm::Attribute<0x0008,0x0018> SOPInstanceUID1; // SOP instance UID is mandatory and unique gdcm::Attribute<0x0008,0x0018> SOPInstanceUID2; SOPInstanceUID1.Set(ds1); SOPInstanceUID2.Set(ds2); return SOPInstanceUID1 < SOPInstanceUID2; } else { // no DICOM file should really come down here, this should only be reached with unskillful and unlucky manipulation of files std::string error_message("Malformed DICOM images, which do not even contain a SOP Instance UID."); MITK_ERROR << error_message; throw std::logic_error( error_message ); } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif //configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor& blockInfo, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, blockInfo, image); } void DicomSeriesReader::CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); //DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for ( std::list::iterator i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++ ) { const StringContainer& files = (*i); unsigned int slice(0); for ( StringContainer::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter, ++slice ) { filesForSlices.SetTableValue( slice, *fIter ); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if(tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if(tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if(tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) ); //If more than one time step add postfix ".t" + timestep if(timeStep != 0) { std::ostringstream postfix; postfix << ".t" << timeStep; propertyKeySliceLocation.append(postfix.str()); propertyKeyInstanceNumber.append(postfix.str()); propertyKeySOPInstanceNumber.append(postfix.str()); } image->SetProperty( propertyKeySliceLocation.c_str(), StringLookupTableProperty::New( sliceLocationForSlices ) ); image->SetProperty( propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New( instanceNumberForSlices ) ); image->SetProperty( propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New( SOPInstanceNumberForSlices ) ); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary& dict = io->GetMetaDataDictionary(); const TagToPropertyMapType& propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); itk::MetaDataDictionary::ConstIterator dictIter = dict.Begin(); while ( dictIter != dict.End() ) { //MITK_DEBUG << "Key " << dictIter->first; std::string value; if ( itk::ExposeMetaData( dict, dictIter->first, value ) ) { //MITK_DEBUG << "Value " << value; TagToPropertyMapType::const_iterator valuePosition = propertyLookup.find( dictIter->first ); if ( valuePosition != propertyLookup.end() ) { std::string propertyKey = valuePosition->second; //MITK_DEBUG << "--> " << propertyKey; image->SetProperty( propertyKey.c_str(), StringProperty::New(value) ); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..." ; } ++dictIter; } // copy imageblockdescriptor as properties image->SetProperty("dicomseriesreader.SOPClass", StringProperty::New(blockInfo.GetSOPClassUIDAsString())); image->SetProperty("dicomseriesreader.ReaderImplementationLevelString", StringProperty::New(ReaderImplementationLevelToString( blockInfo.GetReaderImplementationLevel() ))); image->SetProperty("dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( blockInfo.GetReaderImplementationLevel() )); image->SetProperty("dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New(PixelSpacingInterpretationToString( blockInfo.GetPixelSpacingType() ))); image->SetProperty("dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New(blockInfo.GetPixelSpacingType())); image->SetProperty("dicomseriesreader.MultiFrameImage", BoolProperty::New(blockInfo.IsMultiFrameImage())); image->SetProperty("dicomseriesreader.GantyTiltCorrected", BoolProperty::New(blockInfo.HasGantryTiltCorrected())); image->SetProperty("dicomseriesreader.3D+t", BoolProperty::New(blockInfo.HasMultipleTimePoints())); } void DicomSeriesReader::FixSpacingInformation( mitk::Image* image, const ImageBlockDescriptor& imageBlockDescriptor ) { // spacing provided by ITK/GDCM Vector3D imageSpacing = image->GetGeometry()->GetSpacing(); ScalarType imageSpacingX = imageSpacing[0]; ScalarType imageSpacingY = imageSpacing[1]; // spacing as desired by MITK (preference for "in patient", else "on detector", or "1.0/1.0") ScalarType desiredSpacingX = imageSpacingX; ScalarType desiredSpacingY = imageSpacingY; imageBlockDescriptor.GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); MITK_DEBUG << "Loaded spacing: " << imageSpacingX << "/" << imageSpacingY; MITK_DEBUG << "Corrected spacing: " << desiredSpacingX << "/" << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; image->GetGeometry()->SetSpacing( imageSpacing ); } -} // end namespace mitk +void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) +{ + const char* previousCLocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + std::locale previousCppLocale( std::cin.getloc() ); + std::locale l( "C" ); + std::cin.imbue(l); + + ImageBlockDescriptor imageBlockDescriptor; + + const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) + const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation + const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID + const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID + const gdcm::Tag tagModality(0x0008, 0x0060); // modality + const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing + const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing + const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames + + try + { + Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock; + CallbackCommand *command = callback ? new CallbackCommand(callback) : 0; + bool initialize_node = false; + + /* special case for Philips 3D+t ultrasound images */ + if ( DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str()) ) + { + // TODO what about imageBlockDescriptor? + // TODO what about preLoadedImageBlock? + ReadPhilips3DDicom(filenames.front().c_str(), image); + initialize_node = true; + } + else + { + /* default case: assume "normal" image blocks, possibly 3D+t */ + bool canLoadAs4D(true); + gdcm::Scanner scanner; + ScanForSliceInformation(filenames, scanner); + + // need non-const access for map + gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings()); + + std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, tagValueMappings, sort, canLoadAs4D ); + unsigned int volume_count = imageBlocks.size(); + + imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSeriesInstanceUID ) ) ); + imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSOPClassUID ) ) ); + imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagModality ) ) ); + imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagNumberOfFrames ) ) ); + imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagPixelSpacing ) ), + ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagImagerPixelSpacing ) ) ); + + GantryTiltInformation tiltInfo; + + // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice possible + if ( !imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt) + { + // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents + // check first and last position slice from tags, make some calculations to detect tilt + + std::string firstFilename(imageBlocks.front().front()); + // calculate from first and last slice to minimize rounding errors + std::string secondFilename(imageBlocks.front().back()); + + std::string imagePosition1( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImagePositionPatient ] ) ); + std::string imageOrientation( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImageOrientation ] ) ); + std::string imagePosition2( ConstCharStarToString( tagValueMappings[secondFilename.c_str() ][ tagImagePositionPatient ] ) ); + + bool ignoredConversionError(-42); // hard to get here, no graceful way to react + Point3D origin1( DICOMStringToPoint3D( imagePosition1, ignoredConversionError ) ); + Point3D origin2( DICOMStringToPoint3D( imagePosition2, ignoredConversionError ) ); + + Vector3D right; right.Fill(0.0); + Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point + DICOMStringToOrientationVectors( imageOrientation, right, up, ignoredConversionError ); + + tiltInfo = GantryTiltInformation ( origin1, origin2, right, up, filenames.size()-1 ); + correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt(); + } + else + { + correctTilt = false; // we CANNOT do that + } + + imageBlockDescriptor.SetHasGantryTiltCorrected( correctTilt ); + + if (volume_count == 1 || !canLoadAs4D || !load4D) + { + + DcmIoType::Pointer io; + image = MultiplexLoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock ); // load first 3D block + + imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded + imageBlockDescriptor.SetHasMultipleTimePoints( false ); -#include + FixSpacingInformation( image, imageBlockDescriptor ); + CopyMetaDataToImageProperties( imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image); + + initialize_node = true; + } + else if (volume_count > 1) + { + imageBlockDescriptor.AddFiles(filenames); // all is loaded + imageBlockDescriptor.SetHasMultipleTimePoints( true ); + + DcmIoType::Pointer io; + image = MultiplexLoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock ); + + initialize_node = true; + } + } + + if (initialize_node) + { + // forward some image properties to node + node.GetPropertyList()->ConcatenatePropertyList( image->GetPropertyList(), true ); + + node.SetData( image ); + setlocale(LC_NUMERIC, previousCLocale); + std::cin.imbue(previousCppLocale); + } + + MITK_DEBUG << "--------------------------------------------------------------------------------"; + MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):"; + MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; + MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No"); + MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); + MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0]; + MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No"); + MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No"); + MITK_DEBUG << "--------------------------------------------------------------------------------"; + } + catch (std::exception& e) + { + // reset locale then throw up + setlocale(LC_NUMERIC, previousCLocale); + std::cin.imbue(previousCppLocale); + + MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom"; + + throw e; + } +} + +void +DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner& scanner) +{ + const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) + scanner.AddTag(tagImagePositionPatient); + + const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID + scanner.AddTag(tagSeriesInstanceUID); + + const gdcm::Tag tagImageOrientation(0x0020,0x0037); //Image orientation + scanner.AddTag(tagImageOrientation); + + const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location + scanner.AddTag( tagSliceLocation ); + + const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number + scanner.AddTag( tagInstanceNumber ); + + const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number + scanner.AddTag( tagSOPInstanceNumber ); + + const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing + scanner.AddTag( tagPixelSpacing ); + + const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing + scanner.AddTag( tagImagerPixelSpacing ); + + const gdcm::Tag tagModality(0x0008, 0x0060); // Modality + scanner.AddTag( tagModality ); + + const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID + scanner.AddTag( tagSOPClassUID ); + + const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames + scanner.AddTag( tagNumberOfFrames ); + + scanner.Scan(filenames); // make available image information for each file +} + +std::list +DicomSeriesReader::SortIntoBlocksFor3DplusT( + const StringContainer& presortedFilenames, + const gdcm::Scanner::MappingType& tagValueMappings, + bool /*sort*/, + bool& canLoadAs4D ) +{ + std::list imageBlocks; + + // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022) + StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames); + + std::string firstPosition; + unsigned int numberOfBlocks(0); // number of 3D image blocks + + static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) + + // loop files to determine number of image blocks + for (StringContainer::const_iterator fileIter = sorted_filenames.begin(); + fileIter != sorted_filenames.end(); + ++fileIter) + { + gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find( fileIter->c_str() )->second; + + if(tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end()) + { + // we expect to get images w/ missing position information ONLY as separated blocks. + assert( presortedFilenames.size() == 1 ); + numberOfBlocks = 1; + break; + } + + std::string position = tagToValueMap.find(tagImagePositionPatient)->second; + MITK_DEBUG << " " << *fileIter << " at " << position; + if (firstPosition.empty()) + { + firstPosition = position; + } + + if ( position == firstPosition ) + { + ++numberOfBlocks; + } + else + { + break; // enough information to know the number of image blocks + } + } + + MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps"; + + if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files + + + // loop files to sort them into image blocks + unsigned int numberOfExpectedSlices(0); + for (unsigned int block = 0; block < numberOfBlocks; ++block) + { + StringContainer filesOfCurrentBlock; + + for ( StringContainer::const_iterator fileIter = sorted_filenames.begin() + block; + fileIter != sorted_filenames.end(); + //fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts + ) + { + filesOfCurrentBlock.push_back( *fileIter ); + for (unsigned int b = 0; b < numberOfBlocks; ++b) + { + if (fileIter != sorted_filenames.end()) + ++fileIter; + } + } + + imageBlocks.push_back(filesOfCurrentBlock); + + if (block == 0) + { + numberOfExpectedSlices = filesOfCurrentBlock.size(); + } + else + { + if (filesOfCurrentBlock.size() != numberOfExpectedSlices) + { + MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks + << " image blocks of " + << numberOfExpectedSlices + << " images each. Block " + << block + << " got " + << filesOfCurrentBlock.size() + << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much as possible) + canLoadAs4D = false; + } + } + } + + return imageBlocks; +} + +Image::Pointer +DicomSeriesReader +::MultiplexLoadDICOMByITK(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) +{ + io = DcmIoType::New(); + io->SetFileName(filenames.front().c_str()); + io->ReadImageInformation(); + + if (io->GetPixelType() == itk::ImageIOBase::SCALAR) + { + return MultiplexLoadDICOMByITKScalar(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + } + else if (io->GetPixelType() == itk::ImageIOBase::RGB) + { + return MultiplexLoadDICOMByITKRGBPixel(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + } + else + { + return NULL; + } +} + +Image::Pointer +DicomSeriesReader +::MultiplexLoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) +{ + io = DcmIoType::New(); + io->SetFileName(imageBlocks.front().front().c_str()); + io->ReadImageInformation(); + + if (io->GetPixelType() == itk::ImageIOBase::SCALAR) + { + return MultiplexLoadDICOMByITK4DScalar(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + } + else if (io->GetPixelType() == itk::ImageIOBase::RGB) + { + return MultiplexLoadDICOMByITK4DRGBPixel(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); + } + else + { + return NULL; + } +} + +} // end namespace mitk diff --git a/Core/Code/IO/mitkDicomSeriesReader.h b/Core/Code/IO/mitkDicomSeriesReader.h index 05ff5edbd4..c35cd71ea5 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.h +++ b/Core/Code/IO/mitkDicomSeriesReader.h @@ -1,939 +1,967 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDicomSeriesReader_h #define mitkDicomSeriesReader_h #include "mitkDataNode.h" #include "mitkConfig.h" #include #include #include #ifdef NOMINMAX # define DEF_NOMINMAX # undef NOMINMAX #endif #include #ifdef DEF_NOMINMAX # ifndef NOMINMAX # define NOMINMAX # endif # undef DEF_NOMINMAX #endif #include #include namespace mitk { /** \brief Loading DICOM images as MITK images. - \ref DicomSeriesReader_purpose - \ref DicomSeriesReader_limitations - \ref DicomSeriesReader_usage - \ref DicomSeriesReader_sorting - \ref DicomSeriesReader_sorting1 - \ref DicomSeriesReader_sorting2 - \ref DicomSeriesReader_sorting3 - \ref DicomSeriesReader_sorting4 - \ref DicomSeriesReader_gantrytilt - \ref DicomSeriesReader_pixelspacing - \ref DicomSeriesReader_nextworkitems - \ref DicomSeriesReader_whynotinitk - \ref DicomSeriesReader_tests \section DicomSeriesReader_purpose Purpose DicomSeriesReader serves as a central class for loading DICOM images as mitk::Image. As the term "DICOM image" covers a huge variety of possible modalities and implementations, and since MITK assumes that 3D images are made up of continuous blocks of slices without any gaps or changes in orientation, the loading mechanism must implement a number of decisions and compromises. The main intention of this implementation is not efficiency but correctness of generated slice positions and pixel spacings! \section DicomSeriesReader_limitations Assumptions and limitations The class is working only with GDCM 2.0.14 (or possibly newer). This version is the default of an MITK super-build. Support for other versions or ITK's DicomIO was dropped because of the associated complexity of DicomSeriesReader. \b Assumptions - expected to work with certain SOP Classes (mostly CT Image Storage and MR Image Storage) - see ImageBlockDescriptor.GetReaderImplementationLevel() method for the details - special treatment for a certain type of Philips 3D ultrasound (recogized by tag 3001,0010 set to "Philips3D") - loader will always attempt to read multiple single slices as a single 3D image volume (i.e. mitk::Image) - slices will be grouped by basic properties such as orientation, rows, columns, spacing and grouped into as large blocks as possible - images which do NOT report a position or orientation in space (Image Position Patient, Image Orientation) will be assigned defaults - image position (0,0,0) - image orientation (1,0,0), (0,1,0) - such images will always be grouped separately since spatial grouping / sorting makes no sense for them \b Options - images that cover the same piece of space (i.e. position, orientation, and dimensions are equal) can be interpreted as time-steps of the same image, i.e. a series will be loaded as 3D+t \b Limitations - the 3D+t assumption only works if all time-steps have an equal number of slices and if all have the Acquisition Time attribute set to meaningful values \section DicomSeriesReader_usage Usage The starting point for an application is a set of DICOM files that should be loaded. For convenience, DicomSeriesReader can also parse a whole directory for DICOM files, but an application should better know exactly what to load. Loading is then done in two steps: 1. Group the files into spatial blocks by calling GetSeries(). This method will sort all passed files into meaningful blocks that could fit into an mitk::Image. Sorting for 3D+t loading is optional but default. The \b return value of this function is a list of descriptors, which describe a grouped list of files with its most basic properties: - SOP Class (CT Image Storage, Secondary Capture Image Storage, etc.) - Modality - What type of pixel spacing can be read from the provided DICOM tags - How well DicomSeriesReader is prepared to load this type of data 2. Load a sorted set of files by calling LoadDicomSeries(). This method expects go receive the sorting output of GetSeries(). The method will then invoke ITK methods configured with GDCM-IO classes to actually load the files into memory and put them into mitk::Images. Again, loading as 3D+t is optional. Example: \code // only a directory is known at this point: /home/who/dicom DicomSeriesReader::FileNamesGrouping allImageBlocks = DicomSeriesReader::GetSeries("/home/who/dicom/"); // file now divided into groups of identical image size, orientation, spacing, etc. // each of these lists should be loadable as an mitk::Image. DicomSeriesReader::StringContainer seriesToLoad = allImageBlocks[...]; // decide what to load // final step: load into DataNode (can result in 3D+t image) DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( oneBlockSorted ); Image::Pointer image = dynamic_cast( node->GetData() ); \endcode \section DicomSeriesReader_sorting Logic for sorting 2D slices from DICOM images into 3D+t blocks for mitk::Image The general sorting mechanism (implemented in GetSeries) groups and sorts a set of DICOM files, each assumed to contain a single CT/MR slice. In the following we refer to those file groups as "blocks", since this is what they are meant to become when loaded into an mitk::Image. \subsection DicomSeriesReader_sorting1 Step 1: Avoiding pure non-sense A first pass separates slices that cannot possibly be loaded together because of restrictions of mitk::Image. After this steps, each block contains only slices that match in all of the following DICOM tags: - (0020,000e) Series Instance UID - (0020,0037) Image Orientation - (0028,0030) Pixel Spacing - (0018,1164) Imager Pixel Spacing - (0018,0050) Slice Thickness - (0028,0010) Number Of Rows - (0028,0011) Number Of Columns - (0028,0008) Number Of Frames \subsection DicomSeriesReader_sorting2 Step 2: Sort slices spatially Before slices are further analyzed, they are sorted spatially. As implemented by GdcmSortFunction(), slices are sorted by 1. distance from origin (calculated using (0020,0032) Image Position Patient and (0020,0037) Image Orientation) 2. when distance is equal, (0020,0012) Aquisition Number, (0008,0032) Acquisition Time and (0018,1060) Trigger Time are used as a backup criterions (necessary for meaningful 3D+t sorting) \subsection DicomSeriesReader_sorting3 Step 3: Ensure equal z spacing Since inter-slice distance is not recorded in DICOM tags, we must ensure that blocks are made up of slices that have equal distances between neighboring slices. This is especially necessary because itk::ImageSeriesReader is later used for the actual loading, and this class expects (and does nocht verify) equal inter-slice distance (see \ref DicomSeriesReader_whatweknowaboutitk). To achieve such grouping, the inter-slice distance is calculated from the first two different slice positions of a block. Following slices are added to a block as long as they can be added by adding the calculated inter-slice distance to the last slice of the block. Slices that do not fit into the expected distance pattern, are set aside for further analysis. This grouping is done until each file has been assigned to a group. Slices that share a position in space are also sorted into separate blocks during this step. So the result of this step is a set of blocks that contain only slices with equal z spacing and uniqe slices at each position. \subsection DicomSeriesReader_sorting4 Step 4 (optional): group 3D blocks as 3D+t when possible This last step depends on an option of GetSeries(). When requested, image blocks from the previous step are merged again whenever two blocks occupy the same portion of space (i.e. same origin, number of slices and z-spacing). \section DicomSeriesReader_gantrytilt Handling of gantry tilt When CT gantry tilt is used, the gantry plane (= X-Ray source and detector ring) and the vertical plane do not align anymore. This scanner feature is used for example to reduce metal artifacs (e.g. Lee C , Evaluation of Using CT Gantry Tilt Scan on Head and Neck Cancer Patients with Dental Structure: Scans Show Less Metal Artifacts. Presented at: Radiological Society of North America 2011 Scientific Assembly and Annual Meeting; November 27- December 2, 2011 Chicago IL.). The acquired planes of such CT series do not match the expectations of a orthogonal geometry in mitk::Image: if you stack the slices, they show a small shift along the Y axis: \verbatim without tilt with tilt |||||| ////// |||||| ////// -- |||||| --------- ////// -------- table orientation |||||| ////// |||||| ////// Stacked slices: without tilt with tilt -------------- -------------- -------------- -------------- -------------- -------------- -------------- -------------- -------------- -------------- \endverbatim As such gemetries do not in conjunction with mitk::Image, DicomSeriesReader performs a correction for such series if the groupImagesWithGantryTilt or correctGantryTilt flag in GetSeries and LoadDicomSeries is set (default = on). The correction algorithms undoes two errors introduced by ITK's ImageSeriesReader: - the plane shift that is ignored by ITK's reader is recreated by applying a shearing transformation using itk::ResampleFilter. - the spacing is corrected (it is calculated by ITK's reader from the distance between two origins, which is NOT the slice distance in this special case) Both errors are introduced in itkImageSeriesReader.txx (ImageSeriesReader::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20) For the correction, we examine two consecutive slices of a series, both described as a pair (origin/orientation): - we calculate if the first origin is on a line along the normal of the second slice - if this is not the case, the geometry will not fit a normal mitk::Image/mitk::Geometry3D - we then project the second origin into the first slice's coordinate system to quantify the shift - both is done in class GantryTiltInformation with quite some comments. The geometry of image stacks with tilted geometries is illustrated below: - green: the DICOM images as described by their tags: origin as a point with the line indicating the orientation - red: the output of ITK ImageSeriesReader: wrong, larger spacing, no tilt - blue: how much a shear must correct \image tilt-correction.jpg \section DicomSeriesReader_whatweknowaboutitk The actual image loading process When calling LoadDicomSeries(), this method "mainly" uses an instance of itk::ImageSeriesReader, configured with an itk::GDCMImageIO object. Because DicomSeriesReader works around some of the behaviors of these classes, the following is a list of features that we find in the code and need to work with: - itk::ImageSeriesReader::GenerateOutputInformation() does the z-spacing handling + spacing is directly determined by comparing (euclidean distance) the origins of the first two slices of a series * this is GOOD because there is no reliable z-spacing information in DICOM images * this is bad because it does not work with gantry tilt, in which case the slice distance is SMALLER than the distance between two origins (see section on tilt) - origin and spacing are calculated by GDCMImageIO and re-used in itk::ImageSeriesReader + the origins are read from appropriate tags, nothing special about that + the spacing is read by gdcm::ImageReader, gdcm::ImageHelper::GetSpacingValue() from a tag determined by gdcm::ImageHelper::GetSpacingTagFromMediaStorage(), which basically determines ONE appropriate pixel spacing tag for each media storage type (ct image, mr image, secondary capture image, etc.) * this is fine for modalities such as CT/MR where the "Pixel Spacing" tag is mandatory, but for other modalities such as CR or Secondary Capture, the tag "Imager Pixel Spacing" is taken, which is no only optional but also has a more complicated relation with the "Pixel Spacing" tag. For this reason we check/modify the pixel spacing reported by itk::ImageSeriesReader after loading the image (see \ref DicomSeriesReader_pixelspacing) AFTER loading, DicomSeriesReader marks some of its findings as mitk::Properties to the loaded Image and DataNode: - dicomseriesreader.SOPClass : DICOM SOP Class as readable string (instead of a UID) - dicomseriesreader.ReaderImplementationLevelString : Confidence /Support level of the reader for this image as readable string - dicomseriesreader.ReaderImplementationLevel : Confidence /Support level of the reader for this image as enum value of type ReaderImplementationLevel - dicomseriesreader.PixelSpacingInterpretationString : Appropriate interpreteation of pixel spacing for this Image as readable string - dicomseriesreader.PixelSpacingInterpretation : Appropriate interpreteation of pixel spacing for this Image as enum value of type PixelSpacingInterpretation - dicomseriesreader.MultiFrameImage : bool flag to mark multi-frame images - dicomseriesreader.GantyTiltCorrected : bool flag to mark images where a gantry tilt was corrected to fit slices into an mitk::Image - dicomseriesreader.3D+t : bool flag to mark images with a time dimension (multiple 3D blocks of the same size at the same position in space) \section DicomSeriesReader_pixelspacing Handling of pixel spacing The reader implementes what is described in DICOM Part 3, chapter 10.7 (Basic Pixel Spacing Calibration Macro): Both tags - (0028,0030) Pixel Spacing and - (0018,1164) Imager Pixel Spacing are evaluated and the pixel spacing is set to the spacing within the patient when tags allow that. The result of pixel spacing interpretation can be read from a property "dicomseriesreader.PixelSpacingInterpretation", which refers to one of the enumerated values of type PixelSpacingInterpretation; \section DicomSeriesReader_supportedmodalities Limitations for specific modalities - Enhanced Computed Tomography / Magnetic Resonance Images are currently NOT supported at all, because we lack general support for multi-frame images. - Nuclear Medicine Images are not supported fully supported, only the single-frame variants are loaded properly. \section DicomSeriesReader_nextworkitems Possible enhancements This is a short list of ideas for enhancement: - Class has historically grown and should be reviewed again. There is probably too many duplicated scanning code - Multi-frame images don't mix well with the curent assumption of "one file - one slice", which is assumed by our code - It should be checked how well GDCM and ITK support these files (some load, some don't) - Specializations such as the Philips 3D code should be handled in a more generic way. The current handling of Philips 3D images is not nice at all \section DicomSeriesReader_whynotinitk Why is this not in ITK? Some of this code would probably be better located in ITK. It is just a matter of resources that this is not the case yet. Any attempts into this direction are welcome and can be supported. At least the gantry tilt correction should be a simple addition to itk::ImageSeriesReader. \section DicomSeriesReader_tests Tests regarding DICOM loading A number of tests have been implemented to check our assumptions regarding DICOM loading. Please see \ref DICOMTesting \todo refactor all the protected helper objects/methods into a separate header so we compile faster */ class MITK_CORE_EXPORT DicomSeriesReader { public: /** \brief Lists of filenames. */ typedef std::vector StringContainer; /** \brief Interface for the progress callback. */ typedef void (*UpdateCallBackMethod)(float); /** \brief Describes how well the reader is tested for a certain file type. Applications should not rely on the outcome for images which are reported ReaderImplementationLevel_Implemented or ReaderImplementationLevel_Unsupported. Errors to load images which are reported as ReaderImplementationLevel_Supported are considered bugs. For ReaderImplementationLevel_PartlySupported please check the appropriate paragraph in \ref DicomSeriesReader_supportedmodalities */ typedef enum { ReaderImplementationLevel_Supported, /// loader code and tests are established ReaderImplementationLevel_PartlySupported, /// loader code and tests are establised for specific parts of a SOP Class ReaderImplementationLevel_Implemented, /// loader code is implemented but not accompanied by tests ReaderImplementationLevel_Unsupported, /// loader code is not working with this SOP Class } ReaderImplementationLevel; /** \brief How the mitk::Image spacing should be interpreted. Compare DICOM PS 3.3 10.7 (Basic Pixel Spacing Calibration Macro). */ typedef enum { PixelSpacingInterpretation_SpacingInPatient, /// distances are mm within a patient PixelSpacingInterpretation_SpacingAtDetector, /// distances are mm at detector surface PixelSpacingInterpretation_SpacingUnknown /// NO spacing information is present, we use (1,1) as default } PixelSpacingInterpretation; /** \brief Return type of GetSeries, describes a logical group of files. Files grouped into a single 3D or 3D+t block are described by an instance of this class. Relevant descriptive properties can be used to provide the application user with meaningful choices. */ class MITK_CORE_EXPORT ImageBlockDescriptor { public: /// List of files in this group StringContainer GetFilenames() const; /// A unique ID describing this bloc (enhanced Series Instance UID). std::string GetImageBlockUID() const; /// The Series Instance UID. std::string GetSeriesInstanceUID() const; /// Series Modality (CT, MR, etc.) std::string GetModality() const; /// SOP Class UID as readable string (Computed Tomography Image Storage, Secondary Capture Image Storage, etc.) std::string GetSOPClassUIDAsString() const; /// SOP Class UID as DICOM UID std::string GetSOPClassUID() const; /// Confidence of the reader that this block can be read successfully. ReaderImplementationLevel GetReaderImplementationLevel() const; /// Whether or not the block contains a gantry tilt which will be "corrected" during loading bool HasGantryTiltCorrected() const; /// Whether or not mitk::Image spacing relates to the patient bool PixelSpacingRelatesToPatient() const; /// Whether or not mitk::Image spacing relates to the detector surface bool PixelSpacingRelatesToDetector() const; /// Whether or not mitk::Image spacing is of unknown origin bool PixelSpacingIsUnknown() const; /// How the mitk::Image spacing can meaningfully be interpreted. PixelSpacingInterpretation GetPixelSpacingType() const; /// 3D+t or not bool HasMultipleTimePoints() const; /// Multi-frame image(s) or not bool IsMultiFrameImage() const; ImageBlockDescriptor(); ~ImageBlockDescriptor(); private: friend class DicomSeriesReader; ImageBlockDescriptor(const StringContainer& files); void AddFile(const std::string& file); void AddFiles(const StringContainer& files); void SetImageBlockUID(const std::string& uid); void SetSeriesInstanceUID(const std::string& uid); void SetModality(const std::string& modality); void SetNumberOfFrames(const std::string& ); void SetSOPClassUID(const std::string& mediaStorageSOPClassUID); void SetHasGantryTiltCorrected(bool); void SetPixelSpacingInformation(const std::string& pixelSpacing, const std::string& imagerPixelSpacing); void SetHasMultipleTimePoints(bool); void GetDesiredMITKImagePixelSpacing(ScalarType& spacingX, ScalarType& spacingY) const; StringContainer m_Filenames; std::string m_ImageBlockUID; std::string m_SeriesInstanceUID; std::string m_Modality; std::string m_SOPClassUID; bool m_HasGantryTiltCorrected; std::string m_PixelSpacing; std::string m_ImagerPixelSpacing; bool m_HasMultipleTimePoints; bool m_IsMultiFrameImage; }; typedef std::map FileNamesGrouping; /** \brief Provide combination of preprocessor defines that was active during compilation. Since this class is a combination of several possible implementations, separated only by ifdef's, calling instances might want to know which flags were active at compile time. */ static std::string GetConfigurationString(); /** \brief Checks if a specific file contains DICOM data. */ static bool IsDicom(const std::string &filename); /** \brief see other GetSeries(). Find all series (and sub-series -- see details) in a particular directory. */ static FileNamesGrouping GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions = StringContainer()); /** \brief see other GetSeries(). \warning Untested, could or could not work. This differs only by having an additional restriction to a single known DICOM series. Internally, it uses the other GetSeries() method. */ static StringContainer GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions = StringContainer()); /** \brief PREFERRED version of this method - scan and sort DICOM files. Parse a list of files for images of DICOM series. For each series, an enumeration of the files contained in it is created. \return The resulting maps UID-like keys (based on Series Instance UID and slice properties) to sorted lists of file names. SeriesInstanceUID will be enhanced to be unique for each set of file names that is later loadable as a single mitk::Image. This implies that Image orientation, slice thickness, pixel spacing, rows, and columns must be the same for each file (i.e. the image slice contained in the file). If this separation logic requires that a SeriesInstanceUID must be made more specialized, it will follow the same logic as itk::GDCMSeriesFileNames to enhance the UID with more digits and dots. Optionally, more tags can be used to separate files into different logical series by setting the restrictions parameter. \warning Adding restrictions is not yet implemented! */ static FileNamesGrouping GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer &restrictions = StringContainer()); /** \brief See other GetSeries(). Use GetSeries(const StringContainer& files, bool sortTo3DPlust, const StringContainer &restrictions) instead. */ static FileNamesGrouping GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions = StringContainer()); /** Loads a DICOM series composed by the file names enumerated in the file names container. If a callback method is supplied, it will be called after every progress update with a progress value in [0,1]. \param filenames The filenames to load. \param sort Whether files should be sorted spatially (true) or not (false - maybe useful if presorted) \param load4D Whether to load the files as 3D+t (if possible) */ static DataNode::Pointer LoadDicomSeries(const StringContainer &filenames, bool sort = true, bool load4D = true, bool correctGantryTilt = true, UpdateCallBackMethod callback = 0, Image::Pointer preLoadedImageBlock = 0); /** \brief See LoadDicomSeries! Just a slightly different interface. If \p preLoadedImageBlock is provided, the reader will only "fake" loading and create appropriate mitk::Properties. */ static bool LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort = true, bool load4D = true, bool correctGantryTilt = true, UpdateCallBackMethod callback = 0, Image::Pointer preLoadedImageBlock = 0); protected: /** \brief Return type of DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption. Class contains the grouping result of method DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption, which takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction. The result contains of two blocks: a first one is the grouping result, all of those images can be loaded into one image block because they have an equal origin-to-origin distance without any gaps in-between. */ class SliceGroupingAnalysisResult { public: SliceGroupingAnalysisResult(); /** \brief Grouping result, all same origin-to-origin distance w/o gaps. */ StringContainer GetBlockFilenames(); /** \brief Remaining files, which could not be grouped. */ StringContainer GetUnsortedFilenames(); /** \brief Wheter or not the grouped result contain a gantry tilt. */ bool ContainsGantryTilt(); /** \brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only. */ void AddFileToSortedBlock(const std::string& filename); /** \brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only. */ void AddFileToUnsortedBlock(const std::string& filename); void AddFilesToUnsortedBlock(const StringContainer& filenames); /** \brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only. \todo Could make sense to enhance this with an instance of GantryTiltInformation to store the whole result! */ void FlagGantryTilt(); /** \brief Only meaningful for use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption. */ void UndoPrematureGrouping(); protected: StringContainer m_GroupedFiles; StringContainer m_UnsortedFiles; bool m_GantryTilt; }; /** \brief Gantry tilt analysis result. Takes geometry information for two slices of a DICOM series and calculates whether these fit into an orthogonal block or not. If NOT, they can either be the result of an acquisition with gantry tilt OR completly broken by some shearing transformation. Most calculations are done in the constructor, results can then be read via the remaining methods. */ class GantryTiltInformation { public: // two types to avoid any rounding errors typedef itk::Point Point3Dd; typedef itk::Vector Vector3Dd; /** \brief Just so we can create empty instances for assigning results later. */ GantryTiltInformation(); /** \brief THE constructor, which does all the calculations. Determining the amount of tilt is done by checking the distances of origin1 from planes through origin2. Two planes are considered: - normal vector along normal of slices (right x up): gives the slice distance - normal vector along orientation vector "up": gives the shift parallel to the plane orientation The tilt angle can then be calculated from these distances \param origin1 origin of the first slice \param origin2 origin of the second slice \param right right/up describe the orientatation of borth slices \param up right/up describe the orientatation of borth slices \param numberOfSlicesApart how many slices are the given origins apart (1 for neighboring slices) */ GantryTiltInformation( const Point3D& origin1, const Point3D& origin2, const Vector3D& right, const Vector3D& up, unsigned int numberOfSlicesApart); /** \brief Whether the slices were sheared. True if any of the shifts along right or up vector are non-zero. */ bool IsSheared() const; /** \brief Whether the shearing is a gantry tilt or more complicated. Gantry tilt will only produce shifts in ONE orientation, not in both. Since the correction code currently only coveres one tilt direction AND we don't know of medical images with two tilt directions, the loading code wants to check if our assumptions are true. */ bool IsRegularGantryTilt() const; /** \brief The offset distance in Y direction for each slice in mm (describes the tilt result). */ double GetMatrixCoefficientForCorrectionInWorldCoordinates() const; /** \brief The z / inter-slice spacing. Needed to correct ImageSeriesReader's result. */ double GetRealZSpacing() const; /** \brief The shift between first and last slice in mm. Needed to resize an orthogonal image volume. */ double GetTiltCorrectedAdditionalSize() const; /** \brief Calculated tilt angle in degrees. */ double GetTiltAngleInDegrees() const; protected: /** \brief Projection of point p onto line through lineOrigin in direction of lineDirection. */ Point3D projectPointOnLine( Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection ); double m_ShiftUp; double m_ShiftRight; double m_ShiftNormal; double m_ITKAssumedSliceSpacing; unsigned int m_NumberOfSlicesApart; }; /** \brief for internal sorting. */ typedef std::pair TwoStringContainers; /** \brief Maps DICOM tags to MITK properties. */ typedef std::map TagToPropertyMapType; /** \brief Ensure an equal z-spacing for a group of files. Takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction. Internally used by GetSeries. Returns two lists: the first one contins slices of equal inter-slice spacing. The second list contains remaining files, which need to be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption again. Relevant code that is matched here is in itkImageSeriesReader.txx (ImageSeriesReader::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20) */ static SliceGroupingAnalysisResult AnalyzeFileForITKImageSeriesReaderSpacingAssumption(const StringContainer& files, bool groupsOfSimilarImages, const gdcm::Scanner::MappingType& tagValueMappings_); /** \brief Safely convert const char* to std::string. */ static std::string ConstCharStarToString(const char* s); /** \brief Safely convert a string into pixel spacing x and y. */ static bool DICOMStringToSpacing(const std::string& s, ScalarType& spacingX, ScalarType& spacingY); /** \brief Convert DICOM string describing a point to Point3D. DICOM tags like ImagePositionPatient contain a position as float numbers separated by backslashes: \verbatim 42.7131\13.77\0.7 \endverbatim */ static Point3D DICOMStringToPoint3D(const std::string& s, bool& successful); /** \brief Convert DICOM string describing a point two Vector3D. DICOM tags like ImageOrientationPatient contain two vectors as float numbers separated by backslashes: \verbatim 42.7131\13.77\0.7\137.76\0.3 \endverbatim */ static void DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful); template static typename ImageType::Pointer // TODO this is NOT inplace! InPlaceFixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo ); /** \brief Sort a set of file names in an order that is meaningful for loading them into an mitk::Image. \warning This method assumes that input files are similar in basic properties such as slice thicknes, image orientation, pixel spacing, rows, columns. It should always be ok to put the result of a call to GetSeries(..) into this method. Sorting order is determined by 1. image position along its normal (distance from world origin) 2. acquisition time If P denotes a position and T denotes a time step, this method will order slices from three timesteps like this: \verbatim P1T1 P1T2 P1T3 P2T1 P2T2 P2T3 P3T1 P3T2 P3T3 \endverbatim */ static StringContainer SortSeriesSlices(const StringContainer &unsortedFilenames); public: /** \brief Checks if a specific file is a Philips3D ultrasound DICOM file. */ static bool IsPhilips3DDicom(const std::string &filename); static std::string ReaderImplementationLevelToString( const ReaderImplementationLevel& enumValue ); static std::string PixelSpacingInterpretationToString( const PixelSpacingInterpretation& enumValue ); protected: /** \brief Read a Philips3D ultrasound DICOM file and put into an mitk::Image. */ static bool ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image); /** \brief Construct a UID that takes into account sorting criteria from GetSeries(). */ static std::string CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ); /** \brief Helper for CreateMoreUniqueSeriesIdentifier */ static std::string CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ); /** \brief Helper for CreateMoreUniqueSeriesIdentifier */ static std::string IDifyTagValue(const std::string& value); typedef itk::GDCMImageIO DcmIoType; /** \brief Progress callback for DicomSeriesReader. */ class CallbackCommand : public itk::Command { public: CallbackCommand(UpdateCallBackMethod callback) : m_Callback(callback) { } void Execute(const itk::Object *caller, const itk::EventObject&) { (*this->m_Callback)(static_cast(caller)->GetProgress()); } void Execute(itk::Object *caller, const itk::EventObject&) { (*this->m_Callback)(static_cast(caller)->GetProgress()); } protected: UpdateCallBackMethod m_Callback; }; static void FixSpacingInformation( Image* image, const ImageBlockDescriptor& imageBlockDescriptor ); /** \brief Scan for slice image information */ static void ScanForSliceInformation( const StringContainer &filenames, gdcm::Scanner& scanner ); /** \brief Performs actual loading of a series and creates an image having the specified pixel type. */ - template static void LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock); /** \brief Feed files into itk::ImageSeriesReader and retrieve a 3D MITK image. \param command can be used for progress reporting */ template static Image::Pointer - LoadDICOMByITK( const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command = NULL, Image::Pointer preLoadedImageBlock = NULL); + LoadDICOMByITK( const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock); + + static + Image::Pointer MultiplexLoadDICOMByITK(const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock); + + static + Image::Pointer MultiplexLoadDICOMByITKScalar(const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock); + + static + Image::Pointer MultiplexLoadDICOMByITKRGBPixel(const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock); + + + template + static + Image::Pointer + LoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock); + + static + Image::Pointer + MultiplexLoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock); + + static + Image::Pointer + MultiplexLoadDICOMByITK4DScalar( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock); + + static + Image::Pointer + MultiplexLoadDICOMByITK4DRGBPixel( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock); + + /** \brief Sort files into time step blocks of a 3D+t image. Called by LoadDicom. Expects to be fed a single list of filenames that have been sorted by GetSeries previously (one map entry). This method will check how many timestep can be filled with given files. Assumption is that the number of time steps is determined by how often the first position in space repeats. I.e. if the first three files in the input parameter all describe the same location in space, we'll construct three lists of files. and sort the remaining files into them. \todo We can probably remove this method if we somehow transfer 3D+t information from GetSeries to LoadDicomSeries. */ static std::list SortIntoBlocksFor3DplusT( const StringContainer& presortedFilenames, const gdcm::Scanner::MappingType& tagValueMappings_, bool sort, bool& canLoadAs4D); /** \brief Defines spatial sorting for sorting by GDCM 2. Sorts by image position along image normal (distance from world origin). In cases of conflict, acquisition time is used as a secondary sort criterium. */ static bool GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2); /** \brief Copy information about files and DICOM tags from ITK's MetaDataDictionary and from the list of input files to the PropertyList of mitk::Image. \todo Tag copy must follow; image level will cause some additional files parsing, probably. */ static void CopyMetaDataToImageProperties( StringContainer filenames, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image); static void CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image); /** \brief Map between DICOM tags and MITK properties. Uses as a positive list for copying specified DICOM tags (from ITK's ImageIO) to MITK properties. ITK provides MetaDataDictionary entries of form "gggg|eeee" (g = group, e = element), e.g. "0028,0109" (Largest Pixel in Series), which we want to sort as dicom.series.largest_pixel_in_series". */ static const TagToPropertyMapType& GetDICOMTagsToMITKPropertyMap(); }; } #endif /* mitkDicomSeriesReader_h */ diff --git a/Core/Code/IO/mitkDicomSeriesReader.txx b/Core/Code/IO/mitkDicomSeriesReader.txx index e28fa06880..ec4058551c 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.txx +++ b/Core/Code/IO/mitkDicomSeriesReader.txx @@ -1,568 +1,296 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDICOMSERIESREADER_TXX_ #define MITKDICOMSERIESREADER_TXX_ #include #include #include #include #include #include #include #include namespace mitk { template -void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) +Image::Pointer DicomSeriesReader::LoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock ) { - const char* previousCLocale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "C"); - std::locale previousCppLocale( std::cin.getloc() ); - std::locale l( "C" ); - std::cin.imbue(l); - - ImageBlockDescriptor imageBlockDescriptor; - - const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) - const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation - const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID - const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID - const gdcm::Tag tagModality(0x0008, 0x0060); // modality - const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing - const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing - const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames - - try - { - Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock; - CallbackCommand *command = callback ? new CallbackCommand(callback) : 0; - bool initialize_node = false; - - /* special case for Philips 3D+t ultrasound images */ - if ( DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str()) ) - { - // TODO what about imageBlockDescriptor? - // TODO what about preLoadedImageBlock? - ReadPhilips3DDicom(filenames.front().c_str(), image); - initialize_node = true; - } - else - { - /* default case: assume "normal" image blocks, possibly 3D+t */ - bool canLoadAs4D(true); - gdcm::Scanner scanner; - ScanForSliceInformation(filenames, scanner); + mitk::Image::Pointer image = mitk::Image::New(); - // need non-const access for map - gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings()); + // It is 3D+t! Read it and store into mitk image + typedef itk::Image ImageType; + typedef itk::ImageSeriesReader ReaderType; - std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, tagValueMappings, sort, canLoadAs4D ); - unsigned int volume_count = imageBlocks.size(); + typename ReaderType::Pointer reader = ReaderType::New(); - imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSeriesInstanceUID ) ) ); - imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSOPClassUID ) ) ); - imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagModality ) ) ); - imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagNumberOfFrames ) ) ); - imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagPixelSpacing ) ), - ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagImagerPixelSpacing ) ) ); + reader->SetImageIO(io); + reader->ReverseOrderOff(); - GantryTiltInformation tiltInfo; + if (command) + { + reader->AddObserver(itk::ProgressEvent(), command); + } - // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice possible - if ( !imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt) - { - // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents - // check first and last position slice from tags, make some calculations to detect tilt + unsigned int act_volume = 1u; - std::string firstFilename(imageBlocks.front().front()); - // calculate from first and last slice to minimize rounding errors - std::string secondFilename(imageBlocks.front().back()); + if (preLoadedImageBlock.IsNull()) + { + reader->SetFileNames(imageBlocks.front()); - std::string imagePosition1( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImagePositionPatient ] ) ); - std::string imageOrientation( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImageOrientation ] ) ); - std::string imagePosition2( ConstCharStarToString( tagValueMappings[secondFilename.c_str() ][ tagImagePositionPatient ] ) ); + reader->Update(); - bool ignoredConversionError(-42); // hard to get here, no graceful way to react - Point3D origin1( DICOMStringToPoint3D( imagePosition1, ignoredConversionError ) ); - Point3D origin2( DICOMStringToPoint3D( imagePosition2, ignoredConversionError ) ); + typename ImageType::Pointer readVolume = reader->GetOutput(); + // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position + if (correctTilt) + { + readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); + } - Vector3D right; right.Fill(0.0); - Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point - DICOMStringToOrientationVectors( imageOrientation, right, up, ignoredConversionError ); + unsigned int volume_count = imageBlocks.size(); + image->InitializeByItk( readVolume.GetPointer(), 1, volume_count); + image->SetImportVolume( readVolume->GetBufferPointer(), 0u); - tiltInfo = GantryTiltInformation ( origin1, origin2, right, up, filenames.size()-1 ); - correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt(); - } - else - { - correctTilt = false; // we CANNOT do that - } + FixSpacingInformation( image, imageBlockDescriptor ); + } + else + { + StringContainer fakeList; + fakeList.push_back( imageBlocks.front().front() ); + reader->SetFileNames(fakeList); // only ONE first filename to get MetaDataDictionary - imageBlockDescriptor.SetHasGantryTiltCorrected( correctTilt ); + image = preLoadedImageBlock; + } - if (volume_count == 1 || !canLoadAs4D || !load4D) - { + gdcm::Scanner scanner; + ScanForSliceInformation(imageBlockDescriptor.GetFilenames(), scanner); + CopyMetaDataToImageProperties( imageBlocks, scanner.GetMappings(), io, imageBlockDescriptor, image); - DcmIoType::Pointer io; - image = LoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock ); // load first 3D block + MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", " + << image->GetDimension(1) << ", " + << image->GetDimension(2) << ", " + << image->GetDimension(3) << "]"; - imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded - imageBlockDescriptor.SetHasMultipleTimePoints( false ); + MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", " + << image->GetGeometry()->GetSpacing()[1] << ", " + << image->GetGeometry()->GetSpacing()[2] << "]"; - FixSpacingInformation( image, imageBlockDescriptor ); - CopyMetaDataToImageProperties( imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image); + if ( preLoadedImageBlock.IsNull() ) + { + for (std::list::iterator df_it = ++imageBlocks.begin(); df_it != imageBlocks.end(); ++df_it) + { + reader->SetFileNames(*df_it); + reader->Update(); + typename ImageType::Pointer readVolume = reader->GetOutput(); - initialize_node = true; - } - else if (volume_count > 1) + if (correctTilt) { - imageBlockDescriptor.AddFiles(filenames); // all is loaded - imageBlockDescriptor.SetHasMultipleTimePoints( true ); - // It is 3D+t! Read it and store into mitk image - typedef itk::Image ImageType; - typedef itk::ImageSeriesReader ReaderType; - - DcmIoType::Pointer io = DcmIoType::New(); - typename ReaderType::Pointer reader = ReaderType::New(); - - reader->SetImageIO(io); - reader->ReverseOrderOff(); - - if (command) - { - reader->AddObserver(itk::ProgressEvent(), command); - } - - unsigned int act_volume = 1u; - - if (preLoadedImageBlock.IsNull()) - { - reader->SetFileNames(imageBlocks.front()); - - reader->Update(); - - typename ImageType::Pointer readVolume = reader->GetOutput(); - // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position - if (correctTilt) - { - readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); - } - - image->InitializeByItk( readVolume.GetPointer(), 1, volume_count); - image->SetImportVolume( readVolume->GetBufferPointer(), 0u); - - FixSpacingInformation( image, imageBlockDescriptor ); - } - else - { - StringContainer fakeList; - fakeList.push_back( imageBlocks.front().front() ); - reader->SetFileNames(fakeList); // only ONE first filename to get MetaDataDictionary - } - - gdcm::Scanner scanner; - ScanForSliceInformation(filenames, scanner); - CopyMetaDataToImageProperties( imageBlocks, scanner.GetMappings(), io, imageBlockDescriptor, image); - - MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", " - << image->GetDimension(1) << ", " - << image->GetDimension(2) << ", " - << image->GetDimension(3) << "]"; - - MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", " - << image->GetGeometry()->GetSpacing()[1] << ", " - << image->GetGeometry()->GetSpacing()[2] << "]"; - - if ( preLoadedImageBlock.IsNull() ) - { - for (std::list::iterator df_it = ++imageBlocks.begin(); df_it != imageBlocks.end(); ++df_it) - { - reader->SetFileNames(*df_it); - reader->Update(); - typename ImageType::Pointer readVolume = reader->GetOutput(); - - if (correctTilt) - { - readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); - } - - image->SetImportVolume(readVolume->GetBufferPointer(), act_volume++); - } - } - - initialize_node = true; + readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); } + image->SetImportVolume(readVolume->GetBufferPointer(), act_volume++); } - - if (initialize_node) - { - // forward some image properties to node - node.GetPropertyList()->ConcatenatePropertyList( image->GetPropertyList(), true ); - - node.SetData( image ); - setlocale(LC_NUMERIC, previousCLocale); - std::cin.imbue(previousCppLocale); - } - - MITK_DEBUG << "--------------------------------------------------------------------------------"; - MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):"; - MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; - MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No"); - MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); - MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0]; - MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No"); - MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No"); - MITK_DEBUG << "--------------------------------------------------------------------------------"; } - catch (std::exception& e) - { - // reset locale then throw up - setlocale(LC_NUMERIC, previousCLocale); - std::cin.imbue(previousCppLocale); - - MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom"; - throw e; - } + return image; } template Image::Pointer DicomSeriesReader::LoadDICOMByITK( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock ) { /******** Normal Case, 3D (also for GDCM < 2 usable) ***************/ mitk::Image::Pointer image = mitk::Image::New(); typedef itk::Image ImageType; typedef itk::ImageSeriesReader ReaderType; io = DcmIoType::New(); typename ReaderType::Pointer reader = ReaderType::New(); reader->SetImageIO(io); reader->ReverseOrderOff(); if (command) { reader->AddObserver(itk::ProgressEvent(), command); } if (preLoadedImageBlock.IsNull()) { reader->SetFileNames(filenames); reader->Update(); typename ImageType::Pointer readVolume = reader->GetOutput(); // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position if (correctTilt) { readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); } image->InitializeByItk(readVolume.GetPointer()); image->SetImportVolume(readVolume->GetBufferPointer()); } else { image = preLoadedImageBlock; StringContainer fakeList; fakeList.push_back( filenames.front() ); reader->SetFileNames( fakeList ); // we always need to load at least one file to get the MetaDataDictionary reader->Update(); } MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", " << image->GetDimension(1) << ", " << image->GetDimension(2) << "]"; MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", " << image->GetGeometry()->GetSpacing()[1] << ", " << image->GetGeometry()->GetSpacing()[2] << "]"; return image; } -void -DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner& scanner) -{ - const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) - scanner.AddTag(tagImagePositionPatient); - - const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID - scanner.AddTag(tagSeriesInstanceUID); - - const gdcm::Tag tagImageOrientation(0x0020,0x0037); //Image orientation - scanner.AddTag(tagImageOrientation); - - const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location - scanner.AddTag( tagSliceLocation ); - - const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number - scanner.AddTag( tagInstanceNumber ); - - const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number - scanner.AddTag( tagSOPInstanceNumber ); - - const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing - scanner.AddTag( tagPixelSpacing ); - - const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing - scanner.AddTag( tagImagerPixelSpacing ); - - const gdcm::Tag tagModality(0x0008, 0x0060); // Modality - scanner.AddTag( tagModality ); - - const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID - scanner.AddTag( tagSOPClassUID ); - - const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames - scanner.AddTag( tagNumberOfFrames ); - - scanner.Scan(filenames); // make available image information for each file -} - -std::list -DicomSeriesReader::SortIntoBlocksFor3DplusT( - const StringContainer& presortedFilenames, - const gdcm::Scanner::MappingType& tagValueMappings, - bool /*sort*/, - bool& canLoadAs4D ) -{ - std::list imageBlocks; - - // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022) - StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames); - - std::string firstPosition; - unsigned int numberOfBlocks(0); // number of 3D image blocks - - static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) - - // loop files to determine number of image blocks - for (StringContainer::const_iterator fileIter = sorted_filenames.begin(); - fileIter != sorted_filenames.end(); - ++fileIter) - { - gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find( fileIter->c_str() )->second; - - if(tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end()) - { - // we expect to get images w/ missing position information ONLY as separated blocks. - assert( presortedFilenames.size() == 1 ); - numberOfBlocks = 1; - break; - } - - std::string position = tagToValueMap.find(tagImagePositionPatient)->second; - MITK_DEBUG << " " << *fileIter << " at " << position; - if (firstPosition.empty()) - { - firstPosition = position; - } - - if ( position == firstPosition ) - { - ++numberOfBlocks; - } - else - { - break; // enough information to know the number of image blocks - } - } - - MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps"; - - if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files - - - // loop files to sort them into image blocks - unsigned int numberOfExpectedSlices(0); - for (unsigned int block = 0; block < numberOfBlocks; ++block) - { - StringContainer filesOfCurrentBlock; - - for ( StringContainer::const_iterator fileIter = sorted_filenames.begin() + block; - fileIter != sorted_filenames.end(); - //fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts - ) - { - filesOfCurrentBlock.push_back( *fileIter ); - for (unsigned int b = 0; b < numberOfBlocks; ++b) - { - if (fileIter != sorted_filenames.end()) - ++fileIter; - } - } - - imageBlocks.push_back(filesOfCurrentBlock); - - if (block == 0) - { - numberOfExpectedSlices = filesOfCurrentBlock.size(); - } - else - { - if (filesOfCurrentBlock.size() != numberOfExpectedSlices) - { - MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks - << " image blocks of " - << numberOfExpectedSlices - << " images each. Block " - << block - << " got " - << filesOfCurrentBlock.size() - << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much as possible) - canLoadAs4D = false; - } - } - } - - return imageBlocks; -} - template typename ImageType::Pointer DicomSeriesReader::InPlaceFixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo ) { typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetInput( input ); /* Transform for a point is - transform from actual position to index coordinates - apply a shear that undoes the gantry tilt - transform back into world coordinates Anybody who does this in a simpler way: don't forget to write up how and why your solution works */ typedef itk::ScalableAffineTransform< double, ImageType::ImageDimension > TransformType; typename TransformType::Pointer transformShear = TransformType::New(); /** - apply a shear and spacing correction to the image block that corrects the ITK reader's error - ITK ignores the shear and loads slices into an orthogonal volume - ITK calculates the spacing from the origin distance, which is more than the actual spacing with gantry tilt images - to undo the effect - we have calculated some information in tiltInfo: - the shift in Y direction that is added with each additional slice is the most important information - the Y-shift is calculated in mm world coordinates - we apply a shearing transformation to the ITK-read image volume - to do this locally, - we transform the image volume back to origin and "normal" orientation by applying the inverse of its transform (this brings us into the image's "index coordinate" system) - we apply a shear with the Y-shift factor put into a unit transform at row 1, col 2 - we transform the image volume back to its actual position (from index to world coordinates) - we lastly apply modify the image spacing in z direction by replacing this number with the correctly calulcated inter-slice distance */ ScalarType factor = tiltInfo.GetMatrixCoefficientForCorrectionInWorldCoordinates() / input->GetSpacing()[1]; // row 1, column 2 corrects shear in parallel to Y axis, proportional to distance in Z direction transformShear->Shear( 1, 2, factor ); typename TransformType::Pointer imageIndexToWorld = TransformType::New(); imageIndexToWorld->SetOffset( input->GetOrigin().GetVectorFromOrigin() ); typename TransformType::MatrixType indexToWorldMatrix; indexToWorldMatrix = input->GetDirection(); typename ImageType::DirectionType scale; for ( unsigned int i = 0; i < ImageType::ImageDimension; i++ ) { scale[i][i] = input->GetSpacing()[i]; } indexToWorldMatrix *= scale; imageIndexToWorld->SetMatrix( indexToWorldMatrix ); typename TransformType::Pointer imageWorldToIndex = TransformType::New(); imageIndexToWorld->GetInverse( imageWorldToIndex ); typename TransformType::Pointer gantryTiltCorrection = TransformType::New(); gantryTiltCorrection->Compose( imageWorldToIndex ); gantryTiltCorrection->Compose( transformShear ); gantryTiltCorrection->Compose( imageIndexToWorld ); resampler->SetTransform( gantryTiltCorrection ); typedef itk::LinearInterpolateImageFunction< ImageType, double > InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); resampler->SetInterpolator( interpolator ); /* This would be the right place to invent a meaningful value for positions outside of the image. For CT, HU -1000 might be meaningful, but a general solution seems not possible. Even for CT, -1000 would only look natural for many not all images. */ // TODO use (0028,0120) Pixel Padding Value if present resampler->SetDefaultPixelValue( itk::NumericTraits< typename ImageType::PixelType >::min() ); // adjust size in Y direction! (maybe just transform the outer last pixel to see how much space we would need resampler->SetOutputParametersFromImage( input ); // we basically need the same image again, just sheared // if tilt positive, then we need additional pixels BELOW origin, otherwise we need pixels behind the end of the block // in any case we need more size to accomodate shifted slices typename ImageType::SizeType largerSize = resampler->GetSize(); // now the resampler already holds the input image's size. largerSize[1] += static_cast(tiltInfo.GetTiltCorrectedAdditionalSize() / input->GetSpacing()[1]+ 2.0); resampler->SetSize( largerSize ); // in SOME cases this additional size is below/behind origin if ( tiltInfo.GetMatrixCoefficientForCorrectionInWorldCoordinates() > 0.0 ) { typename ImageType::DirectionType imageDirection = input->GetDirection(); Vector3D yDirection; yDirection[0] = imageDirection[0][1]; yDirection[1] = imageDirection[1][1]; yDirection[2] = imageDirection[2][1]; yDirection.Normalize(); typename ImageType::PointType shiftedOrigin; shiftedOrigin = input->GetOrigin(); // add some pixels to make everything fit shiftedOrigin[0] -= yDirection[0] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]); shiftedOrigin[1] -= yDirection[1] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]); shiftedOrigin[2] -= yDirection[2] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]); resampler->SetOutputOrigin( shiftedOrigin ); } resampler->Update(); typename ImageType::Pointer result = resampler->GetOutput(); // ImageSeriesReader calculates z spacing as the distance between the first two origins. // This is not correct in case of gantry tilt, so we set our calculated spacing. typename ImageType::SpacingType correctedSpacing = result->GetSpacing(); correctedSpacing[2] = tiltInfo.GetRealZSpacing(); result->SetSpacing( correctedSpacing ); return result; } - } #endif diff --git a/Core/Code/Rendering/mitkPointSetVtkMapper3D.cpp b/Core/Code/Rendering/mitkPointSetVtkMapper3D.cpp index 0f0931456f..660387006a 100644 --- a/Core/Code/Rendering/mitkPointSetVtkMapper3D.cpp +++ b/Core/Code/Rendering/mitkPointSetVtkMapper3D.cpp @@ -1,633 +1,642 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetVtkMapper3D.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkColorProperty.h" #include "mitkVtkPropRenderer.h" #include "mitkPointSet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const mitk::PointSet* mitk::PointSetVtkMapper3D::GetInput() { return static_cast ( GetDataNode()->GetData() ); } mitk::PointSetVtkMapper3D::PointSetVtkMapper3D() : m_vtkSelectedPointList(NULL), m_vtkUnselectedPointList(NULL), m_VtkSelectedPolyDataMapper(NULL), m_VtkUnselectedPolyDataMapper(NULL), m_vtkTextList(NULL), m_NumberOfSelectedAdded(0), m_NumberOfUnselectedAdded(0), m_PointSize(1.0), m_ContourRadius(0.5) { //propassembly m_PointsAssembly = vtkSmartPointer::New(); //creating actors to be able to set transform m_SelectedActor = vtkSmartPointer::New(); m_UnselectedActor = vtkSmartPointer::New(); m_ContourActor = vtkSmartPointer::New(); } mitk::PointSetVtkMapper3D::~PointSetVtkMapper3D() { } void mitk::PointSetVtkMapper3D::ReleaseGraphicsResources(vtkWindow *renWin) { m_PointsAssembly->ReleaseGraphicsResources(renWin); m_SelectedActor->ReleaseGraphicsResources(renWin); m_UnselectedActor->ReleaseGraphicsResources(renWin); m_ContourActor->ReleaseGraphicsResources(renWin); } +void mitk::PointSetVtkMapper3D::ReleaseGraphicsResources(mitk::BaseRenderer* renderer) +{ + m_PointsAssembly->ReleaseGraphicsResources(renderer->GetRenderWindow()); + + m_SelectedActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); + m_UnselectedActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); + m_ContourActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); + +} void mitk::PointSetVtkMapper3D::CreateVTKRenderObjects() { m_vtkSelectedPointList = vtkSmartPointer::New(); m_vtkUnselectedPointList = vtkSmartPointer::New(); m_PointsAssembly->VisibilityOn(); if(m_PointsAssembly->GetParts()->IsItemPresent(m_SelectedActor)) m_PointsAssembly->RemovePart(m_SelectedActor); if(m_PointsAssembly->GetParts()->IsItemPresent(m_UnselectedActor)) m_PointsAssembly->RemovePart(m_UnselectedActor); if(m_PointsAssembly->GetParts()->IsItemPresent(m_ContourActor)) m_PointsAssembly->RemovePart(m_ContourActor); // exceptional displaying for PositionTracker -> MouseOrientationTool int mapperID; bool isInputDevice=false; if( this->GetDataNode()->GetBoolProperty("inputdevice",isInputDevice) && isInputDevice ) { if( this->GetDataNode()->GetIntProperty("BaseRendererMapperID",mapperID) && mapperID == 2) return; //The event for the PositionTracker came from the 3d widget and not needs to be displayed } // get and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); /* only update the input data, if the property tells us to */ bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet( timestep ); if ( itkPointSet.GetPointer() == NULL) { m_PointsAssembly->VisibilityOff(); return; } mitk::PointSet::PointsContainer::Iterator pointsIter; mitk::PointSet::PointDataContainer::Iterator pointDataIter; int j; m_NumberOfSelectedAdded = 0; m_NumberOfUnselectedAdded = 0; //create contour bool makeContour = false; this->GetDataNode()->GetBoolProperty("show contour", makeContour); if (makeContour) { this->CreateContour(); } //now fill selected and unselected pointList //get size of Points in Property m_PointSize = 2; mitk::FloatProperty::Pointer pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); if ( pointSizeProp.IsNotNull() ) m_PointSize = pointSizeProp->GetValue(); //get the property for creating a label onto every point only once bool showLabel = true; this->GetDataNode()->GetBoolProperty("show label", showLabel); const char * pointLabel=NULL; if(showLabel) { if(dynamic_cast(this->GetDataNode()->GetPropertyList()->GetProperty("label")) != NULL) pointLabel =dynamic_cast(this->GetDataNode()->GetPropertyList()->GetProperty("label"))->GetValue(); else showLabel = false; } //check if the list for the PointDataContainer is the same size as the PointsContainer. Is not, then the points were inserted manually and can not be visualized according to the PointData (selected/unselected) bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); //now add an object for each point in data pointDataIter = itkPointSet->GetPointData()->Begin(); for (j=0, pointsIter=itkPointSet->GetPoints()->Begin(); pointsIter!=itkPointSet->GetPoints()->End(); pointsIter++, j++) { //check for the pointtype in data and decide which geom-object to take and then add to the selected or unselected list int pointType; if(itkPointSet->GetPointData()->size() == 0 || pointDataBroken) pointType = mitk::PTUNDEFINED; else pointType = pointDataIter.Value().pointSpec; vtkSmartPointer source; switch (pointType) { case mitk::PTUNDEFINED: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize); itk::Point point1 = pointsIter->Value(); sphere->SetCenter(point1[0],point1[1],point1[2]); //sphere->SetCenter(pointsIter.Value()[0],pointsIter.Value()[1],pointsIter.Value()[2]); //MouseOrientation Tool (PositionTracker) if(isInputDevice) { sphere->SetThetaResolution(10); sphere->SetPhiResolution(10); } else { sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); } source = sphere; } break; case mitk::PTSTART: { vtkSmartPointer cube = vtkSmartPointer::New(); cube->SetXLength(m_PointSize/2); cube->SetYLength(m_PointSize/2); cube->SetZLength(m_PointSize/2); itk::Point point1 = pointsIter->Value(); cube->SetCenter(point1[0],point1[1],point1[2]); source = cube; } break; case mitk::PTCORNER: { vtkSmartPointer cone = vtkSmartPointer::New(); cone->SetRadius(m_PointSize); itk::Point point1 = pointsIter->Value(); cone->SetCenter(point1[0],point1[1],point1[2]); cone->SetResolution(20); source = cone; } break; case mitk::PTEDGE: { vtkSmartPointer cylinder = vtkSmartPointer::New(); cylinder->SetRadius(m_PointSize); itk::Point point1 = pointsIter->Value(); cylinder->SetCenter(point1[0],point1[1],point1[2]); cylinder->SetResolution(20); source = cylinder; } break; case mitk::PTEND: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize); //itk::Point point1 = pointsIter->Value(); sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); source = sphere; } break; default: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize); itk::Point point1 = pointsIter->Value(); sphere->SetCenter(point1[0],point1[1],point1[2]); sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); source = sphere; } break; } if (!pointDataBroken) { if (pointDataIter.Value().selected) { m_vtkSelectedPointList->AddInput(source->GetOutput()); ++m_NumberOfSelectedAdded; } else { m_vtkUnselectedPointList->AddInput(source->GetOutput()); ++m_NumberOfUnselectedAdded; } } else { m_vtkUnselectedPointList->AddInput(source->GetOutput()); ++m_NumberOfUnselectedAdded; } if (showLabel) { char buffer[20]; std::string l = pointLabel; if ( input->GetSize()>1 ) { sprintf(buffer,"%d",j+1); l.append(buffer); } // Define the text for the label vtkSmartPointer label = vtkSmartPointer::New(); label->SetText(l.c_str()); //# Set up a transform to move the label to a new position. vtkSmartPointer aLabelTransform = vtkSmartPointer::New(); aLabelTransform->Identity(); itk::Point point1 = pointsIter->Value(); aLabelTransform->Translate(point1[0]+2,point1[1]+2,point1[2]); aLabelTransform->Scale(5.7,5.7,5.7); //# Move the label to a new position. vtkSmartPointer labelTransform = vtkSmartPointer::New(); labelTransform->SetTransform(aLabelTransform); labelTransform->SetInput(label->GetOutput()); //add it to the wright PointList if (pointType) { m_vtkSelectedPointList->AddInput(labelTransform->GetOutput()); ++m_NumberOfSelectedAdded; } else { m_vtkUnselectedPointList->AddInput(labelTransform->GetOutput()); ++m_NumberOfUnselectedAdded; } } if(pointDataIter != itkPointSet->GetPointData()->End()) pointDataIter++; } // end FOR //now according to number of elements added to selected or unselected, build up the rendering pipeline if (m_NumberOfSelectedAdded > 0) { m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper->SetInput(m_vtkSelectedPointList->GetOutput()); //create a new instance of the actor m_SelectedActor = vtkSmartPointer::New(); m_SelectedActor->SetMapper(m_VtkSelectedPolyDataMapper); m_PointsAssembly->AddPart(m_SelectedActor); } if (m_NumberOfUnselectedAdded > 0) { m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkUnselectedPolyDataMapper->SetInput(m_vtkUnselectedPointList->GetOutput()); //create a new instance of the actor m_UnselectedActor = vtkSmartPointer::New(); m_UnselectedActor->SetMapper(m_VtkUnselectedPolyDataMapper); m_PointsAssembly->AddPart(m_UnselectedActor); } } void mitk::PointSetVtkMapper3D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) { m_UnselectedActor->VisibilityOff(); m_SelectedActor->VisibilityOff(); m_ContourActor->VisibilityOff(); return; } // create new vtk render objects (e.g. sphere for a point) SetVtkMapperImmediateModeRendering(m_VtkSelectedPolyDataMapper); SetVtkMapperImmediateModeRendering(m_VtkUnselectedPolyDataMapper); BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired( renderer, this, GetDataNode() ); if(!needGenerateData) { mitk::FloatProperty * pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); mitk::FloatProperty * contourSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("contoursize")); // only create new vtk render objects if property values were changed if(pointSizeProp && m_PointSize!=pointSizeProp->GetValue() ) needGenerateData = true; if(contourSizeProp && m_ContourRadius!=contourSizeProp->GetValue() ) needGenerateData = true; } if(needGenerateData) { this->CreateVTKRenderObjects(); ls->UpdateGenerateDataTime(); } this->ApplyAllProperties(renderer, m_ContourActor); bool showPoints = true; this->GetDataNode()->GetBoolProperty("show points", showPoints); if(showPoints) { m_UnselectedActor->VisibilityOn(); m_SelectedActor->VisibilityOn(); } else { m_UnselectedActor->VisibilityOff(); m_SelectedActor->VisibilityOff(); } if(dynamic_cast(this->GetDataNode()->GetProperty("opacity")) != NULL) { mitk::FloatProperty::Pointer pointOpacity =dynamic_cast(this->GetDataNode()->GetProperty("opacity")); float opacity = pointOpacity->GetValue(); m_ContourActor->GetProperty()->SetOpacity(opacity); m_UnselectedActor->GetProperty()->SetOpacity(opacity); m_SelectedActor->GetProperty()->SetOpacity(opacity); } bool makeContour = false; this->GetDataNode()->GetBoolProperty("show contour", makeContour); if (makeContour) { m_ContourActor->VisibilityOn(); } else { m_ContourActor->VisibilityOff(); } } void mitk::PointSetVtkMapper3D::ResetMapper( BaseRenderer* /*renderer*/ ) { m_PointsAssembly->VisibilityOff(); } vtkProp* mitk::PointSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_PointsAssembly; } void mitk::PointSetVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { vtkSmartPointer vtktransform = this->GetDataNode()->GetVtkTransform(this->GetTimestep()); m_SelectedActor->SetUserTransform(vtktransform); m_UnselectedActor->SetUserTransform(vtktransform); m_ContourActor->SetUserTransform(vtktransform); } void mitk::PointSetVtkMapper3D::ApplyAllProperties(mitk::BaseRenderer* renderer, vtkActor* actor) { Superclass::ApplyColorAndOpacityProperties(renderer, actor); //check for color props and use it for rendering of selected/unselected points and contour //due to different params in VTK (double/float) we have to convert! //vars to convert to vtkFloatingPointType unselectedColor[4]={1.0f,1.0f,0.0f,1.0f};//yellow vtkFloatingPointType selectedColor[4]={1.0f,0.0f,0.0f,1.0f};//red vtkFloatingPointType contourColor[4]={1.0f,0.0f,0.0f,1.0f};//red //different types for color!!! mitk::Color tmpColor; double opacity = 1.0; //check if there is an unselected property if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("unselectedcolor")) != NULL) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("unselectedcolor"))->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("unselectedcolor")) != NULL) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("unselectedcolor"))->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else { //check if the node has a color float unselectedColorTMP[4]={1.0f,1.0f,0.0f,1.0f};//yellow m_DataNode->GetColor(unselectedColorTMP, NULL); unselectedColor[0] = unselectedColorTMP[0]; unselectedColor[1] = unselectedColorTMP[1]; unselectedColor[2] = unselectedColorTMP[2]; //unselectedColor[3] stays 1.0f } //get selected property if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != NULL) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("selectedcolor")) != NULL) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; } //get contour property if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != NULL) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("contourcolor")) != NULL) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } if(dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("opacity")) != NULL) { mitk::FloatProperty::Pointer pointOpacity =dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("opacity")); opacity = pointOpacity->GetValue(); } else if(dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("opacity")) != NULL) { mitk::FloatProperty::Pointer pointOpacity =dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("opacity")); opacity = pointOpacity->GetValue(); } //finished color / opacity fishing! //check if a contour shall be drawn bool makeContour = false; this->GetDataNode()->GetBoolProperty("show contour", makeContour, renderer); if(makeContour && (m_ContourActor != NULL) ) { this->CreateContour(); m_ContourActor->GetProperty()->SetColor(contourColor); m_ContourActor->GetProperty()->SetOpacity(opacity); } m_SelectedActor->GetProperty()->SetColor(selectedColor); m_SelectedActor->GetProperty()->SetOpacity(opacity); m_UnselectedActor->GetProperty()->SetColor(unselectedColor); m_UnselectedActor->GetProperty()->SetOpacity(opacity); } void mitk::PointSetVtkMapper3D::CreateContour() { vtkSmartPointer vtkContourPolyData = vtkSmartPointer::New(); vtkSmartPointer vtkContourPolyDataMapper = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer polys = vtkSmartPointer::New(); mitk::PointSet::PointsContainer::Iterator pointsIter; // mitk::PointSet::PointDataContainer::Iterator pointDataIter; int j; // get and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet( timestep ); if ( itkPointSet.GetPointer() == NULL) { return; } for (j=0, pointsIter=itkPointSet->GetPoints()->Begin(); pointsIter!=itkPointSet->GetPoints()->End() ; pointsIter++,j++) { vtkIdType cell[2] = {j-1,j}; itk::Point point1 = pointsIter->Value(); points->InsertPoint(j,point1[0],point1[1],point1[2]); if (j>0) polys->InsertNextCell(2,cell); } bool close = false; this->GetDataNode()->GetBoolProperty("close contour", close); if (close) { vtkIdType cell[2] = {j-1,0}; polys->InsertNextCell(2,cell); } vtkSmartPointer contour = vtkSmartPointer::New(); contour->SetPoints(points); contour->SetLines(polys); contour->Update(); vtkSmartPointer tubeFilter = vtkSmartPointer::New(); tubeFilter->SetNumberOfSides( 12 ); tubeFilter->SetInput(contour); //check for property contoursize. m_ContourRadius = 0.5; mitk::FloatProperty::Pointer contourSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("contoursize") ); if (contourSizeProp.IsNotNull()) m_ContourRadius = contourSizeProp->GetValue(); tubeFilter->SetRadius( m_ContourRadius ); tubeFilter->Update(); //add to pipeline vtkContourPolyData->AddInput(tubeFilter->GetOutput()); vtkContourPolyDataMapper->SetInput(vtkContourPolyData->GetOutput()); m_ContourActor->SetMapper(vtkContourPolyDataMapper); m_PointsAssembly->AddPart(m_ContourActor); } void mitk::PointSetVtkMapper3D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "line width", mitk::IntProperty::New(2), renderer, overwrite ); node->AddProperty( "pointsize", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty( "selectedcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); //red node->AddProperty( "color", mitk::ColorProperty::New(1.0f, 1.0f, 0.0f), renderer, overwrite); //yellow node->AddProperty( "opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite ); node->AddProperty( "show contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "close contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "contourcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); node->AddProperty( "contoursize", mitk::FloatProperty::New(0.5), renderer, overwrite ); node->AddProperty( "show points", mitk::BoolProperty::New(true), renderer, overwrite ); node->AddProperty( "updateDataOnRender", mitk::BoolProperty::New(true), renderer, overwrite ); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Core/Code/Rendering/mitkPointSetVtkMapper3D.h b/Core/Code/Rendering/mitkPointSetVtkMapper3D.h index 1ecdff32a8..12b8481144 100644 --- a/Core/Code/Rendering/mitkPointSetVtkMapper3D.h +++ b/Core/Code/Rendering/mitkPointSetVtkMapper3D.h @@ -1,153 +1,158 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPointSetVtkMAPPER3D_H_HEADER_INCLUDED_C1907273 #define MITKPointSetVtkMAPPER3D_H_HEADER_INCLUDED_C1907273 #include #include "mitkVtkMapper.h" #include "mitkBaseRenderer.h" #include class vtkActor; class vtkPropAssembly; class vtkAppendPolyData; class vtkPolyData; class vtkTubeFilter; class vtkPolyDataMapper; namespace mitk { class PointSet; /** * @brief Vtk-based mapper for PointSet * * Due to the need of different colors for selected * and unselected points and the facts, that we also have a contour and * labels for the points, the vtk structure is build up the following way: * * We have two AppendPolyData, one selected, and one unselected and one * for a contour between the points. Each one is connected to an own * PolyDaraMapper and an Actor. The different color for the unselected and * selected state and for the contour is read from properties. * * "unselectedcolor", "selectedcolor" and "contourcolor" are the strings, * that are looked for. Pointlabels are added besides the selected or the * deselected points. * * Then the three Actors are combined inside a vtkPropAssembly and this * object is returned in GetProp() and so hooked up into the rendering * pipeline. * Properties that can be set for point sets and influence the PointSetVTKMapper3D are: * * - \b "color": (ColorProperty*) Color of the point set * - \b "Opacity": (FloatProperty) Opacity of the point set * - \b "show contour": (BoolProperty) If the contour of the points are visible * - \b "contourSizeProp":(FloatProperty) Contour size of the points The default properties are: * - \b "line width": (IntProperty::New(2), renderer, overwrite ) * - \b "pointsize": (FloatProperty::New(1.0), renderer, overwrite) * - \b "selectedcolor": (ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite) //red * - \b "color": (ColorProperty::New(1.0f, 1.0f, 0.0f), renderer, overwrite) //yellow * - \b "show contour": (BoolProperty::New(false), renderer, overwrite ) * - \b "contourcolor": (ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite) * - \b "contoursize": (FloatProperty::New(0.5), renderer, overwrite ) * - \b "close contour": (BoolProperty::New(false), renderer, overwrite ) * - \b "show points": (BoolProperty::New(true), renderer, overwrite ) * - \b "updateDataOnRender": (BoolProperty::New(true), renderer, overwrite ) *Other properties looked for are: * * - \b "show contour": if set to on, lines between the points are shown * - \b "close contour": if set to on, the open strip is closed (first point * connected with last point) * - \b "pointsize": size of the points mapped * - \b "label": text of the Points to show besides points * - \b "contoursize": size of the contour drawn between the points * (if not set, the pointsize is taken) * * @ingroup Mapper */ class MITK_CORE_EXPORT PointSetVtkMapper3D : public VtkMapper { public: mitkClassMacro(PointSetVtkMapper3D, VtkMapper); itkNewMacro(Self); virtual const mitk::PointSet* GetInput(); //overwritten from VtkMapper3D to be able to return a //m_PointsAssembly which is much faster than a vtkAssembly virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer); virtual void UpdateVtkTransform(mitk::BaseRenderer* renderer); static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = NULL, bool overwrite = false); - void ReleaseGraphicsResources(vtkWindow *renWin); + /* + * \deprecatedSince{2013_12} Use ReleaseGraphicsResources(mitk::BaseRenderer* renderer) instead + */ + DEPRECATED(void ReleaseGraphicsResources(vtkWindow *renWin)); + + void ReleaseGraphicsResources(mitk::BaseRenderer* renderer); LocalStorageHandler m_LSH; protected: PointSetVtkMapper3D(); virtual ~PointSetVtkMapper3D(); virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer); virtual void ResetMapper( BaseRenderer* renderer ); virtual void ApplyAllProperties(mitk::BaseRenderer* renderer, vtkActor* actor); virtual void CreateContour(); virtual void CreateVTKRenderObjects(); vtkSmartPointer m_vtkSelectedPointList; vtkSmartPointer m_vtkUnselectedPointList; vtkSmartPointer m_VtkSelectedPolyDataMapper; vtkSmartPointer m_VtkUnselectedPolyDataMapper; vtkSmartPointer m_SelectedActor; vtkSmartPointer m_UnselectedActor; vtkSmartPointer m_ContourActor; vtkSmartPointer m_PointsAssembly; //help for contour between points vtkSmartPointer m_vtkTextList; //variables to be able to log, how many inputs have been added to PolyDatas unsigned int m_NumberOfSelectedAdded; unsigned int m_NumberOfUnselectedAdded; //variables to check if an update of the vtk objects is needed ScalarType m_PointSize; ScalarType m_ContourRadius; }; } // namespace mitk #endif /* MITKPointSetVtkMAPPER3D_H_HEADER_INCLUDED_C1907273 */ diff --git a/Core/Code/Rendering/mitkVtkMapper.h b/Core/Code/Rendering/mitkVtkMapper.h index 6344bcfe5d..06ca18d2f0 100644 --- a/Core/Code/Rendering/mitkVtkMapper.h +++ b/Core/Code/Rendering/mitkVtkMapper.h @@ -1,145 +1,161 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // change number #ifndef VTKMAPPER_H_HEADER_INCLUDED_C1C5453B #define VTKMAPPER_H_HEADER_INCLUDED_C1C5453B #include #include "mitkMapper.h" #include "mitkBaseRenderer.h" #include "mitkDataNode.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include class vtkProp; class vtkProp3D; class vtkActor; namespace mitk { /** \brief Base class of all Vtk Mappers in order to display primitives * by exploiting Vtk functionality. * * Rendering of opaque, translucent or volumetric geometry and overlays * is done in consecutive render passes. * * \ingroup Mapper */ class MITK_CORE_EXPORT VtkMapper : public Mapper { public: mitkClassMacro(VtkMapper,Mapper); virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer) = 0; /** \brief Re-issues all drawing commands required to describe * the entire scene each time a new frame is required, * regardless of actual changes. */ static void SetVtkMapperImmediateModeRendering(vtkMapper *mapper); /** * \brief Returns whether this is an vtk-based mapper * \deprecatedSince{2013_03} All mappers of superclass VTKMapper are vtk based, use a dynamic_cast instead */ DEPRECATED( virtual bool IsVtkBased() const ); /** \brief Determines which geometry should be rendered * (opaque, translucent, volumetric, overlay) * and calls the appropriate function. * * Called by mitk::VtkPropRenderer::Render */ void MitkRender(mitk::BaseRenderer* renderer, mitk::VtkPropRenderer::RenderType type); /** \brief Checks visibility and renders the overlay */ virtual void MitkRenderOverlay(BaseRenderer* renderer); /** \brief Checks visibility and renders untransparent geometry */ virtual void MitkRenderOpaqueGeometry(BaseRenderer* renderer); /** \brief Checks visiblity and renders transparent geometry */ virtual void MitkRenderTranslucentGeometry(BaseRenderer* renderer); /** \brief Checks visibility and renders volumes */ virtual void MitkRenderVolumetricGeometry(BaseRenderer* renderer); /** \brief Returns true if this mapper owns the specified vtkProp for * the given BaseRenderer. * * Note: returns false by default; should be implemented for VTK-based * Mapper subclasses. */ virtual bool HasVtkProp( const vtkProp *prop, BaseRenderer *renderer ); /** \brief Set the vtkTransform of the m_Prop3D for * the current time step of \a renderer * * Called by mitk::VtkPropRenderer::Update before rendering */ virtual void UpdateVtkTransform(mitk::BaseRenderer *renderer); /** * \brief Apply color and opacity properties read from the PropertyList * \deprecatedSince{2013_03} Use ApplyColorAndOpacityProperties(mitk::BaseRenderer* renderer, vtkActor * actor) instead */ DEPRECATED(inline virtual void ApplyProperties(vtkActor* actor, mitk::BaseRenderer* renderer)) { ApplyColorAndOpacityProperties(renderer, actor); } /** * \brief Apply color and opacity properties read from the PropertyList. * Called by mapper subclasses. */ virtual void ApplyColorAndOpacityProperties(mitk::BaseRenderer* renderer, vtkActor * actor); /** * \brief Release vtk-based graphics resources that are being consumed by this mapper. + * * The parameter window could be used to determine which graphic - * resources to releases. Must be overwritten in individual subclasses + * resources to release. Must be overwritten in individual subclasses * if vtkProps are used. + * + * \deprecatedSince{2013_12} Use ReleaseGraphicsResources(mitk::BaseRenderer* renderer) instead */ - virtual void ReleaseGraphicsResources(vtkWindow* /*renWin*/) { }; + DEPRECATED(virtual void ReleaseGraphicsResources(vtkWindow* /*renWin*/)) + { + }; + + /** + * \brief Release vtk-based graphics resources that are being consumed by this mapper. + * + * Method called by mitk::VtkPropRenderer. The parameter renderer could be used to + * determine which graphic resources to release. The local storage is accessible + * by the parameter renderer. Should be overwritten in subclasses. + */ + virtual void ReleaseGraphicsResources(mitk::BaseRenderer* /*renderer*/) + { + }; protected: /** constructor */ VtkMapper(); /** virtual destructor in order to derive from this class */ virtual ~VtkMapper(); private: /** copy constructor */ VtkMapper( const VtkMapper &); /** assignment operator */ VtkMapper & operator=(const VtkMapper &); }; } // namespace mitk #endif /* VTKMAPPER_H_HEADER_INCLUDED_C1C5453B */ diff --git a/Core/Code/Rendering/mitkVtkPropRenderer.cpp b/Core/Code/Rendering/mitkVtkPropRenderer.cpp index 97f0aadc91..562706a4ec 100644 --- a/Core/Code/Rendering/mitkVtkPropRenderer.cpp +++ b/Core/Code/Rendering/mitkVtkPropRenderer.cpp @@ -1,1005 +1,1005 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVtkPropRenderer.h" // MAPPERS #include "mitkMapper.h" #include "mitkImageVtkMapper2D.h" #include "mitkVtkMapper.h" #include "mitkGLMapper.h" #include "mitkGeometry2DDataVtkMapper3D.h" #include "mitkImageSliceSelector.h" #include "mitkRenderingManager.h" #include "mitkGL.h" #include "mitkGeometry3D.h" #include "mitkDisplayGeometry.h" #include "mitkLevelWindow.h" #include "mitkCameraController.h" #include "mitkVtkInteractorCameraController.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include "mitkSurface.h" #include "mitkNodePredicateDataType.h" #include "mitkVtkInteractorStyle.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::VtkPropRenderer::VtkPropRenderer( const char* name, vtkRenderWindow * renWin, mitk::RenderingManager* rm ) : BaseRenderer(name,renWin, rm), m_VtkMapperPresent(false), m_CameraInitializedForMapperID(0) { didCount=false; m_WorldPointPicker = vtkWorldPointPicker::New(); m_PointPicker = vtkPointPicker::New(); m_PointPicker->SetTolerance( 0.0025 ); m_CellPicker = vtkCellPicker::New(); m_CellPicker->SetTolerance( 0.0025 ); mitk::Geometry2DDataVtkMapper3D::Pointer geometryMapper = mitk::Geometry2DDataVtkMapper3D::New(); m_CurrentWorldGeometry2DMapper = geometryMapper; m_CurrentWorldGeometry2DNode->SetMapper(2, geometryMapper); m_LightKit = vtkLightKit::New(); m_LightKit->AddLightsToRenderer(m_VtkRenderer); m_PickingMode = WorldPointPicking; m_TextRenderer = vtkRenderer::New(); m_TextRenderer->SetRenderWindow(renWin); m_TextRenderer->SetInteractive(0); m_TextRenderer->SetErase(0); } /*! \brief Destructs the VtkPropRenderer. */ mitk::VtkPropRenderer::~VtkPropRenderer() { // Workaround for GLDisplayList Bug { m_MapperID=0; checkState(); } if (m_LightKit != NULL) m_LightKit->Delete(); if (m_VtkRenderer!=NULL) { m_CameraController = NULL; m_VtkRenderer->Delete(); m_VtkRenderer = NULL; } else m_CameraController = NULL; if (m_WorldPointPicker != NULL) m_WorldPointPicker->Delete(); if (m_PointPicker != NULL) m_PointPicker->Delete(); if (m_CellPicker != NULL) m_CellPicker->Delete(); if (m_TextRenderer != NULL) m_TextRenderer->Delete(); } void mitk::VtkPropRenderer::SetDataStorage( mitk::DataStorage* storage ) { if ( storage == NULL ) return; BaseRenderer::SetDataStorage(storage); static_cast(m_CurrentWorldGeometry2DMapper.GetPointer())->SetDataStorageForTexture( m_DataStorage.GetPointer() ); // Compute the geometry from the current data tree bounds and set it as world geometry this->SetWorldGeometryToDataStorageBounds(); } bool mitk::VtkPropRenderer::SetWorldGeometryToDataStorageBounds() { if ( m_DataStorage.IsNull() ) return false; //initialize world geometry mitk::TimeGeometry::Pointer geometry = m_DataStorage->ComputeVisibleBoundingGeometry3D( NULL, "includeInBoundingBox" ); if ( geometry.IsNull() ) return false; this->SetWorldTimeGeometry(geometry); //this->GetDisplayGeometry()->SetSizeInDisplayUnits( this->m_TextRenderer->GetRenderWindow()->GetSize()[0], this->m_TextRenderer->GetRenderWindow()->GetSize()[1] ); this->GetDisplayGeometry()->Fit(); this->GetVtkRenderer()->ResetCamera(); this->Modified(); return true; } /*! \brief Called by the vtkMitkRenderProp in order to start MITK rendering process. */ int mitk::VtkPropRenderer::Render(mitk::VtkPropRenderer::RenderType type) { // Do we have objects to render? if ( this->GetEmptyWorldGeometry()) return 0; if ( m_DataStorage.IsNull()) return 0; // Update mappers and prepare mapper queue if (type == VtkPropRenderer::Opaque) this->PrepareMapperQueue(); //go through the generated list and let the sorted mappers paint bool lastVtkBased = true; //bool sthVtkBased = false; for(MappersMapType::iterator it = m_MappersMap.begin(); it != m_MappersMap.end(); it++) { Mapper * mapper = (*it).second; VtkMapper* vtkmapper = dynamic_cast(mapper); if(vtkmapper) { //sthVtkBased = true; if(!lastVtkBased) { Disable2DOpenGL(); lastVtkBased = true; } } else if(lastVtkBased) { Enable2DOpenGL(); lastVtkBased = false; } mapper->MitkRender(this, type); } this->UpdateOverlays(); if (lastVtkBased == false) Disable2DOpenGL(); // Render text if (type == VtkPropRenderer::Overlay) { if (m_TextCollection.size() > 0) { m_TextRenderer->SetViewport( this->GetVtkRenderer()->GetViewport() ); for (TextMapType::iterator it = m_TextCollection.begin(); it != m_TextCollection.end() ; it++) m_TextRenderer->AddViewProp((*it).second); m_TextRenderer->Render(); } } return 1; } /*! \brief PrepareMapperQueue iterates the datatree PrepareMapperQueue iterates the datatree in order to find mappers which shall be rendered. Also, it sortes the mappers wrt to their layer. */ void mitk::VtkPropRenderer::PrepareMapperQueue() { // variable for counting LOD-enabled mappers m_NumberOfVisibleLODEnabledMappers = 0; // Do we have to update the mappers ? if ( m_LastUpdateTime < GetMTime() || m_LastUpdateTime < GetDisplayGeometry()->GetMTime() ) { Update(); } else if (m_MapperID>=1 && m_MapperID < 6) Update(); // remove all text properties before mappers will add new ones m_TextRenderer->RemoveAllViewProps(); for ( unsigned int i=0; iDelete(); } m_TextCollection.clear(); // clear priority_queue m_MappersMap.clear(); int mapperNo = 0; //DataStorage if( m_DataStorage.IsNull() ) return; DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { DataNode::Pointer node = it->Value(); if ( node.IsNull() ) continue; mitk::Mapper::Pointer mapper = node->GetMapper(m_MapperID); if ( mapper.IsNull() ) continue; bool visible = true; node->GetVisibility(visible, this, "visible"); // The information about LOD-enabled mappers is required by RenderingManager if ( mapper->IsLODEnabled( this ) && visible ) { ++m_NumberOfVisibleLODEnabledMappers; } // mapper without a layer property get layer number 1 int layer = 1; node->GetIntProperty("layer", layer, this); int nr = (layer<<16) + mapperNo; m_MappersMap.insert( std::pair< int, Mapper * >( nr, mapper ) ); mapperNo++; } } /*! \brief Enable2DOpenGL() and Disable2DOpenGL() are used to switch between 2D rendering (orthographic projection) and 3D rendering (perspective projection) */ void mitk::VtkPropRenderer::Enable2DOpenGL() { GLint iViewport[4]; // Get a copy of the viewport glGetIntegerv( GL_VIEWPORT, iViewport ); // Save a copy of the projection matrix so that we can restore it // when it's time to do 3D rendering again. glMatrixMode( GL_PROJECTION ); glPushMatrix(); glLoadIdentity(); // Set up the orthographic projection const DisplayGeometry* displayGeometry = this->GetDisplayGeometry(); float displayGeometryWidth = displayGeometry->GetSizeInDisplayUnits()[0]; float displayGeometryHeight = displayGeometry->GetSizeInDisplayUnits()[1]; float viewportWidth = iViewport[2]; float viewportHeight = iViewport[3]; /* The following makes OpenGL mappers draw into the same viewport that is used by VTK when someone calls vtkRenderer::SetViewport(). The parameters of glOrtho describe what "input" coordinates (display coordinates generated by the OpenGL mappers) are transformed into the region defined by the viewport. The call has to consider that the scene is fit vertically and centered horizontally. Problem: this is a crude first step towards rendering into viewports. - mitkViewportRenderingTest demonstrates the non-interactive rendering that is now possible - interactors that measure mouse movement in pixels will probably run into problems with display-to-world transformation A proper solution should probably modify the DisplayGeometry to correctly describe the viewport. */ // iViewport is (x,y,width,height) // glOrtho expects (left,right,bottom,top,znear,zfar) glOrtho( 0 - 0.5 * (viewportWidth/viewportHeight-1.0)*displayGeometryHeight + 0.5 * (displayGeometryWidth - displayGeometryHeight) , displayGeometryWidth + 0.5 * (viewportWidth/viewportHeight-1.0)*displayGeometryHeight - 0.5 * (displayGeometryWidth - displayGeometryHeight) , 0, displayGeometryHeight, -1.0, 1.0 ); glMatrixMode( GL_MODELVIEW ); glPushMatrix(); glLoadIdentity(); // Make sure depth testing and lighting are disabled for 2D rendering until // we are finished rendering in 2D glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT ); glDisable( GL_DEPTH_TEST ); glDisable( GL_LIGHTING ); // disable the texturing here so crosshair is painted in the correct colors // vtk will reenable texturing every time it is needed glDisable( GL_TEXTURE_1D ); glDisable( GL_TEXTURE_2D ); glLineWidth(1.0); } /*! \brief Initialize the VtkPropRenderer Enable2DOpenGL() and Disable2DOpenGL() are used to switch between 2D rendering (orthographic projection) and 3D rendering (perspective projection) */ void mitk::VtkPropRenderer::Disable2DOpenGL() { glPopAttrib(); glMatrixMode( GL_PROJECTION ); glPopMatrix(); glMatrixMode( GL_MODELVIEW ); glPopMatrix(); } void mitk::VtkPropRenderer::Update(mitk::DataNode* datatreenode) { if(datatreenode!=NULL) { mitk::Mapper::Pointer mapper = datatreenode->GetMapper(m_MapperID); if(mapper.IsNotNull()) { GLMapper* glmapper=dynamic_cast(mapper.GetPointer()); if(GetDisplayGeometry()->IsValid()) { if(glmapper != NULL) { glmapper->Update(this); m_VtkMapperPresent=false; } else { VtkMapper* vtkmapper=dynamic_cast(mapper.GetPointer()); if(vtkmapper != NULL) { vtkmapper->Update(this); vtkmapper->UpdateVtkTransform(this); m_VtkMapperPresent=true; } } } } } } void mitk::VtkPropRenderer::Update() { if( m_DataStorage.IsNull() ) return; m_VtkMapperPresent = false; mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetAll(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) Update(it->Value()); Modified(); m_LastUpdateTime = GetMTime(); } /*! \brief This method is called from the two Constructors */ void mitk::VtkPropRenderer::InitRenderer(vtkRenderWindow* renderWindow) { BaseRenderer::InitRenderer(renderWindow); if(renderWindow == NULL) { m_InitNeeded = false; m_ResizeNeeded = false; return; } m_InitNeeded = true; m_ResizeNeeded = true; m_LastUpdateTime = 0; } /*! \brief Resize the OpenGL Window */ void mitk::VtkPropRenderer::Resize(int w, int h) { BaseRenderer::Resize(w, h); m_RenderingManager->RequestUpdate(this->GetRenderWindow()); } void mitk::VtkPropRenderer::InitSize(int w, int h) { m_RenderWindow->SetSize(w,h); Superclass::InitSize(w, h); Modified(); Update(); if(m_VtkRenderer!=NULL) { int w=vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); m_VtkRenderer->ResetCamera(); vtkObject::SetGlobalWarningDisplay(w); } } void mitk::VtkPropRenderer::SetMapperID(const MapperSlotId mapperId) { if(m_MapperID != mapperId) Superclass::SetMapperID(mapperId); // Workaround for GL Displaylist Bug checkState(); } /*! \brief Activates the current renderwindow. */ void mitk::VtkPropRenderer::MakeCurrent() { if(m_RenderWindow!=NULL) m_RenderWindow->MakeCurrent(); } void mitk::VtkPropRenderer::PickWorldPoint(const mitk::Point2D& displayPoint, mitk::Point3D& worldPoint) const { if(m_VtkMapperPresent) { //m_WorldPointPicker->SetTolerance (0.0001); switch ( m_PickingMode ) { case (WorldPointPicking) : { m_WorldPointPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_WorldPointPicker->GetPickPosition(), worldPoint); break; } case (PointPicking) : { // create a new vtkRenderer // give it all necessary information (camera position, etc.) // get all surfaces from datastorage, get actors from them // add all those actors to the new renderer // give this new renderer to pointpicker /* vtkRenderer* pickingRenderer = vtkRenderer::New(); pickingRenderer->SetActiveCamera( ); DataStorage* dataStorage = m_DataStorage; TNodePredicateDataType isSurface; DataStorage::SetOfObjects::ConstPointer allSurfaces = dataStorage->GetSubset( isSurface ); MITK_INFO << "in picking: got " << allSurfaces->size() << " surfaces." << std::endl; for (DataStorage::SetOfObjects::const_iterator iter = allSurfaces->begin(); iter != allSurfaces->end(); ++iter) { const DataNode* currentNode = *iter; VtkMapper3D* baseVtkMapper3D = dynamic_cast( currentNode->GetMapper( BaseRenderer::Standard3D ) ); if ( baseVtkMapper3D ) { vtkActor* actor = dynamic_cast( baseVtkMapper3D->GetViewProp() ); if (actor) { MITK_INFO << "a" << std::flush; pickingRenderer->AddActor( actor ); } } } MITK_INFO << ";" << std::endl; */ m_PointPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_PointPicker->GetPickPosition(), worldPoint); break; } case(CellPicking) : { m_CellPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_CellPicker->GetPickPosition(), worldPoint); break; } } } else { Superclass::PickWorldPoint(displayPoint, worldPoint); } } mitk::DataNode * mitk::VtkPropRenderer::PickObject( const Point2D &displayPosition, Point3D &worldPosition ) const { if ( m_VtkMapperPresent ) { m_CellPicker->InitializePickList(); // Iterate over all DataStorage objects to determine all vtkProps intended // for picking DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for ( DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it ) { DataNode *node = it->Value(); if ( node == NULL ) continue; bool pickable = false; node->GetBoolProperty( "pickable", pickable ); if ( !pickable ) continue; VtkMapper *mapper = dynamic_cast < VtkMapper * > ( node->GetMapper( m_MapperID ) ); if ( mapper == NULL ) continue; vtkProp *prop = mapper->GetVtkProp( (mitk::BaseRenderer *)this ); if ( prop == NULL ) continue; m_CellPicker->AddPickList( prop ); } // Do the picking and retrieve the picked vtkProp (if any) m_CellPicker->PickFromListOn(); m_CellPicker->Pick( displayPosition[0], displayPosition[1], 0.0, m_VtkRenderer ); m_CellPicker->PickFromListOff(); vtk2itk( m_CellPicker->GetPickPosition(), worldPosition ); vtkProp *prop = m_CellPicker->GetViewProp(); if ( prop == NULL ) { return NULL; } // Iterate over all DataStorage objects to determine if the retrieved // vtkProp is owned by any associated mapper. for ( DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { DataNode::Pointer node = it->Value(); if ( node.IsNull() ) continue; mitk::Mapper * mapper = node->GetMapper( m_MapperID ); if ( mapper == NULL) continue; mitk::VtkMapper * vtkmapper = dynamic_cast< VtkMapper * >(mapper); if(vtkmapper){ //if vtk-based, then ... if ( vtkmapper->HasVtkProp( prop, const_cast< mitk::VtkPropRenderer * >( this ) ) ) { return node; } } } return NULL; } else { return Superclass::PickObject( displayPosition, worldPosition ); } }; /*! \brief Writes some 2D text as overlay. Function returns an unique int Text_ID for each call, which can be used via the GetTextLabelProperty(int text_id) function in order to get a vtkTextProperty. This property enables the setup of font, font size, etc. */ int mitk::VtkPropRenderer::WriteSimpleText(std::string text, double posX, double posY, double color1, double color2, double color3, float opacity) { if(!text.empty()) { Point2D p; p[0] = posX; p[1] = posY; p = TransformOpenGLPointToViewport(p); vtkTextActor* textActor = vtkTextActor::New(); textActor->SetPosition(p[0], p[1]); textActor->SetInput(text.c_str()); textActor->SetTextScaleModeToNone(); textActor->GetTextProperty()->SetColor(color1, color2, color3); //TODO: Read color from node property textActor->GetTextProperty()->SetOpacity( opacity ); int text_id = m_TextCollection.size(); m_TextCollection.insert(TextMapType::value_type(text_id,textActor)); return text_id; } else { return -1; } } /*! \brief Can be used in order to get a vtkTextProperty for a specific text_id. This property enables the setup of font, font size, etc. */ vtkTextProperty* mitk::VtkPropRenderer::GetTextLabelProperty(int text_id) { return this->m_TextCollection[text_id]->GetTextProperty(); } void mitk::VtkPropRenderer::InitPathTraversal() { if (m_DataStorage.IsNotNull()) { m_PickingObjects = m_DataStorage->GetAll(); m_PickingObjectsIterator = m_PickingObjects->begin(); } } vtkAssemblyPath* mitk::VtkPropRenderer::GetNextPath() { if (m_DataStorage.IsNull() ) { return NULL; } if ( m_PickingObjectsIterator == m_PickingObjects->end() ) { return NULL; } vtkAssemblyPath* returnPath = vtkAssemblyPath::New(); //returnPath->Register(NULL); bool success = false; while (!success) { // loop until AddNode can be called successfully const DataNode* node = *m_PickingObjectsIterator; if (node) { Mapper* mapper = node->GetMapper( BaseRenderer::Standard3D ); if (mapper) { VtkMapper* vtkmapper = dynamic_cast( mapper ); if (vtkmapper) { vtkProp* prop = vtkmapper->GetVtkProp(this); if ( prop && prop->GetVisibility() ) { // add to assembly path returnPath->AddNode( prop, prop->GetMatrix() ); success = true; } } } } ++m_PickingObjectsIterator; if ( m_PickingObjectsIterator == m_PickingObjects->end() ) break; } if ( success ) { return returnPath; } else { return NULL; } } void mitk::VtkPropRenderer::ReleaseGraphicsResources(vtkWindow *renWin) { if( m_DataStorage.IsNull() ) return; DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::const_iterator iter = allObjects->begin(); iter != allObjects->end(); ++iter) { DataNode::Pointer node = *iter; if ( node.IsNull() ) continue; Mapper * mapper = node->GetMapper(m_MapperID); if (mapper) { VtkMapper* vtkmapper = dynamic_cast( mapper ); if(vtkmapper) - vtkmapper->ReleaseGraphicsResources(renWin); + vtkmapper->ReleaseGraphicsResources(this); } } } const vtkWorldPointPicker *mitk::VtkPropRenderer::GetWorldPointPicker() const { return m_WorldPointPicker; } const vtkPointPicker *mitk::VtkPropRenderer::GetPointPicker() const { return m_PointPicker; } const vtkCellPicker *mitk::VtkPropRenderer::GetCellPicker() const { return m_CellPicker; } mitk::VtkPropRenderer::MappersMapType mitk::VtkPropRenderer::GetMappersMap() const { return m_MappersMap; } // Workaround for GL Displaylist bug static int glWorkAroundGlobalCount = 0; bool mitk::VtkPropRenderer::useImmediateModeRendering() { return glWorkAroundGlobalCount>1; } void mitk::VtkPropRenderer::checkState() { if (m_MapperID == Standard3D) { if (!didCount) { didCount = true; glWorkAroundGlobalCount++; if (glWorkAroundGlobalCount == 2) { MITK_INFO << "Multiple 3D Renderwindows active...: turning Immediate Rendering ON for legacy mappers"; // vtkMapper::GlobalImmediateModeRenderingOn(); } //MITK_INFO << "GLOBAL 3D INCREASE " << glWorkAroundGlobalCount << "\n"; } } else { if(didCount) { didCount=false; glWorkAroundGlobalCount--; if(glWorkAroundGlobalCount==1) { MITK_INFO << "Single 3D Renderwindow active...: turning Immediate Rendering OFF for legacy mappers"; // vtkMapper::GlobalImmediateModeRenderingOff(); } //MITK_INFO << "GLOBAL 3D DECREASE " << glWorkAroundGlobalCount << "\n"; } } } //### Contains all methods which are neceassry before each VTK Render() call void mitk::VtkPropRenderer::PrepareRender() { if ( this->GetMapperID() != m_CameraInitializedForMapperID ) { Initialize2DvtkCamera(); //Set parallel projection etc. } AdjustCameraToScene(); //Prepare camera for 2D render windows } bool mitk::VtkPropRenderer::Initialize2DvtkCamera() { if ( this->GetMapperID() == Standard3D ) { //activate parallel projection for 2D this->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(false); this->GetRenderWindow()->GetInteractor()->SetInteractorStyle( vtkInteractorStyleTrackballCamera::New() ); m_CameraInitializedForMapperID = Standard3D; } else if( this->GetMapperID() == Standard2D) { //activate parallel projection for 2D this->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(true); //turn the light out in the scene in order to render correct grey values. //TODO Implement a property for light in the 2D render windows (in another method) this->GetVtkRenderer()->RemoveAllLights(); this->GetRenderWindow()->GetInteractor()->SetInteractorStyle( mitkVtkInteractorStyle::New() ); m_CameraInitializedForMapperID = Standard2D; } return true; } void mitk::VtkPropRenderer::AdjustCameraToScene(){ if(this->GetMapperID() == Standard2D) { const mitk::DisplayGeometry* displayGeometry = this->GetDisplayGeometry(); double objectHeightInMM = this->GetCurrentWorldGeometry2D()->GetExtentInMM(1);//the height of the current object slice in mm double displayHeightInMM = displayGeometry->GetSizeInMM()[1]; //the display height in mm (gets smaller when you zoom in) double zoomFactor = objectHeightInMM/displayHeightInMM; //displayGeometry->GetScaleFactorMMPerDisplayUnit() //determine how much of the object can be displayed Vector2D displayGeometryOriginInMM = displayGeometry->GetOriginInMM(); //top left of the render window (Origin) Vector2D displayGeometryCenterInMM = displayGeometryOriginInMM + displayGeometry->GetSizeInMM()*0.5; //center of the render window: (Origin + Size/2) //Scale the rendered object: //The image is scaled by a single factor, because in an orthographic projection sizes //are preserved (so you cannot scale X and Y axis with different parameters). The //parameter sets the size of the total display-volume. If you set this to the image //height, the image plus a border with the size of the image will be rendered. //Therefore, the size is imageHeightInMM / 2. this->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(objectHeightInMM*0.5 ); //zooming with the factor calculated by dividing displayHeight through imegeHeight. The factor is inverse, because the VTK zoom method is working inversely. this->GetVtkRenderer()->GetActiveCamera()->Zoom(zoomFactor); //the center of the view-plane double viewPlaneCenter[3]; viewPlaneCenter[0] = displayGeometryCenterInMM[0]; viewPlaneCenter[1] = displayGeometryCenterInMM[1]; viewPlaneCenter[2] = 0.0; //the view-plane is located in the XY-plane with Z=0.0 //define which direction is "up" for the ciamera (like default for vtk (0.0, 1.0, 0.0) double cameraUp[3]; cameraUp[0] = 0.0; cameraUp[1] = 1.0; cameraUp[2] = 0.0; //the position of the camera (center[0], center[1], 900000) double cameraPosition[3]; cameraPosition[0] = viewPlaneCenter[0]; cameraPosition[1] = viewPlaneCenter[1]; cameraPosition[2] = 900000.0; //Reason for 900000: VTK seems to calculate the clipping planes wrong for small values. See VTK bug (id #7823) in VTK bugtracker. //set the camera corresponding to the textured plane vtkSmartPointer camera = this->GetVtkRenderer()->GetActiveCamera(); if (camera) { camera->SetPosition( cameraPosition ); //set the camera position on the textured plane normal (in our case this is the view plane normal) camera->SetFocalPoint( viewPlaneCenter ); //set the focal point to the center of the textured plane camera->SetViewUp( cameraUp ); //set the view-up for the camera // double distance = sqrt((cameraPosition[2]-viewPlaneCenter[2])*(cameraPosition[2]-viewPlaneCenter[2])); // camera->SetClippingRange(distance-50, distance+50); //Reason for huge range: VTK seems to calculate the clipping planes wrong for small values. See VTK bug (id #7823) in VTK bugtracker. camera->SetClippingRange(0.1, 1000000); //Reason for huge range: VTK seems to calculate the clipping planes wrong for small values. See VTK bug (id #7823) in VTK bugtracker. } const PlaneGeometry *planeGeometry = dynamic_cast< const PlaneGeometry * >( this->GetCurrentWorldGeometry2D() ); if ( planeGeometry != NULL ) { //Transform the camera to the current position (transveral, coronal and saggital plane). //This is necessary, because the SetUserTransform() method does not manipulate the vtkCamera. //(Without not all three planes would be visible). vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = vtkSmartPointer::New(); Point3D origin; Vector3D right, bottom, normal; origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); // right = Extent of Image in mm (worldspace) bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); right.Normalize(); bottom.Normalize(); normal.Normalize(); matrix->SetElement(0, 0, right[0]); matrix->SetElement(1, 0, right[1]); matrix->SetElement(2, 0, right[2]); matrix->SetElement(0, 1, bottom[0]); matrix->SetElement(1, 1, bottom[1]); matrix->SetElement(2, 1, bottom[2]); matrix->SetElement(0, 2, normal[0]); matrix->SetElement(1, 2, normal[1]); matrix->SetElement(2, 2, normal[2]); matrix->SetElement(0, 3, origin[0]); matrix->SetElement(1, 3, origin[1]); matrix->SetElement(2, 3, origin[2]); matrix->SetElement(3, 0, 0.0); matrix->SetElement(3, 1, 0.0); matrix->SetElement(3, 2, 0.0); matrix->SetElement(3, 3, 1.0); trans->SetMatrix(matrix); //Transform the camera to the current position (transveral, coronal and saggital plane). this->GetVtkRenderer()->GetActiveCamera()->ApplyTransform(trans); } } } mitk::Point2D mitk::VtkPropRenderer::TransformOpenGLPointToViewport( mitk::Point2D point ) { GLint iViewport[4]; // Get a copy of the viewport glGetIntegerv( GL_VIEWPORT, iViewport ); const mitk::DisplayGeometry* displayGeometry = this->GetDisplayGeometry(); float displayGeometryWidth = displayGeometry->GetSizeInDisplayUnits()[0]; float displayGeometryHeight = displayGeometry->GetSizeInDisplayUnits()[1]; float viewportWidth = iViewport[2]; float viewportHeight = iViewport[3]; // seemingly right float zoom = viewportHeight / displayGeometryHeight; // see glOrtho call above for more explanation point[0] += 0.5 * (viewportWidth/viewportHeight-1.0)*displayGeometryHeight - 0.5 * (displayGeometryWidth - displayGeometryHeight) ; point[0] *= zoom; point[1] *= zoom; return point; } diff --git a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp index 533a066154..a538553755 100644 --- a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp +++ b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp @@ -1,448 +1,449 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG +#include #include "mitkTestDICOMLoading.h" #include mitk::TestDICOMLoading::TestDICOMLoading() :m_PreviousCLocale(NULL) { } void mitk::TestDICOMLoading::SetDefaultLocale() { // remember old locale only once if (m_PreviousCLocale == NULL) { m_PreviousCLocale = setlocale(LC_NUMERIC, NULL); // set to "C" setlocale(LC_NUMERIC, "C"); m_PreviousCppLocale = std::cin.getloc(); std::locale l( "C" ); std::cin.imbue(l); std::cout.imbue(l); } } void mitk::TestDICOMLoading::ResetUserLocale() { if (m_PreviousCLocale) { setlocale(LC_NUMERIC, m_PreviousCLocale); std::cin.imbue(m_PreviousCppLocale); std::cout.imbue(m_PreviousCppLocale); m_PreviousCLocale = NULL; } } mitk::TestDICOMLoading::ImageList mitk::TestDICOMLoading::LoadFiles( const StringContainer& files, Image::Pointer preLoadedVolume ) { for (StringContainer::const_iterator iter = files.begin(); iter != files.end(); ++iter) { MITK_DEBUG << "File " << *iter; } ImageList result; DicomSeriesReader::FileNamesGrouping seriesInFiles = DicomSeriesReader::GetSeries( files, true ); // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a standard topic??) for (DicomSeriesReader::FileNamesGrouping::const_iterator seriesIter = seriesInFiles.begin(); seriesIter != seriesInFiles.end(); ++seriesIter) { StringContainer files = seriesIter->second.GetFilenames(); DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( files, true, true, true, 0, preLoadedVolume ); // true, true, true ist just a copy of the default values if (node.IsNotNull()) { Image::Pointer image = dynamic_cast( node->GetData() ); result.push_back( image ); } else { } } return result; } std::string mitk::TestDICOMLoading::ComponentTypeToString(int type) { if (type == itk::ImageIOBase::UCHAR) return "UCHAR"; else if (type == itk::ImageIOBase::CHAR) return "CHAR"; else if (type == itk::ImageIOBase::USHORT) return "USHORT"; else if (type == itk::ImageIOBase::SHORT) return "SHORT"; else if (type == itk::ImageIOBase::UINT) return "UINT"; else if (type == itk::ImageIOBase::INT) return "INT"; else if (type == itk::ImageIOBase::ULONG) return "ULONG"; else if (type == itk::ImageIOBase::LONG) return "LONG"; else if (type == itk::ImageIOBase::FLOAT) return "FLOAT"; else if (type == itk::ImageIOBase::DOUBLE) return "DOUBLE"; else return "UNKNOWN"; } // add a line to stringstream result (see DumpImageInformation #define DumpLine(field, data) DumpILine(0, field, data) // add an indented(!) line to stringstream result (see DumpImageInformation #define DumpILine(indent, field, data) \ { \ std::string DumpLine_INDENT; DumpLine_INDENT.resize(indent, ' ' ); \ result << DumpLine_INDENT << field << ": " << data << "\n"; \ } std::string mitk::TestDICOMLoading::DumpImageInformation( const Image* image ) { std::stringstream result; if (image == NULL) return result.str(); SetDefaultLocale(); // basic image data DumpLine( "Pixeltype", ComponentTypeToString(image->GetPixelType().GetComponentType()) ); DumpLine( "BitsPerPixel", image->GetPixelType().GetBpe() ); DumpLine( "Dimension", image->GetDimension() ); result << "Dimensions: "; for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) result << image->GetDimension(dim) << " "; result << "\n"; // geometry data result << "Geometry: \n"; Geometry3D* geometry = image->GetGeometry(); if (geometry) { AffineTransform3D* transform = geometry->GetIndexToWorldTransform(); if (transform) { result << " " << "Matrix: "; const AffineTransform3D::MatrixType& matrix = transform->GetMatrix(); for (unsigned int i = 0; i < 3; ++i) for (unsigned int j = 0; j < 3; ++j) result << matrix[i][j] << " "; result << "\n"; result << " " << "Offset: "; const AffineTransform3D::OutputVectorType& offset = transform->GetOffset(); for (unsigned int i = 0; i < 3; ++i) result << offset[i] << " "; result << "\n"; result << " " << "Center: "; const AffineTransform3D::InputPointType& center = transform->GetCenter(); for (unsigned int i = 0; i < 3; ++i) result << center[i] << " "; result << "\n"; result << " " << "Translation: "; const AffineTransform3D::OutputVectorType& translation = transform->GetTranslation(); for (unsigned int i = 0; i < 3; ++i) result << translation[i] << " "; result << "\n"; result << " " << "Scale: "; const double* scale = transform->GetScale(); for (unsigned int i = 0; i < 3; ++i) result << scale[i] << " "; result << "\n"; result << " " << "Origin: "; const Point3D& origin = geometry->GetOrigin(); for (unsigned int i = 0; i < 3; ++i) result << origin[i] << " "; result << "\n"; result << " " << "Spacing: "; const Vector3D& spacing = geometry->GetSpacing(); for (unsigned int i = 0; i < 3; ++i) result << spacing[i] << " "; result << "\n"; result << " " << "TimeBounds: "; const TimeBounds timeBounds = geometry->GetTimeBounds(); for (unsigned int i = 0; i < 2; ++i) result << timeBounds[i] << " "; result << "\n"; } } ResetUserLocale(); return result.str(); } std::string mitk::TestDICOMLoading::trim(const std::string& pString, const std::string& pWhitespace) { const size_t beginStr = pString.find_first_not_of(pWhitespace); if (beginStr == std::string::npos) { // no content return ""; } const size_t endStr = pString.find_last_not_of(pWhitespace); const size_t range = endStr - beginStr + 1; return pString.substr(beginStr, range); } std::string mitk::TestDICOMLoading::reduce(const std::string& pString, const std::string& pFill, const std::string& pWhitespace) { // trim first std::string result(trim(pString, pWhitespace)); // replace sub ranges size_t beginSpace = result.find_first_of(pWhitespace); while (beginSpace != std::string::npos) { const size_t endSpace = result.find_first_not_of(pWhitespace, beginSpace); const size_t range = endSpace - beginSpace; result.replace(beginSpace, range, pFill); const size_t newStart = beginSpace + pFill.length(); beginSpace = result.find_first_of(pWhitespace, newStart); } return result; } bool mitk::TestDICOMLoading::CompareSpacedValueFields( const std::string& reference, const std::string& test, double /*eps*/ ) { bool result(true); // tokenize string, compare each token, if possible by float comparison std::stringstream referenceStream(reduce(reference)); std::stringstream testStream(reduce(test)); std::string refToken; std::string testToken; while ( std::getline( referenceStream, refToken, ' ' ) && std::getline ( testStream, testToken, ' ' ) ) { float refNumber; float testNumber; if ( this->StringToNumber(refToken, refNumber) ) { if ( this->StringToNumber(testToken, testNumber) ) { // print-out compared tokens if DEBUG output allowed MITK_DEBUG << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << testToken << "'" << " value " << testNumber; bool old_result = result; result &= ( fabs(refNumber - testNumber) < 0.0001 /*mitk::eps*/ ); // log the token/number which causes the test to fail if( old_result != result) { MITK_ERROR << std::setprecision(16) << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << testToken << "'" << " value " << testNumber; MITK_ERROR << "[FALSE] - difference: " << std::setprecision(16) << fabs(refNumber - testNumber) << " EPS: " << 0.0001;// mitk::eps; } } else { MITK_ERROR << refNumber << " cannot be compared to '" << testToken << "'"; } } else { MITK_DEBUG << "Token '" << refToken << "'" << " handled as string"; result &= refToken == testToken; } } if ( std::getline( referenceStream, refToken, ' ' ) ) { MITK_ERROR << "Reference string still had values when test string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } else if ( std::getline( testStream, testToken, ' ' ) ) { MITK_ERROR << "Test string still had values when reference string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } return result; } bool mitk::TestDICOMLoading::CompareImageInformationDumps( const std::string& referenceDump, const std::string& testDump ) { KeyValueMap reference = ParseDump(referenceDump); KeyValueMap test = ParseDump(testDump); bool testResult(true); // verify all expected values for (KeyValueMap::const_iterator refIter = reference.begin(); refIter != reference.end(); ++refIter) { const std::string& refKey = refIter->first; const std::string& refValue = refIter->second; if ( test.find(refKey) != test.end() ) { const std::string& testValue = test[refKey]; bool thisTestResult = CompareSpacedValueFields( refValue, testValue ); testResult &= thisTestResult; MITK_DEBUG << refKey << ": '" << refValue << "' == '" << testValue << "' ? " << (thisTestResult?"YES":"NO"); } else { MITK_ERROR << "Reference dump contains a key'" << refKey << "' (value '" << refValue << "')." ; MITK_ERROR << "This key is expected to be generated for tests (but was not). Most probably you need to update your test data."; return false; } } // now check test dump does not contain any additional keys for (KeyValueMap::const_iterator testIter = test.begin(); testIter != test.end(); ++testIter) { const std::string& key = testIter->first; const std::string& value = testIter->second; if ( reference.find(key) == reference.end() ) { MITK_ERROR << "Test dump contains an unexpected key'" << key << "' (value '" << value << "')." ; MITK_ERROR << "This key is not expected. Most probably you need to update your test data."; return false; } } return testResult; } mitk::TestDICOMLoading::KeyValueMap mitk::TestDICOMLoading::ParseDump( const std::string& dump ) { KeyValueMap parsedResult; std::string shredder(dump); std::stack surroundingKeys; std::stack expectedIndents; expectedIndents.push(0); while (true) { std::string::size_type newLinePos = shredder.find( '\n' ); if (newLinePos == std::string::npos || newLinePos == 0) break; std::string line = shredder.substr( 0, newLinePos ); shredder = shredder.erase( 0, newLinePos+1 ); std::string::size_type keyPosition = line.find_first_not_of( ' ' ); std::string::size_type colonPosition = line.find( ':' ); std::string key = line.substr(keyPosition, colonPosition - keyPosition); std::string::size_type firstSpacePosition = key.find_first_of(" "); if (firstSpacePosition != std::string::npos) { key.erase(firstSpacePosition); } if ( keyPosition > expectedIndents.top() ) { // more indent than before expectedIndents.push(keyPosition); } else if (keyPosition == expectedIndents.top() ) { if (!surroundingKeys.empty()) { surroundingKeys.pop(); // last of same length } } else { // less indent than before do expectedIndents.pop(); while (expectedIndents.top() != keyPosition); // unwind until current indent is found } if (!surroundingKeys.empty()) { key = surroundingKeys.top() + "." + key; // construct current key name } surroundingKeys.push(key); // this is the new embracing key std::string value = line.substr(colonPosition+1); MITK_DEBUG << " Key: '" << key << "' value '" << value << "'" ; parsedResult[key] = value; // store parsing result } return parsedResult; } diff --git a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.h b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.h index a64b48415c..1a6b664aac 100644 --- a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.h +++ b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.h @@ -1,105 +1,105 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkTestDICOMLoading_h #define mitkTestDICOMLoading_h #include "mitkDicomSeriesReader.h" #include "mitkDICOMTestingExports.h" namespace mitk { class mitkDICOMTesting_EXPORT TestDICOMLoading { public: typedef DicomSeriesReader::StringContainer StringContainer; typedef std::list NodeList; typedef std::list ImageList; TestDICOMLoading(); ImageList LoadFiles( const StringContainer& files, Image::Pointer preLoadedVolume = NULL ); /** \brief Dump relevant image information for later comparison. \sa CompareImageInformationDumps */ std::string DumpImageInformation( const Image* image ); /** \brief Compare two image information dumps. \return true, if dumps are sufficiently equal (see parameters) \sa DumpImageInformation */ bool CompareImageInformationDumps( const std::string& reference, const std::string& test ); private: typedef std::map KeyValueMap; void SetDefaultLocale(); void ResetUserLocale(); std::string ComponentTypeToString( int type ); KeyValueMap ParseDump( const std::string& dump ); bool CompareSpacedValueFields( const std::string& reference, const std::string& test, double eps = mitk::eps ); /** Compress whitespace in string \param pString input string \param pFill replacement whitespace (only whitespace in string after reduction) \param pWhitespace characters handled as whitespace */ std::string reduce(const std::string& pString, const std::string& pFill = " ", const std::string& pWhitespace = " \t"); /** Remove leading and trailing whitespace \param pString input string \param pWhitespace characters handled as whitespace */ std::string trim(const std::string& pString, const std::string& pWhitespace = " \t"); template bool StringToNumber(const std::string& s, T& value) { std::stringstream stream(s); stream >> value; - return !stream.fail(); + return (!stream.fail()) && std::isfinite(value); } const char* m_PreviousCLocale; std::locale m_PreviousCppLocale; }; } #endif diff --git a/Core/Code/Testing/files.cmake b/Core/Code/Testing/files.cmake index 7ef459d238..c0b02836c1 100644 --- a/Core/Code/Testing/files.cmake +++ b/Core/Code/Testing/files.cmake @@ -1,175 +1,176 @@ # tests with no extra command line parameter set(MODULE_TESTS mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkEventTest.cpp mitkFocusManagerTest.cpp mitkGenericPropertyTest.cpp mitkGeometry2DTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp mitkImageEqualTest.cpp mitkImageDataItemTest.cpp #mitkImageMapper2DTest.cpp mitkImageGeneratorTest.cpp mitkBaseDataTest.cpp #mitkImageToItkTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkInteractorTest.cpp #mitkITKThreadingTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp #mitkPipelineSmartPointerCorrectnessTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetInteractorTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp #mitkRegistrationBaseTest.cpp #mitkSegmentationInterpolationTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkStateMachineTest.cpp ##mitkStateMachineContainerTest.cpp ## rewrite test, indirect since no longer exported Bug 14529 mitkStateTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkTransitionTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp #mitkAbstractTransformGeometryTest.cpp mitkStepperTest.cpp itkTotalVariationDenoisingImageFilterTest.cpp mitkRenderingManagerTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkShaderRepositoryTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp + mitkTinyXMLTest.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 mitkDataNodeFactoryTest.cpp #runs on all types of data ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces mitkDataNodeFactoryTest.cpp #runs on all types of data ) # list of images for which the tests are run set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACES binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS #mitkLabeledImageToSurfaceFilterTest.cpp #mitkExternalToolsTest.cpp mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp mitkEventMapperTest.cpp mitkEventConfigTest.cpp mitkNodeDependentPointSetInteractorTest.cpp mitkStateMachineFactoryTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageWriterTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkIOUtilTest.cpp mitkSurfaceVtkMapper3DTest mitkSurfaceVtkMapper3DTexturedSphereTest.cpp mitkSurfaceGLMapper2DColorTest.cpp mitkSurfaceGLMapper2DOpacityTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkLabelOverlay3DRendering2DTest.cpp mitkLabelOverlay3DRendering3DTest.cpp mitkTextOverlay2DRenderingTest.cpp mitkTextOverlay2DLayouterRenderingTest.cpp mitkTextOverlay3DRendering2DTest.cpp mitkTextOverlay3DRendering3DTest.cpp mitkTextOverlay3DColorRenderingTest.cpp mitkVTKRenderWindowSizeTest.cpp ) if (${VTK_MAJOR_VERSION} VERSION_LESS 6) # test can be removed with VTK 6 set(MODULE_TESTS ${MODULE_TESTS} mitkVTKRenderWindowSizeTest.cpp) endif() set(MODULE_RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) # Create an artificial module initializing class for # the usServiceListenerTest.cpp usFunctionGenerateExecutableInit(testdriver_init_file IDENTIFIER ${MODULE_NAME}TestDriver ) # Embed the resources set(testdriver_resources ) usFunctionEmbedResources(testdriver_resources EXECUTABLE_NAME ${MODULE_NAME}TestDriver ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Resources FILES ${MODULE_RESOURCE_FILES} ) set(TEST_CPP_FILES ${testdriver_init_file} ${testdriver_resources}) diff --git a/Core/Code/Testing/mitkImageTimeSelectorTest.cpp b/Core/Code/Testing/mitkImageTimeSelectorTest.cpp index 55bbdefde8..249c9a3514 100644 --- a/Core/Code/Testing/mitkImageTimeSelectorTest.cpp +++ b/Core/Code/Testing/mitkImageTimeSelectorTest.cpp @@ -1,90 +1,120 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImage.h" #include "mitkDataNodeFactory.h" #include "mitkImageTimeSelector.h" +#include "mitkImageGenerator.h" + +#include "mitkTesting.h" +#include "mitkTestingMacros.h" + +#include "mitkIOUtil.h" #include #include -int mitkImageTimeSelectorTest(int argc, char* argv[]) + + +/** Global members common for all subtests */ +namespace { + std::string m_Filename; + mitk::Image::Pointer m_Image; +} // end of anonymous namespace - std::cout << "Loading file: "; - if(argc==0) - { - std::cout<<"no file specified [FAILED]"<SetFileName( argv[1] ); - factory->Update(); - - if(factory->GetNumberOfOutputs()<1) - { - std::cout<<"file could not be loaded [FAILED]"<GetOutput( 0 ); - image = dynamic_cast(node->GetData()); - if(image.IsNull()) - { - std::cout<<"file not an image - test will not be applied [PASSED]"<SetInput(image); - timeSelector->SetTimeNr( 0 ); - timeSelector->UpdateLargestPossibleRegion(); - mitk::Image::Pointer result = timeSelector->GetOutput(); + timeSelector->SetInput(m_Image); - std::cout << "Testing IsInitialized(): "; - if(result->IsInitialized()==false) + // test all timesteps + const unsigned int maxTimeStep = m_Image->GetTimeSteps(); + for( unsigned int t=0; tSetTimeNr(t); + timeSelector->Update(); + + mitk::Image::Pointer currentTimestepImage = timeSelector->GetOutput(); + + std::stringstream ss; + ss << " : Valid image in timestep " << t ; + + MITK_TEST_CONDITION_REQUIRED( currentTimestepImage.IsNotNull() + , ss.str().c_str() ); } - std::cout<<"[PASSED]"<DisconnectPipeline(); - //timeSelector = NULL; - if( result->IsVolumeSet(0) == false) +} + +static void Valid_ImageExpandedByTimestep_ReturnsTrue() +{ + Setup(); + + mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); + + const unsigned int maxTimeStep = m_Image->GetTimeSteps(); + mitk::TimeGeometry* tsg = m_Image->GetTimeGeometry(); + mitk::ProportionalTimeGeometry* ptg = dynamic_cast(tsg); + ptg->Expand(maxTimeStep+1); + ptg->SetTimeStepGeometry( ptg->GetGeometryForTimeStep(0), maxTimeStep ); + + mitk::Image::Pointer expandedImage = mitk::Image::New(); + expandedImage->Initialize( m_Image->GetPixelType(0), *tsg ); + + timeSelector->SetInput(expandedImage); + + for( unsigned int t=0; tSetTimeNr(t); + timeSelector->Update(); + + mitk::Image::Pointer currentTimestepImage = timeSelector->GetOutput(); + + std::stringstream ss; + ss << " : Valid image in timestep " << t ; + + MITK_TEST_CONDITION_REQUIRED( currentTimestepImage.IsNotNull() + , ss.str().c_str() ); } - std::cout<<"[PASSED]"< +#include +#include + + +static const std::string filename = itksys::SystemTools::GetCurrentWorkingDirectory() + "/TinyXMLTest.txt"; +static const std::string elementToStoreAttributeName = "DoubleTest"; +static const std::string attributeToStoreName = "CommaValue"; + +static double calcPrecision(const unsigned int requiredDecimalPlaces) +{ + return pow(10.0, -1.0 * ((double) requiredDecimalPlaces)); +} + +/** + * create a simple xml document which stores the values + * @param valueToWrite value which should be stored + * @return true, if document was successfully created. + */ +static bool Setup(double valueToWrite) +{ + // 1. create simple document + TiXmlDocument document; + TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" ); // TODO what to write here? encoding? etc.... + document.LinkEndChild( decl ); + + TiXmlElement* version = new TiXmlElement("Version"); + version->SetAttribute("Writer", __FILE__ ); + version->SetAttribute("CVSRevision", "$Revision: 17055 $" ); + version->SetAttribute("FileVersion", 1 ); + document.LinkEndChild(version); + + // 2. store one element containing a double value with potentially many after comma digits. + TiXmlElement* vElement = new TiXmlElement( elementToStoreAttributeName ); + vElement->SetDoubleAttribute( attributeToStoreName, valueToWrite ); + document.LinkEndChild(vElement); + + // 3. store in file. + return document.SaveFile( filename ); +} + +static int readValueFromSetupDocument(double& readOutValue) +{ + TiXmlDocument document; + + if (!document.LoadFile(filename)) + { + MITK_TEST_CONDITION_REQUIRED(false, "Test Setup failed, could not open " << filename); + return TIXML_NO_ATTRIBUTE; + } + else + { + TiXmlElement* doubleTest = document.FirstChildElement(elementToStoreAttributeName); + return doubleTest->QueryDoubleAttribute(attributeToStoreName, &readOutValue); + } +} + +/** + * + * @return true if TearDown was successful. + */ +static bool TearDown() +{ + return !remove(filename.c_str()); +} + +static void Test_Setup_works() +{ + MITK_TEST_CONDITION_REQUIRED(Setup(1.0) && TearDown(), + "Test if setup and teardown correctly writes data to " << filename << " and deletes the file after the test"); +} + +/** + * this first test ensures we can correctly readout values from the + * TinyXMLDocument. + */ +static void Test_ReadOutValue_works() +{ + Setup(1.0); + + double readValue; + + MITK_TEST_CONDITION_REQUIRED(TIXML_SUCCESS == readValueFromSetupDocument(readValue), + "checking if readout mechanism works."); +} + + + +static void Test_DoubleValueWriteOut() +{ + const double valueToWrite = -1.123456; + const int validDigitsAfterComma = 6; // indicates the number of valid digits after comma of valueToWrite + const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); + double readValue; + + Setup(valueToWrite); + + readValueFromSetupDocument(readValue); + + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(valueToWrite, readValue, neededPrecision), + std::setprecision(validDigitsAfterComma) << + "Testing if value " << valueToWrite << " equals " << readValue + << " which was retrieved from TinyXML document"); + + TearDown(); +} + +static void Test_DoubleValueWriteOut_manyDecimalPlaces() +{ + const double valueToWrite = -1.12345678910111; + const int validDigitsAfterComma = 14; // indicates the number of valid digits after comma of valueToWrite + const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); + double readValue; + + Setup(valueToWrite); + + readValueFromSetupDocument(readValue); + + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(valueToWrite, readValue, neededPrecision), + std::setprecision(validDigitsAfterComma) << + "Testing if value " << valueToWrite << " equals " << readValue + << " which was retrieved from TinyXML document"); + + TearDown(); +} + + + +int mitkTinyXMLTest(int /* argc */, char* /*argv*/[]) +{ + MITK_TEST_BEGIN("TinyXMLTest"); + + Test_Setup_works(); + Test_ReadOutValue_works(); + Test_DoubleValueWriteOut(); + Test_DoubleValueWriteOut_manyDecimalPlaces(); + + MITK_TEST_END() +} diff --git a/Core/Code/files.cmake b/Core/Code/files.cmake index 3148a08c28..2651e9feaa 100644 --- a/Core/Code/files.cmake +++ b/Core/Code/files.cmake @@ -1,400 +1,408 @@ set(H_FILES Algorithms/itkImportMitkImageContainer.h Algorithms/itkImportMitkImageContainer.txx Algorithms/itkLocalVariationImageFilter.h Algorithms/itkLocalVariationImageFilter.txx Algorithms/itkMITKScalarImageToHistogramGenerator.h Algorithms/itkMITKScalarImageToHistogramGenerator.txx Algorithms/itkTotalVariationDenoisingImageFilter.h Algorithms/itkTotalVariationDenoisingImageFilter.txx Algorithms/itkTotalVariationSingleIterationImageFilter.h Algorithms/itkTotalVariationSingleIterationImageFilter.txx Algorithms/mitkBilateralFilter.h Algorithms/mitkBilateralFilter.cpp Algorithms/mitkInstantiateAccessFunctions.h Algorithms/mitkPixelTypeList.h Algorithms/mitkPPArithmeticDec.h Algorithms/mitkPPArgCount.h Algorithms/mitkPPCat.h Algorithms/mitkPPConfig.h Algorithms/mitkPPControlExprIIf.h Algorithms/mitkPPControlIf.h Algorithms/mitkPPControlIIf.h Algorithms/mitkPPDebugError.h Algorithms/mitkPPDetailAutoRec.h Algorithms/mitkPPDetailDMCAutoRec.h Algorithms/mitkPPExpand.h Algorithms/mitkPPFacilitiesEmpty.h Algorithms/mitkPPFacilitiesExpand.h Algorithms/mitkPPLogicalBool.h Algorithms/mitkPPRepetitionDetailDMCFor.h Algorithms/mitkPPRepetitionDetailEDGFor.h Algorithms/mitkPPRepetitionDetailFor.h Algorithms/mitkPPRepetitionDetailMSVCFor.h Algorithms/mitkPPRepetitionFor.h Algorithms/mitkPPSeqElem.h Algorithms/mitkPPSeqForEach.h Algorithms/mitkPPSeqForEachProduct.h Algorithms/mitkPPSeq.h Algorithms/mitkPPSeqEnum.h Algorithms/mitkPPSeqSize.h Algorithms/mitkPPSeqToTuple.h Algorithms/mitkPPStringize.h Algorithms/mitkPPTupleEat.h Algorithms/mitkPPTupleElem.h Algorithms/mitkPPTupleRem.h Algorithms/mitkClippedSurfaceBoundsCalculator.h Algorithms/mitkExtractSliceFilter.h Algorithms/mitkConvert2Dto3DImageFilter.h Algorithms/mitkPlaneClipping.h Common/mitkExceptionMacro.h Common/mitkServiceBaseObject.h Common/mitkTestingMacros.h Common/mitkTesting.h DataManagement/mitkProportionalTimeGeometry.h DataManagement/mitkTimeGeometry.h DataManagement/mitkImageAccessByItk.h DataManagement/mitkImageCast.h DataManagement/mitkImagePixelAccessor.h DataManagement/mitkImagePixelReadAccessor.h DataManagement/mitkImagePixelWriteAccessor.h DataManagement/mitkImageReadAccessor.h DataManagement/mitkImageWriteAccessor.h DataManagement/mitkITKImageImport.h DataManagement/mitkITKImageImport.txx DataManagement/mitkImageToItk.h DataManagement/mitkImageToItk.txx DataManagement/mitkTimeSlicedGeometry.h # Deprecated, empty for compatibilty reasons. Interactions/mitkEventMapperAddOn.h Interfaces/mitkIDataNodeReader.h Rendering/mitkLocalStorageHandler.h IO/mitkPixelTypeTraits.h ) set(CPP_FILES Algorithms/mitkBaseDataSource.cpp Algorithms/mitkCompareImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkGeometry2DDataToSurfaceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCoreActivator.cpp Controllers/mitkFocusManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkSlicesRotator.cpp Controllers/mitkSlicesSwiveller.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkInteractorCameraController.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataStorage.cpp # DataManagement/mitkDataTree.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataNodeFactory.cpp # DataManagement/mitkDataTreeStorage.cpp DataManagement/mitkDisplayGeometry.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkGeometry2D.cpp DataManagement/mitkGeometry2DData.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkLandmarkBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModeOperation.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStateTransitionOperation.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkVector.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkShaderProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkMaterial.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilters.cpp Interactions/mitkAction.cpp Interactions/mitkAffineInteractor.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCoordinateSupplier.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayPositionEvent.cpp # Interactions/mitkDisplayVectorInteractorLevelWindow.cpp # legacy, prob even now unneeded # Interactions/mitkDisplayVectorInteractorScroll.cpp Interactions/mitkEvent.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventDescription.cpp Interactions/mitkEventFactory.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkEventMapper.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkGlobalInteraction.cpp Interactions/mitkInteractor.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMovePointSetInteractor.cpp Interactions/mitkMoveBaseDataInteractor.cpp Interactions/mitkNodeDepententPointSetInteractor.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkPointSetInteractor.cpp Interactions/mitkPositionEvent.cpp Interactions/mitkPositionTracker.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkState.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateEvent.cpp Interactions/mitkStateMachine.cpp Interactions/mitkStateMachineFactory.cpp Interactions/mitkTransition.cpp Interactions/mitkWheelEvent.cpp Interactions/mitkKeyEvent.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkCrosshairPositionEvent.cpp Interfaces/mitkInteractionEventObserver.cpp Interfaces/mitkIShaderRepository.cpp Interfaces/mitkIPropertyAliases.cpp Interfaces/mitkIPropertyDescriptions.cpp Interfaces/mitkIPropertyExtensions.cpp Interfaces/mitkIPropertyFilters.cpp IO/mitkBaseDataIOFactory.cpp IO/mitkCoreDataNodeReader.cpp IO/mitkDicomSeriesReader.cpp + IO/mitkDicomSR_LoadDICOMScalar.cpp + IO/mitkDicomSR_LoadDICOMScalar4D.cpp + IO/mitkDicomSR_LoadDICOMRGBPixel.cpp + IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp + IO/mitkDicomSR_ImageBlockDescriptor.cpp + IO/mitkDicomSR_GantryTiltInformation.cpp + IO/mitkDicomSR_SliceGroupingResult.cpp + IO/mitkFileReader.cpp IO/mitkFileSeriesReader.cpp IO/mitkFileWriter.cpp # IO/mitkIpPicGet.c IO/mitkImageGenerator.cpp IO/mitkImageWriter.cpp IO/mitkImageWriterFactory.cpp IO/mitkItkImageFileIOFactory.cpp IO/mitkItkImageFileReader.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkItkPictureWrite.cpp IO/mitkIOUtil.cpp IO/mitkLookupTableProperty.cpp IO/mitkOperation.cpp # IO/mitkPicFileIOFactory.cpp # IO/mitkPicFileReader.cpp # IO/mitkPicFileWriter.cpp # IO/mitkPicHelper.cpp # IO/mitkPicVolumeTimeSeriesIOFactory.cpp # IO/mitkPicVolumeTimeSeriesReader.cpp IO/mitkPixelType.cpp IO/mitkPointSetIOFactory.cpp IO/mitkPointSetReader.cpp IO/mitkPointSetWriter.cpp IO/mitkPointSetWriterFactory.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSTLFileIOFactory.cpp IO/mitkSTLFileReader.cpp IO/mitkSurfaceVtkWriter.cpp IO/mitkSurfaceVtkWriterFactory.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkVtiFileIOFactory.cpp IO/mitkVtiFileReader.cpp IO/mitkVtkImageIOFactory.cpp IO/mitkVtkImageReader.cpp IO/mitkVtkSurfaceIOFactory.cpp IO/mitkVtkSurfaceReader.cpp IO/vtkPointSetXMLParser.cpp IO/mitkLog.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkGeometry2DDataMapper2D.cpp Rendering/mitkGeometry2DDataVtkMapper3D.cpp Rendering/mitkGLMapper.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper.cpp Rendering/mitkPointSetGLMapper2D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkPolyDataGLMapper2D.cpp Rendering/mitkSurfaceGLMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVolumeDataVtkMapper3D.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkShaderRepository.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp Rendering/mitkRenderingTestHelper.cpp Rendering/mitkOverlay.cpp Rendering/mitkVtkOverlay.cpp Rendering/mitkVtkOverlay2D.cpp Rendering/mitkVtkOverlay3D.cpp Rendering/mitkOverlayManager.cpp Rendering/mitkAbstractOverlayLayouter.cpp Rendering/mitkTextOverlay2D.cpp Rendering/mitkTextOverlay3D.cpp Rendering/mitkLabelOverlay3D.cpp Rendering/mitkOverlay2DLayouter.cpp Common/mitkException.cpp Common/mitkCommon.h Common/mitkCoreObjectFactoryBase.cpp Common/mitkCoreObjectFactory.cpp Common/mitkCoreServices.cpp ) list(APPEND CPP_FILES ${CppMicroServices_SOURCES}) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml Shaders/mitkShaderLighting.xml mitkLevelWindowPresets.xml ) diff --git a/Core/CppMicroServices/src/util/usUtils.cpp b/Core/CppMicroServices/src/util/usUtils.cpp index 5cfb254075..9149a8c4a9 100644 --- a/Core/CppMicroServices/src/util/usUtils.cpp +++ b/Core/CppMicroServices/src/util/usUtils.cpp @@ -1,270 +1,278 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usUtils_p.h" #include "usModuleInfo.h" #include "usModuleSettings.h" #include +#include #ifdef US_PLATFORM_POSIX #include #include #include #include #else #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include "dirent_win32_p.h" #endif //------------------------------------------------------------------- // Module auto-loading //------------------------------------------------------------------- namespace { - #if !defined(US_PLATFORM_LINUX) std::string library_suffix() { #ifdef US_PLATFORM_WINDOWS return ".dll"; #elif defined(US_PLATFORM_APPLE) return ".dylib"; #else return ".so"; #endif } #endif #ifdef US_PLATFORM_POSIX const char DIR_SEP = '/'; bool load_impl(const std::string& modulePath) { void* handle = dlopen(modulePath.c_str(), RTLD_NOW | RTLD_LOCAL); return (handle != NULL); } #elif defined(US_PLATFORM_WINDOWS) const char DIR_SEP = '\\'; bool load_impl(const std::string& modulePath) { void* handle = LoadLibrary(modulePath.c_str()); return (handle != NULL); } #else #ifdef US_ENABLE_AUTOLOADING_SUPPORT #error "Missing load_impl implementation for this platform." #else bool load_impl(const std::string&) { return false; } #endif #endif - } US_BEGIN_NAMESPACE void AutoLoadModulesFromPath(const std::string& absoluteBasePath, const std::string& subDir) { std::string loadPath = absoluteBasePath + DIR_SEP + subDir; DIR* dir = opendir(loadPath.c_str()); #ifdef CMAKE_INTDIR // Try intermediate output directories if (dir == NULL) { std::size_t indexOfLastSeparator = absoluteBasePath.find_last_of(DIR_SEP); if (indexOfLastSeparator != -1) { - if (absoluteBasePath.substr(indexOfLastSeparator+1) == CMAKE_INTDIR) + std::string intermediateDir = absoluteBasePath.substr(indexOfLastSeparator+1); + bool equalSubDir = intermediateDir.size() == std::strlen(CMAKE_INTDIR); + for (std::size_t i = 0; equalSubDir && i < intermediateDir.size(); ++i) + { + if (std::tolower(intermediateDir[i]) != std::tolower(CMAKE_INTDIR[i])) + { + equalSubDir = false; + } + } + if (equalSubDir) { loadPath = absoluteBasePath.substr(0, indexOfLastSeparator+1) + subDir + DIR_SEP + CMAKE_INTDIR; dir = opendir(loadPath.c_str()); } } } #endif if (dir != NULL) { struct dirent *ent = NULL; while ((ent = readdir(dir)) != NULL) { bool loadFile = true; #ifdef _DIRENT_HAVE_D_TYPE if (ent->d_type != DT_UNKNOWN && ent->d_type != DT_REG) { loadFile = false; } #endif std::string entryFileName(ent->d_name); // On Linux, library file names can have version numbers appended. On other platforms, we // check the file ending. This could be refined for Linux in the future. #if !defined(US_PLATFORM_LINUX) if (entryFileName.rfind(library_suffix()) != (entryFileName.size() - library_suffix().size())) { loadFile = false; } #endif if (!loadFile) continue; std::string libPath = loadPath; if (!libPath.empty() && libPath.find_last_of(DIR_SEP) != libPath.size() -1) { libPath += DIR_SEP; } libPath += entryFileName; US_INFO << "Auto-loading module " << libPath; if (!load_impl(libPath)) { US_WARN << "Auto-loading of module " << libPath << " failed: " << GetLastErrorStr(); } } closedir(dir); } } void AutoLoadModules(const ModuleInfo& moduleInfo) { if (moduleInfo.autoLoadDir.empty()) { return; } ModuleSettings::PathList autoLoadPaths = ModuleSettings::GetAutoLoadPaths(); std::size_t indexOfLastSeparator = moduleInfo.location.find_last_of(DIR_SEP); std::string moduleBasePath = moduleInfo.location.substr(0, indexOfLastSeparator); for (ModuleSettings::PathList::iterator i = autoLoadPaths.begin(); i != autoLoadPaths.end(); ++i) { if (*i == ModuleSettings::CURRENT_MODULE_PATH()) { // Load all modules from a directory located relative to this modules location // and named after this modules library name. *i = moduleBasePath; } } // We could have introduced a duplicate above, so remove it. std::sort(autoLoadPaths.begin(), autoLoadPaths.end()); autoLoadPaths.erase(std::unique(autoLoadPaths.begin(), autoLoadPaths.end()), autoLoadPaths.end()); for (ModuleSettings::PathList::iterator i = autoLoadPaths.begin(); i != autoLoadPaths.end(); ++i) { if (i->empty()) continue; AutoLoadModulesFromPath(*i, moduleInfo.autoLoadDir); } } US_END_NAMESPACE //------------------------------------------------------------------- // Error handling //------------------------------------------------------------------- US_BEGIN_NAMESPACE std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } static MsgHandler handler = 0; MsgHandler installMsgHandler(MsgHandler h) { MsgHandler old = handler; handler = h; return old; } void message_output(MsgType msgType, const char *buf) { if (handler) { (*handler)(msgType, buf); } else { fprintf(stderr, "%s\n", buf); fflush(stderr); } if (msgType == ErrorMsg) { #if defined(_MSC_VER) && !defined(NDEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) // get the current report mode int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); _CrtSetReportMode(_CRT_ERROR, reportMode); int ret = _CrtDbgReport(_CRT_ERROR, __FILE__, __LINE__, CppMicroServices_VERSION_STR, buf); if (ret == 0 && reportMode & _CRTDBG_MODE_WNDW) return; // ignore else if (ret == 1) _CrtDbgBreak(); #endif #ifdef US_PLATFORM_POSIX abort(); // trap; generates core dump #else exit(1); // goodbye cruel world #endif } } US_END_NAMESPACE diff --git a/Documentation/doxygen.conf.in b/Documentation/doxygen.conf.in index 45c62e4b4d..fcf2c9edc8 100644 --- a/Documentation/doxygen.conf.in +++ b/Documentation/doxygen.conf.in @@ -1,1917 +1,1918 @@ # Doxyfile 1.8.0 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = MITK # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @MITK_VERSION_STRING@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Medical Imaging Interaction Toolkit" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = @MITK_DOXYGEN_OUTPUT_DIR@ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = "FIXME=\par Fix Me's:\n" \ "BlueBerry=\if BLUEBERRY" \ "endBlueBerry=\endif" \ "bundlemainpage{1}=\page \1" \ "embmainpage{1}=\page \1" \ "github{2}=\2" \ "deprecatedSince{1}=\xrefitem deprecatedSince\1 \" Deprecated as of \1\" \"Functions deprecated as of \1\" " # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = YES # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = @MITK_DOXYGEN_INTERNAL_DOCS@ # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = @MITK_DOXYGEN_HIDE_FRIEND_COMPOUNDS@ # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = @MITK_DOXYGEN_INTERNAL_DOCS@ # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = YES # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = @MITK_DOXYGEN_GENERATE_TODOLIST@ # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = @MITK_DOXYGEN_GENERATE_BUGLIST@ # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= @MITK_DOXYGEN_GENERATE_DEPRECATEDLIST@ # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = @MITK_DOXYGEN_ENABLED_SECTIONS@ # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 0 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = @MITK_SOURCE_DIR@/Documentation/MITKDoxygenLayout.xml # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @MITK_SOURCE_DIR@ \ @MITK_BINARY_DIR@ \ @MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.h \ *.cpp \ *.dox \ *.md \ *.txx \ *.tpp \ *.cxx \ *.cmake # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = @MITK_SOURCE_DIR@/BlueBerry/Documentation/reference/api/MainPage.dox \ @MITK_SOURCE_DIR@/Utilities/ipFunc/ \ @MITK_SOURCE_DIR@/Utilities/ipSegmentation/ \ @MITK_SOURCE_DIR@/Utilities/KWStyle/ \ @MITK_SOURCE_DIR@/Utilities/Poco/ \ @MITK_SOURCE_DIR@/Utilities/qtsingleapplication/ \ @MITK_SOURCE_DIR@/Applications/PluginGenerator/ \ @MITK_SOURCE_DIR@/Core/CppMicroServices/README.md \ @MITK_SOURCE_DIR@/Core/CppMicroServices/documentation/snippets/ \ @MITK_SOURCE_DIR@/Core/CppMicroServices/documentation/doxygen/standalone/ \ @MITK_SOURCE_DIR@/Core/CppMicroServices/examples/ \ @MITK_SOURCE_DIR@/Core/CppMicroServices/test/ \ @MITK_SOURCE_DIR@/Core/CppMicroServices/src/util/jsoncpp.cpp \ @MITK_SOURCE_DIR@/Deprecated/ \ @MITK_SOURCE_DIR@/Build/ \ @MITK_SOURCE_DIR@/CMake/PackageDepends \ @MITK_SOURCE_DIR@/CMake/QBundleTemplate \ @MITK_SOURCE_DIR@/CMakeExternals \ @MITK_SOURCE_DIR@/Modules/QmitkExt/vtkQtChartHeaders/ \ @MITK_BINARY_DIR@/bin/ \ @MITK_BINARY_DIR@/PT/ \ @MITK_BINARY_DIR@/GP/ \ @MITK_BINARY_DIR@/Core/CppMicroServices/ \ @MITK_BINARY_DIR@/_CPack_Packages/ \ @MITK_DOXYGEN_ADDITIONAL_EXCLUDE_DIRS@ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = moc_* \ ui_* \ qrc_* \ wrap_* \ Register* \ */files.cmake \ */.git/* \ *_p.h \ *Private.* \ */Snippets/* \ */snippets/* \ */testing/* \ */Testing/* \ @MITK_BINARY_DIR@/*.cmake \ @MITK_DOXYGEN_EXCLUDE_PATTERNS@ # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = @MITK_SOURCE_DIR@/Examples/ \ @MITK_SOURCE_DIR@/Examples/Tutorial/ \ @MITK_SOURCE_DIR@/Examples/Plugins/ \ @MITK_SOURCE_DIR@/Examples/QtFreeRender/ \ @MITK_SOURCE_DIR@/Core/Code/ \ - @MITK_SOURCE_DIR@/Core/Code/CppMicroServices/Documentation/Snippets/ \ + @MITK_SOURCE_DIR@/Core/CppMicroServices/Documentation/Snippets/ \ + @MITK_SOURCE_DIR@/Core/CppMicroServices/examples/ \ @MITK_DOXYGEN_OUTPUT_DIR@/html/extension-points/html/ \ @MITK_SOURCE_DIR@/Documentation/Snippets/ \ @MITK_SOURCE_DIR@/Documentation/Doxygen/ExampleCode/ \ @MITK_SOURCE_DIR@/Modules/OpenCL/Documentation/doxygen/snippets/ # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = @MITK_SOURCE_DIR@/Documentation/Doxygen/ \ @MITK_SOURCE_DIR@/Documentation/Doxygen/Modules/ \ @MITK_SOURCE_DIR@/Documentation/Doxygen/Tutorial/ \ @MITK_SOURCE_DIR@ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = *.cmake=@CMakeDoxygenFilter_EXECUTABLE@ # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 3 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = @MITK_DOXYGEN_STYLESHEET@ # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = @MITK_DOXYGEN_HTML_DYNAMIC_SECTIONS@ # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = @MITK_DOXYGEN_GENERATE_QHP@ # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = @MITK_DOXYGEN_QCH_FILE@ # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = "org.mitk" # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = MITK # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = @QT_HELPGENERATOR_EXECUTABLE@ # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 300 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = amssymb # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = itkNotUsed(x)= \ "itkSetMacro(name,type)= virtual void Set##name (type _arg);" \ "itkGetMacro(name,type)= virtual type Get##name ();" \ "itkGetConstMacro(name,type)= virtual type Get##name () const;" \ "itkSetStringMacro(name)= virtual void Set##name (const char* _arg);" \ "itkGetStringMacro(name)= virtual const char* Get##name () const;" \ "itkSetClampMacro(name,type,min,max)= virtual void Set##name (type _arg);" \ "itkSetObjectMacro(name,type)= virtual void Set##name (type* _arg);" \ "itkGetObjectMacro(name,type)= virtual type* Get##name ();" \ "itkSetConstObjectMacro(name,type)= virtual void Set##name ( const type* _arg);" \ "itkGetConstObjectMacro(name,type)= virtual const type* Get##name ();" \ "itkGetConstReferenceMacro(name,type)= virtual const type& Get##name ();" \ "itkGetConstReferenceObjectMacro(name,type)= virtual const type::Pointer& Get##name () const;" \ "itkBooleanMacro(name)= virtual void name##On (); virtual void name##Off ();" \ "itkSetVector2Macro(name,type)= virtual void Set##name (type _arg1, type _arg2) virtual void Set##name (type _arg[2]);" \ "itkGetVector2Macro(name,type)= virtual type* Get##name () const; virtual void Get##name (type& _arg1, type& _arg2) const; virtual void Get##name (type _arg[2]) const;" \ "itkSetVector3Macro(name,type)= virtual void Set##name (type _arg1, type _arg2, type _arg3) virtual void Set##name (type _arg[3]);" \ "itkGetVector3Macro(name,type)= virtual type* Get##name () const; virtual void Get##name (type& _arg1, type& _arg2, type& _arg3) const; virtual void Get##name (type _arg[3]) const;" \ "itkSetVector4Macro(name,type)= virtual void Set##name (type _arg1, type _arg2, type _arg3, type _arg4) virtual void Set##name (type _arg[4]);" \ "itkGetVector4Macro(name,type)= virtual type* Get##name () const; virtual void Get##name (type& _arg1, type& _arg2, type& _arg3, type& _arg4) const; virtual void Get##name (type _arg[4]) const;" \ "itkSetVector6Macro(name,type)= virtual void Set##name (type _arg1, type _arg2, type _arg3, type _arg4, type _arg5, type _arg6) virtual void Set##name (type _arg[6]);" \ "itkGetVector6Macro(name,type)= virtual type* Get##name () const; virtual void Get##name (type& _arg1, type& _arg2, type& _arg3, type& _arg4, type& _arg5, type& _arg6) const; virtual void Get##name (type _arg[6]) const;" \ "itkSetVectorMacro(name,type,count)= virtual void Set##name(type data[]);" \ "itkGetVectorMacro(name,type,count)= virtual type* Get##name () const;" \ "itkNewMacro(type)= static Pointer New();" \ "itkTypeMacro(thisClass,superclass)= virtual const char *GetClassName() const;" \ "itkConceptMacro(name,concept)= enum { name = 0 };" \ "ITK_NUMERIC_LIMITS= std::numeric_limits" \ "ITK_TYPENAME= typename" \ "FEM_ABSTRACT_CLASS(thisClass,parentClass)= public: /** Standard Self typedef.*/ typedef thisClass Self; /** Standard Superclass typedef. */ typedef parentClass Superclass; /** Pointer or SmartPointer to an object. */ typedef Self* Pointer; /** Const pointer or SmartPointer to an object. */ typedef const Self* ConstPointer; private:" \ "FEM_CLASS(thisClass,parentClass)= FEM_ABSTRACT_CLASS(thisClass,parentClass) public: /** Create a new object from the existing one */ virtual Baseclass::Pointer Clone() const; /** Class ID for FEM object factory */ static const int CLID; /** Virtual function to access the class ID */ virtual int ClassID() const { return CLID; } /** Object creation in an itk compatible way */ static Self::Pointer New() { return new Self(); } private:" \ FREEVERSION \ ERROR_CHECKING \ HAS_TIFF \ HAS_JPEG \ HAS_NETLIB \ HAS_PNG \ HAS_ZLIB \ HAS_GLUT \ HAS_QT \ VCL_USE_NATIVE_STL=1 \ VCL_USE_NATIVE_COMPLEX=1 \ VCL_HAS_BOOL=1 \ VXL_BIG_ENDIAN=1 \ VXL_LITTLE_ENDIAN=0 \ VNL_DLL_DATA= \ size_t=vcl_size_t \ "US_PREPEND_NAMESPACE(x)=mitk::x" \ "US_BEGIN_NAMESPACE= namespace mitk {" \ "US_END_NAMESPACE=}" \ "US_BASECLASS_NAME=itk::LightObject" \ US_EXPORT= \ "DEPRECATED(func)=func" # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = @BLUEBERRY_DOXYGEN_TAGFILE@ # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = @MITK_DOXYGEN_TAGFILE_NAME@ # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = NO # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = @HAVE_DOT@ # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = @MITK_DOXYGEN_DOT_NUM_THREADS@ # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = @MITK_DOXYGEN_UML_LOOK@ # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = @DOXYGEN_DOT_PATH@ # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES diff --git a/Examples/Tutorial/Step3.cpp b/Examples/Tutorial/Step3.cpp index 98aedaa4d1..e512e06f12 100644 --- a/Examples/Tutorial/Step3.cpp +++ b/Examples/Tutorial/Step3.cpp @@ -1,171 +1,174 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkRegisterClasses.h" #include "QmitkRenderWindow.h" #include #include #include #include #include #include +#include "mitkCoreExtObjectFactory.h" #include #include //##Documentation //## @brief Change the type of display to 3D //## //## As in Step2, load one or more data sets (many image, surface //## and other formats), but display it in a 3D view. //## The QmitkRenderWindow is now used for displaying a 3D view, by //## setting the used mapper-slot to Standard3D. //## Since volume-rendering is a (rather) slow procedure, the default //## is that images are not displayed in the 3D view. For this example, //## we want volume-rendering, thus we switch it on by setting //## the Boolean-property "volumerendering" to "true". int main(int argc, char* argv[]) { QApplication qtapplication( argc, argv ); if(argc<2) { fprintf( stderr, "Usage: %s [filename1] [filename2] ...\n\n", itksys::SystemTools::GetFilenameName(argv[0]).c_str() ); return 1; } + // Register MitkExt Factory to use new volumerendering + RegisterCoreExtObjectFactory(); // Register Qmitk-dependent global instances QmitkRegisterClasses(); //************************************************************************* // Part I: Basic initialization //************************************************************************* // Create a DataStorage mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); //************************************************************************* // Part II: Create some data by reading files //************************************************************************* int i; for(i=1; iSetFileName(filename); nodeReader->Update(); //********************************************************************* // Part III: Put the data into the datastorage //********************************************************************* // Since the DataNodeFactory directly creates a node, // use the datastorage to add the read node mitk::DataNode::Pointer node = nodeReader->GetOutput(); ds->Add(node); // ********************************************************* // ****************** START OF NEW PART 1 ****************** // ********************************************************* //********************************************************************* // Part IV: We want all images to be volume-rendered //********************************************************************* // Check if the data is an image by dynamic_cast-ing the data // contained in the node. Warning: dynamic_cast's are rather slow, // do not use it too often! mitk::Image::Pointer image = dynamic_cast(node->GetData()); if(image.IsNotNull()) { // Set the property "volumerendering" to the Boolean value "true" node->SetProperty("volumerendering", mitk::BoolProperty::New(true)); // Create a transfer function to assign optical properties (color and opacity) to grey-values of the data mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); tf->InitializeByMitkImage ( image ); // Set the color transfer function AddRGBPoint(double x, double r, double g, double b) tf->GetColorTransferFunction()->AddRGBPoint ( tf->GetColorTransferFunction()->GetRange() [0], 1.0, 0.0, 0.0 ); tf->GetColorTransferFunction()->AddRGBPoint ( tf->GetColorTransferFunction()->GetRange() [1], 1.0, 1.0, 0.0 ); // Set the piecewise opacity transfer function AddPoint(double x, double y) tf->GetScalarOpacityFunction()->AddPoint ( 0, 0 ); tf->GetScalarOpacityFunction()->AddPoint ( tf->GetColorTransferFunction()->GetRange() [1], 1 ); node->SetProperty ( "TransferFunction", mitk::TransferFunctionProperty::New ( tf.GetPointer() ) ); } // ********************************************************* // ******************* END OF NEW PART 1 ******************* // ********************************************************* } catch(...) { fprintf( stderr, "Could not open file %s \n\n", filename ); exit(2); } } //************************************************************************* // Part V: Create window and pass the tree to it //************************************************************************* // Create a renderwindow QmitkRenderWindow renderWindow; // Tell the renderwindow which (part of) the datastorage to render renderWindow.GetRenderer()->SetDataStorage(ds); // ********************************************************* // ****************** START OF NEW PART 2 ****************** // ********************************************************* // Use it as a 3D view! renderWindow.GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); // ********************************************************* // ******************* END OF NEW PART 2 ******************* // ********************************************************* //************************************************************************* // Part VI: Qt-specific initialization //************************************************************************* renderWindow.show(); renderWindow.resize( 256, 256 ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // for testing #include "QtTesting.h" if(strcmp(argv[argc-1], "-testing")!=0) return qtapplication.exec(); else return QtTesting(); } /** \example Step3.cpp */ diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.cpp b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.cpp index fcc2534540..0421c4beda 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2D.cpp @@ -1,104 +1,106 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelGLMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkContourModel.h" #include "mitkContourModelSubDivisionFilter.h" #include #include "mitkGL.h" mitk::ContourModelGLMapper2D::ContourModelGLMapper2D() : m_SubdivisionContour(mitk::ContourModel::New()), m_InitSubdivisionCurve(true) { } mitk::ContourModelGLMapper2D::~ContourModelGLMapper2D() { } void mitk::ContourModelGLMapper2D::Paint(mitk::BaseRenderer * renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); mitk::DataNode* dataNode = this->GetDataNode(); bool visible = true; dataNode->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; mitk::ContourModel* input = this->GetInput(); mitk::ContourModel::Pointer renderingContour = input; bool subdivision = false; dataNode->GetBoolProperty( "subdivision curve", subdivision, renderer ); if (subdivision) { if(this->m_SubdivisionContour->GetMTime() < renderingContour->GetMTime() || m_InitSubdivisionCurve) { //mitk::ContourModel::Pointer subdivContour = mitk::ContourModel::New(); mitk::ContourModelSubDivisionFilter::Pointer subdivFilter = mitk::ContourModelSubDivisionFilter::New(); subdivFilter->SetInput(input); subdivFilter->Update(); this->m_SubdivisionContour = subdivFilter->GetOutput(); m_InitSubdivisionCurve = false; } renderingContour = this->m_SubdivisionContour; } this->DrawContour(renderingContour, renderer); ls->UpdateGenerateDataTime(); } mitk::ContourModel* mitk::ContourModelGLMapper2D::GetInput(void) { return const_cast< mitk::ContourModel* >(static_cast< const mitk::ContourModel* > ( GetDataNode()->GetData() )); } void mitk::ContourModelGLMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "contour.color", ColorProperty::New(0.9, 1.0, 0.1), renderer, overwrite ); node->AddProperty( "contour.points.color", ColorProperty::New(1.0, 0.0, 0.1), renderer, overwrite ); node->AddProperty( "contour.points.show", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.segments.show", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty( "contour.controlpoints.show", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); node->AddProperty( "contour.hovering.width", mitk::FloatProperty::New( 3.0 ), renderer, overwrite ); node->AddProperty( "contour.hovering", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.points.text", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.controlpoints.text", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "subdivision curve", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.project-onto-plane", mitk::BoolProperty::New( false ), renderer, overwrite ); + node->AddProperty( "opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite ); + Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp index dd644e1908..54f76fa1c2 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp @@ -1,327 +1,329 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelSetGLMapper2D.h" #include "mitkPlaneGeometry.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkContourModelSet.h" #include #include "mitkGL.h" mitk::ContourModelGLMapper2DBase::ContourModelGLMapper2DBase() { } mitk::ContourModelGLMapper2DBase::~ContourModelGLMapper2DBase() { } void mitk::ContourModelGLMapper2DBase::DrawContour(mitk::ContourModel* renderingContour, mitk::BaseRenderer* renderer) { if(!renderingContour) return; mitk::DataNode* dataNode = this->GetDataNode(); renderingContour->UpdateOutputInformation(); unsigned int timestep = renderer->GetTimeStep(); if ( !renderingContour->IsEmptyTimeStep(timestep) ) { mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert(displayGeometry.IsNotNull()); //apply color and opacity read from the PropertyList ApplyProperties(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast(dataNode->GetProperty("contour.color", renderer)); + float opacity = 0.5; + dataNode->GetFloatProperty("opacity", opacity, renderer); if(colorprop) { //set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); - glColor4f(red,green,blue,0.5); + glColor4f(red, green, blue, opacity); } mitk::ColorProperty::Pointer selectedcolor = dynamic_cast(dataNode->GetProperty("contour.points.color", renderer)); if(!selectedcolor) { selectedcolor = mitk::ColorProperty::New(1.0,0.0,0.1); } vtkLinearTransform* transform = dataNode->GetVtkTransform(); // ContourModel::OutputType point; mitk::Point3D point; mitk::Point3D p, projected_p; float vtkp[3]; float lineWidth = 3.0; bool drawit=false; bool isHovering = false; dataNode->GetBoolProperty("contour.hovering", isHovering); if (isHovering) dataNode->GetFloatProperty("contour.hovering.width", lineWidth); else dataNode->GetFloatProperty("contour.width", lineWidth); bool showSegments = false; dataNode->GetBoolProperty("contour.segments.show", showSegments); bool showControlPoints = false; dataNode->GetBoolProperty("contour.controlpoints.show", showControlPoints); bool showPoints = false; dataNode->GetBoolProperty("contour.points.show", showPoints); bool showPointsNumbers = false; dataNode->GetBoolProperty("contour.points.text", showPointsNumbers); bool showControlPointsNumbers = false; dataNode->GetBoolProperty("contour.controlpoints.text", showControlPointsNumbers); bool projectmode=false; dataNode->GetVisibility(projectmode, renderer, "contour.project-onto-plane"); mitk::ContourModel::VertexIterator pointsIt = renderingContour->IteratorBegin(timestep); Point2D pt2d; // projected_p in display coordinates Point2D lastPt2d; int index = 0; mitk::ScalarType maxDiff = 0.25; while ( pointsIt != renderingContour->IteratorEnd(timestep) ) { lastPt2d = pt2d; point = (*pointsIt)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector3D diff=p-projected_p; ScalarType scalardiff = diff.GetNorm(); //project to plane if(projectmode) { drawit=true; } else if(scalardiffIteratorBegin(timestep)) ) { glLineWidth(lineWidth); glBegin (GL_LINES); glVertex2f(pt2d[0], pt2d[1]); glVertex2f(lastPt2d[0], lastPt2d[1]); glEnd(); glLineWidth(1); } } if (showControlPoints) { //draw ontrol points if ((*pointsIt)->IsControlPoint) { float pointsize = 4; Point2D tmp; Vector2D horz,vert; horz[1]=0; vert[0]=0; horz[0]=pointsize; vert[1]=pointsize; glColor3f(selectedcolor->GetColor().GetRed(), selectedcolor->GetColor().GetBlue(), selectedcolor->GetColor().GetGreen()); glLineWidth(1); //a rectangle around the point with the selected color glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); glEnd(); glLineWidth(1); //the actual point in the specified color to see the usual color of the point glColor3f(colorprop->GetColor().GetRed(),colorprop->GetColor().GetGreen(),colorprop->GetColor().GetBlue()); glPointSize(1); glBegin (GL_POINTS); tmp=pt2d; glVertex2dv(&tmp[0]); glEnd (); } } if (showPoints) { float pointsize = 3; Point2D tmp; Vector2D horz,vert; horz[1]=0; vert[0]=0; horz[0]=pointsize; vert[1]=pointsize; glColor3f(0.0, 0.0, 0.0); glLineWidth(1); //a rectangle around the point with the selected color glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); glEnd(); glLineWidth(1); //the actual point in the specified color to see the usual color of the point glColor3f(colorprop->GetColor().GetRed(),colorprop->GetColor().GetGreen(),colorprop->GetColor().GetBlue()); glPointSize(1); glBegin (GL_POINTS); tmp=pt2d; glVertex2dv(&tmp[0]); glEnd (); } if (showPointsNumbers) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); float rgb[3]; rgb[0] = 0.0; rgb[1] = 0.0; rgb[2] = 0.0; OpenGLrenderer->WriteSimpleText(l, pt2d[0] + 2, pt2d[1] + 2,rgb[0], rgb[1],rgb[2]); } if (showControlPointsNumbers && (*pointsIt)->IsControlPoint) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); float rgb[3]; rgb[0] = 1.0; rgb[1] = 1.0; rgb[2] = 0.0; OpenGLrenderer->WriteSimpleText(l, pt2d[0] + 2, pt2d[1] + 2,rgb[0], rgb[1],rgb[2]); } index++; } pointsIt++; }//end while iterate over controlpoints //close contour if necessary if(renderingContour->IsClosed(timestep) && drawit && showSegments) { lastPt2d = pt2d; point = renderingContour->GetVertexAt(0,timestep)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); glLineWidth(lineWidth); glBegin (GL_LINES); glVertex2f(lastPt2d[0], lastPt2d[1]); glVertex2f( pt2d[0], pt2d[1] ); glEnd(); glLineWidth(1); } //draw selected vertex if exists if(renderingContour->GetSelectedVertex()) { //transform selected vertex point = renderingContour->GetSelectedVertex()->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector3D diff=p-projected_p; ScalarType scalardiff = diff.GetNorm(); //---------------------------------- //draw point if close to plane if(scalardiff #include "mitkGL.h" mitk::ContourModelSetGLMapper2D::ContourModelSetGLMapper2D() { } mitk::ContourModelSetGLMapper2D::~ContourModelSetGLMapper2D() { } void mitk::ContourModelSetGLMapper2D::Paint(mitk::BaseRenderer * renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); mitk::DataNode* dataNode = this->GetDataNode(); bool visible = true; dataNode->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; mitk::ContourModelSet* input = this->GetInput(); mitk::ContourModelSet::ContourModelSetIterator it = input->Begin(); mitk::ContourModelSet::ContourModelSetIterator end = input->End(); while(it!=end) { this->DrawContour(it->GetPointer(), renderer); ++it; } if(input->GetSize() < 1) return; ls->UpdateGenerateDataTime(); } mitk::ContourModelSet* mitk::ContourModelSetGLMapper2D::GetInput(void) { return const_cast(static_cast ( GetDataNode()->GetData() )); } void mitk::ContourModelSetGLMapper2D::DrawContour(mitk::ContourModel* renderingContour, mitk::BaseRenderer* renderer) { if(!renderingContour) return; mitk::DataNode* dataNode = this->GetDataNode(); renderingContour->UpdateOutputInformation(); unsigned int timestep = renderer->GetTimeStep(); if ( !renderingContour->IsEmptyTimeStep(timestep) ) { mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert(displayGeometry.IsNotNull()); //apply color and opacity read from the PropertyList ApplyProperties(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast(dataNode->GetProperty("contour.color", renderer)); + float opacity = 0.5; + dataNode->GetFloatProperty("opacity", opacity, renderer); if(colorprop) { //set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); - glColor4f(red,green,blue,0.5); + glColor4f(red, green, blue, opacity); } mitk::ColorProperty::Pointer selectedcolor = dynamic_cast(dataNode->GetProperty("contour.points.color", renderer)); if(!selectedcolor) { selectedcolor = mitk::ColorProperty::New(1.0,0.0,0.1); } vtkLinearTransform* transform = dataNode->GetVtkTransform(); // ContourModel::OutputType point; mitk::Point3D point; mitk::Point3D p, projected_p; float vtkp[3]; float lineWidth = 3.0; bool drawit=false; bool isHovering = false; dataNode->GetBoolProperty("contour.hovering", isHovering); if (isHovering) dataNode->GetFloatProperty("contour.hovering.width", lineWidth); else dataNode->GetFloatProperty("contour.width", lineWidth); bool showSegments = false; dataNode->GetBoolProperty("contour.segments.show", showSegments); bool showControlPoints = false; dataNode->GetBoolProperty("contour.controlpoints.show", showControlPoints); bool showPoints = false; dataNode->GetBoolProperty("contour.points.show", showPoints); bool showPointsNumbers = false; dataNode->GetBoolProperty("contour.points.text", showPointsNumbers); bool showControlPointsNumbers = false; dataNode->GetBoolProperty("contour.controlpoints.text", showControlPointsNumbers); bool projectmode=false; dataNode->GetVisibility(projectmode, renderer, "contour.project-onto-plane"); mitk::ContourModel::VertexIterator pointsIt = renderingContour->IteratorBegin(timestep); Point2D pt2d; // projected_p in display coordinates Point2D lastPt2d; int index = 0; mitk::ScalarType maxDiff = 0.25; while ( pointsIt != renderingContour->IteratorEnd(timestep) ) { lastPt2d = pt2d; point = (*pointsIt)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector3D diff=p-projected_p; ScalarType scalardiff = diff.GetNorm(); //project to plane if(projectmode) { drawit=true; } else if(scalardiffIteratorBegin(timestep)) ) { glLineWidth(lineWidth); glBegin (GL_LINES); glVertex2f(pt2d[0], pt2d[1]); glVertex2f(lastPt2d[0], lastPt2d[1]); glEnd(); glLineWidth(1); } } if (showControlPoints) { //draw ontrol points if ((*pointsIt)->IsControlPoint) { float pointsize = 4; Point2D tmp; Vector2D horz,vert; horz[1]=0; vert[0]=0; horz[0]=pointsize; vert[1]=pointsize; glColor3f(selectedcolor->GetColor().GetRed(), selectedcolor->GetColor().GetBlue(), selectedcolor->GetColor().GetGreen()); glLineWidth(1); //a rectangle around the point with the selected color glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); glEnd(); glLineWidth(1); //the actual point in the specified color to see the usual color of the point glColor3f(colorprop->GetColor().GetRed(),colorprop->GetColor().GetGreen(),colorprop->GetColor().GetBlue()); glPointSize(1); glBegin (GL_POINTS); tmp=pt2d; glVertex2dv(&tmp[0]); glEnd (); } } if (showPoints) { float pointsize = 3; Point2D tmp; Vector2D horz,vert; horz[1]=0; vert[0]=0; horz[0]=pointsize; vert[1]=pointsize; glColor3f(0.0, 0.0, 0.0); glLineWidth(1); //a rectangle around the point with the selected color glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2dv(&tmp[0]); tmp=pt2d+vert; glVertex2dv(&tmp[0]); tmp=pt2d+horz; glVertex2dv(&tmp[0]); tmp=pt2d-vert; glVertex2dv(&tmp[0]); glEnd(); glLineWidth(1); //the actual point in the specified color to see the usual color of the point glColor3f(colorprop->GetColor().GetRed(),colorprop->GetColor().GetGreen(),colorprop->GetColor().GetBlue()); glPointSize(1); glBegin (GL_POINTS); tmp=pt2d; glVertex2dv(&tmp[0]); glEnd (); } if (showPointsNumbers) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); float rgb[3]; rgb[0] = 0.0; rgb[1] = 0.0; rgb[2] = 0.0; OpenGLrenderer->WriteSimpleText(l, pt2d[0] + 2, pt2d[1] + 2,rgb[0], rgb[1],rgb[2]); } if (showControlPointsNumbers && (*pointsIt)->IsControlPoint) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); float rgb[3]; rgb[0] = 1.0; rgb[1] = 1.0; rgb[2] = 0.0; OpenGLrenderer->WriteSimpleText(l, pt2d[0] + 2, pt2d[1] + 2,rgb[0], rgb[1],rgb[2]); } index++; } pointsIt++; }//end while iterate over controlpoints //close contour if necessary if(renderingContour->IsClosed(timestep) && drawit && showSegments) { lastPt2d = pt2d; point = renderingContour->GetVertexAt(0,timestep)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); glLineWidth(lineWidth); glBegin (GL_LINES); glVertex2f(lastPt2d[0], lastPt2d[1]); glVertex2f( pt2d[0], pt2d[1] ); glEnd(); glLineWidth(1); } //draw selected vertex if exists if(renderingContour->GetSelectedVertex()) { //transform selected vertex point = renderingContour->GetSelectedVertex()->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector3D diff=p-projected_p; ScalarType scalardiff = diff.GetNorm(); //---------------------------------- //draw point if close to plane if(scalardiffAddProperty( "contour.color", ColorProperty::New(0.9, 1.0, 0.1), renderer, overwrite ); node->AddProperty( "contour.points.color", ColorProperty::New(1.0, 0.0, 0.1), renderer, overwrite ); node->AddProperty( "contour.points.show", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.segments.show", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty( "contour.controlpoints.show", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); node->AddProperty( "contour.hovering.width", mitk::FloatProperty::New( 3.0 ), renderer, overwrite ); node->AddProperty( "contour.hovering", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.points.text", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.controlpoints.text", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.project-onto-plane", mitk::BoolProperty::New( false ), renderer, overwrite ); + node->AddProperty( "opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite ); + Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h b/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h index d89cd634ff..85851b1b14 100644 --- a/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h +++ b/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h @@ -1,203 +1,229 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _itk_TensorReconstructionWithEigenvalueCorrectionFilter_h_ #define _itk_TensorReconstructionWithEigenvalueCorrectionFilter_h_ #include "itkImageToImageFilter.h" #include #include #include #include namespace itk { template class TensorReconstructionWithEigenvalueCorrectionFilter : public ImageToImageFilter< itk::Image< TDiffusionPixelType, 3 >, itk::Image,3> > { public: typedef itk::VectorImage ImageType; typedef TensorReconstructionWithEigenvalueCorrectionFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageToImageFilter< Image< TDiffusionPixelType, 3>, Image< DiffusionTensor3D< TTensorPixelType >, 3 > > Superclass; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Runtime information support. */ itkTypeMacro(TensorReconstructionWithEigenvalueCorrectionFilter, ImageToImageFilter) typedef TDiffusionPixelType GradientPixelType; typedef DiffusionTensor3D< TTensorPixelType > TensorPixelType; typedef Image< TensorPixelType, 3 > TensorImageType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; /** An alternative typedef defining one (of the many) gradient images. * It will be assumed that the vectorImage has the same dimension as the * Reference image and a vector length parameter of \c n (number of * gradient directions) */ typedef VectorImage< GradientPixelType, 3 > GradientImagesType; typedef typename GradientImagesType::PixelType GradientVectorType; /** Holds each magnetic field gradient used to acquire one DWImage */ typedef vnl_vector_fixed< double, 3 > GradientDirectionType; /** Container to hold gradient directions of the 'n' DW measurements */ typedef VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; /** Another set method to add a gradient directions and its corresponding * image. The image here is a VectorImage. The user is expected to pass the * gradient directions in a container. The ith element of the container * corresponds to the gradient direction of the ith component image the * VectorImage. For the baseline image, a vector of all zeros * should be set. */ void SetGradientImage( GradientDirectionContainerType *, const GradientImagesType *image); itkSetMacro( BValue, TTensorPixelType) - itkSetMacro( B0Threshold, float) + /** Set the b0 threshold */ + itkSetMacro( B0Threshold, double) + + /** Get the pseudeInverse that was calculated in the process of tensor estimation */ itkGetMacro(PseudoInverse, vnl_matrix) + + /** FIXME: added by Sebastian Wirkert due to compile error otherwise. */ + itkGetMacro(CorrectedDiffusionVolumes, ImageType::Pointer) + + /** Outputs the design matrix that was calculated in the process of tensor estimation */ itkGetMacro(H, vnl_matrix) + + /** Outputs the normalized b-vector */ itkGetMacro(BVec, vnl_vector) + + /** Outputs the mask created by thresholding the B0 weighted volume*/ itkGetMacro(B0Mask, vnl_vector) - ImageType::Pointer GetCorrectedDiffusionVolumes() + /** Returns the dwi image volumes with smoothed voxels on positions were there were negative eigenvalues in the tensir*/ + ImageType::Pointer GetGradientImagePointer() { - return m_CorrectedDiffusionVolumes; + return m_GradientImagePointer; } + /** Get the mask image*/ itk::Image::Pointer GetMask() { return m_MaskImage; } protected: TensorReconstructionWithEigenvalueCorrectionFilter(); ~TensorReconstructionWithEigenvalueCorrectionFilter() {} void GenerateData(); typedef enum { GradientIsInASingleImage = 1, GradientIsInManyImages, Else } GradientImageTypeEnumeration; private: - double CheckNeighbours(int x, int y, int z,int f, itk::Size<3> size, itk::Image ::Pointer mask,itk::VectorImage::Pointer corrected_diffusion_temp); + double CheckNeighbours(int x, int y, int z,int f, itk::Size<3> size, itk::Image ::Pointer mask,typename GradientImagesType::Pointer corrected_diffusion_temp); + /** Calculates the attenuation for a voxel*/ void CalculateAttenuation(vnl_vector org_data, vnl_vector &atten,int nof,int numberb0); - void CorrectDiffusionImage(int nof,int numberb0,itk::Size<3> size,itk::VectorImage::Pointer corrected_diffusion,itk::Image::Pointer mask,vnl_vector< double> pixel_max,vnl_vector< double> pixel_min); - void GenerateTensorImage(int nof,int numberb0,itk::Size<3> size,itk::VectorImage::Pointer corrected_diffusion,itk::Image::Pointer mask,double what_mask,itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg ); + /** Correct the diffusion data set for voxels that contain negative eigenvalues in the tensor*/ + void CorrectDiffusionImage(int nof,int numberb0,itk::Size<3> size,typename GradientImagesType::Pointer corrected_diffusion,itk::Image::Pointer mask,vnl_vector< double> pixel_max,vnl_vector< double> pixel_min); - void DeepCopyTensorImage(itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg, itk::Image< itk::DiffusionTensor3D, 3 >::Pointer temp_tensorImg); + /** Calculte a tensor iamge from a diffusion data set*/ + void GenerateTensorImage(int nof,int numberb0,itk::Size<3> size,itk::VectorImage::Pointer corrected_diffusion,itk::Image::Pointer mask,double what_mask, typename itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg ); - void DeepCopyDiffusionImage(itk::VectorImage::Pointer corrected_diffusion, itk::VectorImage::Pointer corrected_diffusion_temp,int nof); + //void DeepCopyTensorImage(itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg, itk::Image< itk::DiffusionTensor3D, 3 >::Pointer temp_tensorImg); + + //void DeepCopyDiffusionImage(itk::VectorImage::Pointer corrected_diffusion, itk::VectorImage::Pointer corrected_diffusion_temp,int nof); void TurnMask( itk::Size<3> size, itk::Image::Pointer mask, double previous_mask, double set_mask); - double CheckNegatives ( itk::Size<3> size, itk::Image::Pointer mask, itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg ); + double CheckNegatives ( itk::Size<3> size, itk::Image::Pointer mask, typename itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg ); /** Gradient image was specified in a single image or in multiple images */ GradientImageTypeEnumeration m_GradientImageTypeEnumeration; /** Number of gradient measurements */ unsigned int m_NumberOfGradientDirections; /** container to hold gradient directions */ GradientDirectionContainerType::Pointer m_GradientDirectionContainer; /** b-value */ TTensorPixelType m_BValue; /** Number of baseline images */ unsigned int m_NumberOfBaselineImages; ImageType::Pointer m_CorrectedDiffusionVolumes; - float m_B0Threshold; + double m_B0Threshold; + /** decodes what is to be done with every single voxel */ itk::Image::Pointer m_MaskImage; + + /** Pseudoinverse calculated in the process of calculating a tensor */ vnl_matrix m_PseudoInverse; + + /** design matrix */ vnl_matrix m_H; + + /** normalized b-vector */ vnl_vector m_BVec; /** m_B0Mask indicates whether a volume identified by an index is B0-weighted or not */ vnl_vector m_B0Mask; + /** volumes of the diffusion data set */ typename GradientImagesType::Pointer m_GradientImagePointer; }; } // end of namespace #ifndef ITK_MANUAL_INSTANTIATION #include "itkTensorReconstructionWithEigenvalueCorrectionFilter.txx" #endif #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.txx b/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.txx index 3023cb57ca..1358bb5271 100644 --- a/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.txx +++ b/Modules/DiffusionImaging/DiffusionCore/Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.txx @@ -1,974 +1,1125 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _itk_TensorReconstructionWithEigenvalueCorrectionFilter_txx_ #define _itk_TensorReconstructionWithEigenvalueCorrectioFiltern_txx_ #endif #include "itkImageRegionConstIterator.h" #include #include "itkImageFileWriter.h" #include "itkImage.h" #include "itkImageRegionIterator.h" namespace itk { template TensorReconstructionWithEigenvalueCorrectionFilter ::TensorReconstructionWithEigenvalueCorrectionFilter() { m_B0Threshold = 50.0; } template void TensorReconstructionWithEigenvalueCorrectionFilter ::GenerateData () { m_GradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); typename GradientImagesType::SizeType size = m_GradientImagePointer->GetLargestPossibleRegion().GetSize(); // number of volumes int nof = m_GradientDirectionContainer->Size(); // determine the number of b-zero values int numberb0=0; for(int i=0; i vec = m_GradientDirectionContainer->ElementAt(i); // due to roundings, the values are not always exactly zero if(vec[0]<0.0001 && vec[1]<0.0001 && vec[2]<0.0001 && vec[0]>-0.0001&& vec[1]>-0.0001 && vec[2]>-0.0001) { numberb0++; } } // Matrix to store all diffusion encoding gradients vnl_matrix directions(nof-numberb0,3); m_B0Mask.set_size(nof); int cnt=0; for(int i=0; i vec = m_GradientDirectionContainer->ElementAt(i); - if(vec[0]<0.0001 && vec[1]<0.0001 && vec[2]<0.0001 && vec[0]>-0.001&& vec[1]>-0.001 && vec[2]>-0.001) + if(vec[0]<0.0001 && vec[1]<0.0001 && vec[2]<0.0001 && vec[0]>-0.0001&& vec[1]>-0.0001 && vec[2]>-0.0001) { // the diffusion encoding gradient is approximately zero, wo we are dealing with a non-diffusion weighted volume m_B0Mask[i]=1; } else { // dealing with a diffusion weighted volume m_B0Mask[i]=0; // set the diffusion encoding gradient to the directions matrix directions[cnt][0] = vec[0]; directions[cnt][1] = vec[1]; directions[cnt][2] = vec[2]; cnt++; } } // looking for maximal norm among gradients. // The norm is calculated with use of spectral radius theorem- based on determination of eigenvalue. vnl_matrix dirsTimesDirsTrans = directions*directions.transpose(); vnl_vector< double> diagonal(nof-numberb0); vnl_vector< double> b_vec(nof-numberb0); vnl_vector< double> temporary(3); for (int i=0;i H(nof-numberb0, 6); vnl_matrix H_org(nof-numberb0, 6); vnl_vector pre_tensor(9); //H is matrix that containes covariances for directions. It is stored twice because its original value is needed later // while H is changed int etbt[6] = { 0, 4, 8, 1, 5, 2 };// tensor order for (int i = 0; i < nof-numberb0; i++) { for (int j = 0; j < 3; j++) { temporary[j] = -directions[i][j]; } for (int j = 0; j < 3; j++) { for (int k = 0; k < 3; k++) { pre_tensor[k + 3 * j] = temporary[k] * directions[i][j]; } } for (int j = 0; j < 6; j++) { H[i][j] = pre_tensor[etbt[j]]; } for (int j = 0; j < 3; j++) { H[i][3 + j] *= 2.0; } } H_org=H; // calculation of inverse matrix by means of pseudoinverse vnl_matrix inputtopseudoinverse=H.transpose()*H; vnl_symmetric_eigensystem eig( inputtopseudoinverse); vnl_matrix pseudoInverse = eig.pinverse()*H.transpose(); - - ImageType::Pointer corrected_diffusion_temp = ImageType::New(); - - typedef itk::VariableLengthVector VariableVectorType; - VariableVectorType variableLengthVector; - variableLengthVector.SetSize(nof); - - - typedef itk::VariableLengthVector VariableVectorType; - VariableVectorType corrected_single; - corrected_single.SetSize(nof-1); - - typedef itk::Image MaskImageType; MaskImageType::Pointer mask = MaskImageType::New(); mask->SetRegions(m_GradientImagePointer->GetLargestPossibleRegion().GetSize()); mask->SetSpacing(m_GradientImagePointer->GetSpacing()); mask->SetOrigin(m_GradientImagePointer->GetOrigin()); mask->Allocate(); // Image thresholding: For every voxel mean B0 image is calculated and then voxels of mean B0 less than the // treshold on the B0 image proviced by the userare excluded from the dataset with use of defined mask image. // 1 in mask voxel means that B0 > assumed treshold. int mask_cnt=0; for(int x=0;x ix = {{x,y,z}}; GradientVectorType pixel = m_GradientImagePointer->GetPixel(ix); for (int i=0;i m_B0Threshold) { mask->SetPixel(ix, 1); mask_cnt++; } else { mask->SetPixel(ix, 0); } } } } + //14.10.2013 + /* //create a copy of the original image- it is then used in pre-processing methods m_CorrectedDiffusionVolumes = ImageType::New(); m_CorrectedDiffusionVolumes->SetRegions(size); m_CorrectedDiffusionVolumes->SetSpacing(m_GradientImagePointer->GetSpacing()); m_CorrectedDiffusionVolumes->SetOrigin(m_GradientImagePointer->GetOrigin()); m_CorrectedDiffusionVolumes->SetVectorLength(nof); m_CorrectedDiffusionVolumes->Allocate(); + for ( int x=0;x ix = {{x,y,z}}; GradientVectorType p = m_GradientImagePointer->GetPixel(ix); m_CorrectedDiffusionVolumes->SetPixel(ix,p); } } } + */ + //Sometimes the gradient voxels may contain negative values ( even if B0 voxel is > = 50 ). This must be corrected by smoothing DWI //Smoothing is done by aproximation of negative voxel value by its correct ( positive) 27-th neighborhood. + // typename TensorImageType::Pointer someimg; + // someimg = TensorImageType::New(); + + + double mask_val=0.0; - vnl_vector org_vec(nof-numberb0); + vnl_vector org_vec(nof); int counter_corrected =0; for ( int x=0;x ix = {{x,y,z}}; + itk::Index<3> ix = {x,y,z}; mask_val = mask->GetPixel(ix); - GradientVectorType pixel2 = m_CorrectedDiffusionVolumes->GetPixel(ix); + + //14.10.2013 + //GradientVectorType pixel2 = m_CorrectedDiffusionVolumes->GetPixel(ix); + + GradientVectorType pixel2 = m_GradientImagePointer->GetPixel(ix); for (int i=0;i0) { for( int f=0;fSetPixel(ix, pixel2); + //14.10.2013 + //m_CorrectedDiffusionVolumes->SetPixel(ix, pixel2); + m_GradientImagePointer->SetPixel(ix, pixel2); } } } } - typename TensorImageType::Pointer tensorImg = TensorImageType::New(); + + + + + typename TensorImageType::Pointer tensorImg; + tensorImg = TensorImageType::New(); tensorImg->SetRegions(m_GradientImagePointer->GetLargestPossibleRegion().GetSize()); tensorImg->SetSpacing(m_GradientImagePointer->GetSpacing()); tensorImg->SetOrigin(m_GradientImagePointer->GetOrigin()); tensorImg->Allocate(); + //14.10.2013 + /* typename TensorImageType::Pointer temp_tensorImg = TensorImageType::New(); + temp_tensorImg->SetRegions(m_GradientImagePointer->GetLargestPossibleRegion().GetSize()); + temp_tensorImg->SetSpacing(m_GradientImagePointer->GetSpacing()); + temp_tensorImg->SetOrigin(m_GradientImagePointer->GetOrigin()); + temp_tensorImg->Allocate(); + + */ + + + //typename TensorImageType::Pointer temp_tensorImg = TensorImageType::New(); // Deep copy a temporary tensor image for the pre-processing methods. - DeepCopyTensorImage(tensorImg,temp_tensorImg); + //14.10.2013 + //DeepCopyTensorImage(tensorImg,temp_tensorImg); //Declaration of vectors that contains too high or too low atenuation for each gradient. Attenuation is only calculated for //non B0 images so nof-numberb0. vnl_vector< double> pixel_max(nof-numberb0); vnl_vector< double> pixel_min(nof-numberb0); // to high and to low attenuation is calculated with use of highest allowed =5 and lowest allowed =0.01 diffusion coefficient for (int i=0;iSetNthOutput(0, tensorImg); } template void TensorReconstructionWithEigenvalueCorrectionFilter ::SetGradientImage( GradientDirectionContainerType *gradientDirection, const GradientImagesType *gradientImage ) { if( m_GradientImageTypeEnumeration == GradientIsInManyImages ) { itkExceptionMacro( << "Cannot call both methods:" << "AddGradientImage and SetGradientImage. Please call only one of them."); } this->m_GradientDirectionContainer = gradientDirection; unsigned int numImages = gradientDirection->Size(); this->m_NumberOfBaselineImages = 0; this->m_NumberOfGradientDirections = numImages - this->m_NumberOfBaselineImages; // ensure that the gradient image we received has as many components as // the number of gradient directions if( gradientImage->GetVectorLength() != this->m_NumberOfBaselineImages + this->m_NumberOfGradientDirections ) { itkExceptionMacro( << this->m_NumberOfGradientDirections << " gradients + " << this->m_NumberOfBaselineImages << "baselines = " << this->m_NumberOfGradientDirections + this->m_NumberOfBaselineImages << " directions specified but image has " << gradientImage->GetVectorLength() << " components."); } this->ProcessObject::SetNthInput( 0, const_cast< GradientImagesType* >(gradientImage) ); m_GradientImageTypeEnumeration = GradientIsInASingleImage; } template double TensorReconstructionWithEigenvalueCorrectionFilter - ::CheckNeighbours(int x, int y, int z,int f, itk::Size<3> size, itk::Image::Pointer mask, itk::VectorImage::Pointer corrected_diffusion_temp) + ::CheckNeighbours(int x, int y, int z,int f, itk::Size<3> size, itk::Image::Pointer mask, typename GradientImagesType::Pointer corrected_diffusion_temp) { // method is used for finding a new value for the voxel with use of its 27 neighborhood. To perform such a smoothing correct voxels are // counted an arithmetical mean is calculated and stored as a new value for the voxel. If there is no proper neigborhood voxel is turned // to the value of 0. // Definition of neighbourhood avoiding crossing the image boundaries - int x_max=size[0]; - int y_max=size[1]; - int z_max=size[2]; + int x_max=size[0]-1; + int y_max=size[1]-1; + int z_max=size[2]-1; double back_x=std::max(0,x-1); double back_y=std::max(0,y-1); double back_z=std::max(0,z-1); double forth_x=std::min((x+1),x_max); double forth_y=std::min((y+1),y_max); double forth_z=std::min((z+1),z_max); double tempsum=0; double temp_number=0; double temp_mask=0; for(int i=back_x; i<=forth_x; i++) { for (int j=back_y; j<=forth_y; j++) { for (int k=back_z; k<=forth_z; k++) { itk::Index<3> ix = {i,j,k}; temp_mask=mask->GetPixel(ix); GradientVectorType p = corrected_diffusion_temp->GetPixel(ix); - double test= p[f]; - - if (test > 0.0 )// taking only positive values and counting them + if (p[f] > 0.0 )// taking only positive values and counting them { if(!(i==x && j==y && k== z)) { tempsum=tempsum+p[f]; temp_number++; } } } } } //getting back to the original position of the voxel itk::Index<3> ix = {x,y,z}; if (temp_number <= 0.0) { tempsum=0; mask->SetPixel(ix,0); } else { tempsum=tempsum/temp_number; } return tempsum;// smoothed value of voxel } template void TensorReconstructionWithEigenvalueCorrectionFilter ::CalculateAttenuation(vnl_vector org_data,vnl_vector &atten,int nof, int numberb0) { double mean_b=0.0; for (int i=0;i0) { double o_d=org_data[i]; mean_b=mean_b+org_data[i]; } } mean_b=mean_b/numberb0; int cnt=0; for (int i=0;i double TensorReconstructionWithEigenvalueCorrectionFilter - ::CheckNegatives ( itk::Size<3> size, itk::Image::Pointer mask, itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg ) + ::CheckNegatives ( itk::Size<3> size, itk::Image::Pointer mask, typename itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg ) { // The method was created to simplif the flow of negative eigenvalue correction process. The method itself just return the number // of voxels (tensors) with negative eigenvalues. Then if the voxel was previously bad ( mask=2 ) but it is not bad anymore mask is //changed to 1. // declaration of important structures and variables double badvoxels=0; double pixel=0; - itk::DiffusionTensor3D ten; + itk::DiffusionTensor3D ten; vnl_matrix temp_tensor(3,3); vnl_vector eigen_vals(3); vnl_vector tensor (6); // for every pixel from the image for (int x=0;x ix = {x,y,z}; pixel = mask->GetPixel(ix); // but only if previously marked as bad one-negative eigen value if(pixel > 1) { ten = tensorImg->GetPixel(ix); // changing order from tensor structure in MITK into vnl structure 3x3 symmetric tensor matrix tensor[0] = ten(0,0); tensor[3] = ten(0,1); tensor[5] = ten(0,2); tensor[1] = ten(1,1); tensor[4] = ten(1,2); tensor[2] = ten(2,2); temp_tensor[0][0]= tensor[0]; temp_tensor[1][0]= tensor[3]; temp_tensor[2][0]= tensor[5]; temp_tensor[0][1]= tensor[3]; temp_tensor[1][1]= tensor[1]; temp_tensor[2][1]= tensor[4]; temp_tensor[0][2]= tensor[5]; temp_tensor[1][2]= tensor[4]; temp_tensor[2][2]= tensor[2]; //checking negativity of tensor eigenvalues vnl_symmetric_eigensystem eigen_tensor(temp_tensor); eigen_vals[0]=eigen_tensor.get_eigenvalue(0); eigen_vals[1]=eigen_tensor.get_eigenvalue(1); eigen_vals[2]=eigen_tensor.get_eigenvalue(2); //comparison to 0.01 instead of 0 was proposed by O.Pasternak if( eigen_vals[0]>0.01 && eigen_vals[1]>0.01 && eigen_vals[2]>0.01) { mask->SetPixel(ix,1); } else { badvoxels++; } } } } } double ret = badvoxels; return ret; } template void TensorReconstructionWithEigenvalueCorrectionFilter - ::CorrectDiffusionImage(int nof,int numberb0,itk::Size<3> size,itk::VectorImage::Pointer corrected_diffusion,itk::Image::Pointer mask,vnl_vector< double> pixel_max,vnl_vector< double> pixel_min) + ::CorrectDiffusionImage(int nof,int numberb0,itk::Size<3> size, typename GradientImagesType::Pointer corrected_diffusion,itk::Image::Pointer mask,vnl_vector< double> pixel_max,vnl_vector< double> pixel_min) { // in this method the voxels that has tensor negative eigenvalues are smoothed. Smoothing is done on DWI image.For the voxel //detected as bad one, B0 image is smoothed obligatory. All other gradient images are smoothed only when value of attenuation //is out of declared bounds for too high or too low attenuation. // declaration of important variables - vnl_vector org_data(nof-numberb0); + vnl_vector org_data(nof); vnl_vector atten(nof-numberb0); double cnt_atten=0; for (int z=0;z ix = {x, y, z}; if(mask->GetPixel(ix) > 1.0) { GradientVectorType pt = corrected_diffusion->GetPixel(ix); for (int i=0;i0) { mean_b=mean_b+org_data[i]; } } mean_b=mean_b/numberb0; int cnt=0; for (int i=0;i pixel_max[cnt_atten]) + if(atten[cnt_atten] pixel_max[cnt_atten]) { - org_data[f] = CheckNeighbours(x,y,z,f,size,mask,corrected_diffusion); + + int x_max=size[0]-1; + int y_max=size[1]-1; + int z_max=size[2]-1; + + double back_x=std::max(0,x-1); + double back_y=std::max(0,y-1); + double back_z=std::max(0,z-1); + + double forth_x=std::min((x+1),x_max); + double forth_y=std::min((y+1),y_max); + double forth_z=std::min((z+1),z_max); + + + double tempsum=0; + double temp_number=0; + double temp_mask=0; + + for(int i=back_x; i<=forth_x; i++) + { + for (int j=back_y; j<=forth_y; j++) + { + for (int k=back_z; k<=forth_z; k++) + { + itk::Index<3> ix = {i,j,k}; + temp_mask=mask->GetPixel(ix); + + + GradientVectorType p = corrected_diffusion->GetPixel(ix); + + //double test= p[f]; + + if (p[f] > 0.0 )// taking only positive values and counting them + { + if(!(i==x && j==y && k== z)) + { + tempsum=tempsum+p[f]; + temp_number++; + } + + } + + + } + } + } + + //getting back to the original position of the voxel + + itk::Index<3> ix = {x,y,z}; + + if (temp_number <= 0.0) + { + tempsum=0; + mask->SetPixel(ix,0); + } + else + { + tempsum=tempsum/temp_number; + + } + + org_data[f] = tempsum; } cnt_atten++; } //smoothing B0 if(m_B0Mask[f]==1) { - org_data[f] = CheckNeighbours(x,y,z,f,size,mask,corrected_diffusion); + int x_max=size[0]; + int y_max=size[1]; + int z_max=size[2]; + + double back_x=std::max(0,x-1); + double back_y=std::max(0,y-1); + double back_z=std::max(0,z-1); + + double forth_x=std::min((x+1),x_max); + double forth_y=std::min((y+1),y_max); + double forth_z=std::min((z+1),z_max); + + + double tempsum=0; + double temp_number=0; + double temp_mask=0; + + for(int i=back_x; i<=forth_x; i++) + { + for (int j=back_y; j<=forth_y; j++) + { + for (int k=back_z; k<=forth_z; k++) + { + itk::Index<3> ix = {i,j,k}; + temp_mask=mask->GetPixel(ix); + + + GradientVectorType p = corrected_diffusion->GetPixel(ix); + + //double test= p[f]; + + if (p[f] > 0.0 )// taking only positive values and counting them + { + if(!(i==x && j==y && k== z)) + { + tempsum=tempsum+p[f]; + temp_number++; + } + + } + + + } + } + } + + //getting back to the original position of the voxel + + itk::Index<3> ix = {x,y,z}; + + if (temp_number <= 0.0) + { + tempsum=0; + mask->SetPixel(ix,0); + } + else + { + tempsum=tempsum/temp_number; + + } + + org_data[f] = tempsum; } } for (int i=0;iSetPixel(ix, pt); } else { GradientVectorType pt = corrected_diffusion->GetPixel(ix); corrected_diffusion->SetPixel(ix, pt); } } } } } template void TensorReconstructionWithEigenvalueCorrectionFilter - ::GenerateTensorImage(int nof,int numberb0,itk::Size<3> size,itk::VectorImage::Pointer corrected_diffusion,itk::Image::Pointer mask,double what_mask,itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg) + ::GenerateTensorImage(int nof,int numberb0,itk::Size<3> size,itk::VectorImage::Pointer corrected_diffusion,itk::Image::Pointer mask,double what_mask, typename itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg) { // in this method the whole tensor image is updated with a tensors for defined voxels ( defined by a value of mask); itk::Index<3> ix; - vnl_vector org_data(nof-numberb0); + vnl_vector org_data(nof); vnl_vector atten(nof-numberb0); vnl_vector tensor(6); - itk::DiffusionTensor3D ten; + itk::DiffusionTensor3D ten; double mask_val=0; for (int x=0;xGetPixel(ix); //Tensors are calculated only for voxels above theshold for B0 image. if( mask_val > 0.0 ) { // calculation of attenuation with use of gradient image and and mean B0 image GradientVectorType pt = corrected_diffusion->GetPixel(ix); for (int i=0;i0) { double o_d=org_data[i]; mean_b=mean_b+org_data[i]; } } mean_b=mean_b/numberb0; int cnt=0; for (int i=0;iSetPixel(ix, ten); } // for voxels with mask value 0 - tensor is simply 0 ( outside brain value) else if (mask_val < 1.0) { + ten(0,0) = 0; ten(0,1) = 0; ten(0,2) = 0; ten(1,1) = 0; ten(1,2) = 0; ten(2,2) = 0; tensorImg->SetPixel(ix, ten); } } } } }// end of Generate Tensor template void TensorReconstructionWithEigenvalueCorrectionFilter ::TurnMask( itk::Size<3> size, itk::Image::Pointer mask, double previous_mask, double set_mask) { // The method changes voxels in the mask that poses a certain value with other value. itk::Index<3> ix; double temp_mask_value=0; for(int x=0;xGetPixel(ix); if(temp_mask_value>previous_mask) { mask->SetPixel(ix,set_mask); } } } } } + /* + template void TensorReconstructionWithEigenvalueCorrectionFilter ::DeepCopyDiffusionImage(itk::VectorImage::Pointer corrected_diffusion, itk::VectorImage::Pointer corrected_diffusion_temp,int nof) { corrected_diffusion_temp->SetSpacing(corrected_diffusion->GetSpacing()); corrected_diffusion_temp->SetOrigin(corrected_diffusion->GetOrigin()); corrected_diffusion_temp->SetVectorLength(nof); corrected_diffusion_temp->SetRegions(corrected_diffusion->GetLargestPossibleRegion()); corrected_diffusion_temp->Allocate(); itk::ImageRegionConstIterator inputIterator(corrected_diffusion, corrected_diffusion->GetLargestPossibleRegion()); itk::ImageRegionIterator outputIterator(corrected_diffusion_temp, corrected_diffusion_temp->GetLargestPossibleRegion()); inputIterator.GoToBegin(); outputIterator.GoToBegin(); while(!inputIterator.IsAtEnd()) { outputIterator.Set(inputIterator.Get()); ++inputIterator; ++outputIterator; } } template void TensorReconstructionWithEigenvalueCorrectionFilter - ::DeepCopyTensorImage(itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg, itk::Image< itk::DiffusionTensor3D, 3 >::Pointer temp_tensorImg) + ::DeepCopyTensorImage(itk::Image< itk::DiffusionTensor3D, 3 >::Pointer tensorImg, itk::Image< itk::DiffusionTensor3D, 3 >::Pointer temp_tensorImg) { temp_tensorImg->SetSpacing(tensorImg->GetSpacing()); temp_tensorImg->SetOrigin(tensorImg->GetOrigin()); temp_tensorImg->SetRegions(tensorImg->GetLargestPossibleRegion()); temp_tensorImg->Allocate(); itk::ImageRegionConstIterator inputIterator(tensorImg, tensorImg->GetLargestPossibleRegion()); itk::ImageRegionIterator outputIterator(temp_tensorImg, temp_tensorImg->GetLargestPossibleRegion()); inputIterator.GoToBegin(); outputIterator.GoToBegin(); while(!inputIterator.IsAtEnd()) { outputIterator.Set(inputIterator.Get()); ++inputIterator; ++outputIterator; } } + */ + + } // end of namespace diff --git a/Modules/DiffusionImaging/DiffusionCore/Rendering/mitkCompositeMapper.h b/Modules/DiffusionImaging/DiffusionCore/Rendering/mitkCompositeMapper.h index b587249019..49e4f0346f 100644 --- a/Modules/DiffusionImaging/DiffusionCore/Rendering/mitkCompositeMapper.h +++ b/Modules/DiffusionImaging/DiffusionCore/Rendering/mitkCompositeMapper.h @@ -1,159 +1,168 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef COMPOSITEMAPPER_H_HEADER_INCLUDED #define COMPOSITEMAPPER_H_HEADER_INCLUDED #include "mitkGLMapper.h" #include "mitkVtkMapper.h" #include "mitkQBallImage.h" #include "mitkImageVtkMapper2D.h" #include "mitkOdfVtkMapper2D.h" #include "mitkLevelWindowProperty.h" namespace mitk { class CopyImageMapper2D : public ImageVtkMapper2D { public: mitkClassMacro(CopyImageMapper2D,ImageVtkMapper2D); itkNewMacro(Self); friend class CompositeMapper; }; //##Documentation //## @brief Composite pattern for combination of different mappers //## @ingroup Mapper class CompositeMapper : public VtkMapper { public: mitkClassMacro(CompositeMapper,VtkMapper); itkNewMacro(Self); virtual void MitkRenderOverlay(BaseRenderer* renderer) { m_ImgMapper->MitkRenderOverlay(renderer); m_OdfMapper->MitkRenderOverlay(renderer); } virtual void MitkRenderOpaqueGeometry(BaseRenderer* renderer) { m_ImgMapper->MitkRenderOpaqueGeometry(renderer); m_OdfMapper->MitkRenderOpaqueGeometry(renderer); if( mitk::RenderingManager::GetInstance()->GetNextLOD( renderer ) == 0 ) { renderer->Modified(); } } virtual void MitkRenderTranslucentGeometry(BaseRenderer* renderer) { m_ImgMapper->MitkRenderTranslucentGeometry(renderer); m_OdfMapper->MitkRenderTranslucentGeometry(renderer); } virtual void MitkRenderVolumetricGeometry(BaseRenderer* renderer) { m_ImgMapper->MitkRenderVolumetricGeometry(renderer); m_OdfMapper->MitkRenderVolumetricGeometry(renderer); } void SetDataNode(DataNode* node) { m_DataNode = node; m_ImgMapper->SetDataNode(node); m_OdfMapper->SetDataNode(node); } mitk::ImageVtkMapper2D::Pointer GetImageMapper() { ImageVtkMapper2D* retval = m_ImgMapper; return retval; } bool HasVtkProp( const vtkProp* prop, BaseRenderer* renderer ) { return m_OdfMapper->HasVtkProp(prop, renderer); } - void ReleaseGraphicsResources(vtkWindow* window) + /* + * \deprecatedSince{2013_12} Use ReleaseGraphicsResources(mitk::BaseRenderer* renderer) instead + */ + DEPRECATED(void ReleaseGraphicsResources(vtkWindow* window)) { m_ImgMapper->ReleaseGraphicsResources(window); m_OdfMapper->ReleaseGraphicsResources(window); } + void ReleaseGraphicsResources(mitk::BaseRenderer* renderer) + { + m_ImgMapper->ReleaseGraphicsResources(renderer); + m_OdfMapper->ReleaseGraphicsResources(renderer); + } + static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = NULL, bool overwrite = false ) { mitk::OdfVtkMapper2D::SetDefaultProperties(node, renderer, overwrite); mitk::CopyImageMapper2D::SetDefaultProperties(node, renderer, overwrite); mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0,255); opaclevwin.SetWindowBounds(0,0); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->AddProperty( "opaclevelwindow", prop ); } bool IsLODEnabled( BaseRenderer * renderer ) const { return m_ImgMapper->IsLODEnabled(renderer) || m_OdfMapper->IsLODEnabled(renderer); } vtkProp* GetVtkProp(mitk::BaseRenderer* renderer) { vtkPropAssembly* assembly = vtkPropAssembly::New(); assembly->AddPart( m_OdfMapper->GetVtkProp(renderer)); assembly->AddPart( m_ImgMapper->GetVtkProp(renderer)); return assembly; } protected: virtual void Update(mitk::BaseRenderer* renderer) { m_OdfMapper->Update(renderer); GenerateDataForRenderer(renderer); } virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer) { m_ImgMapper->GenerateDataForRenderer(renderer); // if( mitk::RenderingManager::GetInstance()->GetNextLOD( renderer ) > 0 ) // { // m_OdfMapper->GenerateDataForRenderer(renderer); // } } CompositeMapper(); virtual ~CompositeMapper(); private: mitk::OdfVtkMapper2D::Pointer m_OdfMapper; mitk::CopyImageMapper2D::Pointer m_ImgMapper; }; } // namespace mitk #endif /* COMPOSITEMAPPER_H_HEADER_INCLUDED */ diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp index 702ebb3677..30a0999123 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp @@ -1,1218 +1,1226 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageStatisticsCalculator.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkExtractImageFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ( ( VTK_MAJOR_VERSION <= 5 ) && ( VTK_MINOR_VERSION<=8) ) #include "mitkvtkLassoStencilSource.h" #else #include "vtkLassoStencilSource.h" #endif #include #include namespace mitk { ImageStatisticsCalculator::ImageStatisticsCalculator() : m_MaskingMode( MASKING_MODE_NONE ), m_MaskingModeChanged( false ), m_IgnorePixelValue(0.0), m_DoIgnorePixelValue(false), m_IgnorePixelValueChanged(false), m_PlanarFigureAxis (0), m_PlanarFigureSlice (0), m_PlanarFigureCoordinate0 (0), m_PlanarFigureCoordinate1 (0) { m_EmptyHistogram = HistogramType::New(); m_EmptyHistogram->SetMeasurementVectorSize(1); HistogramType::SizeType histogramSize(1); histogramSize.Fill( 256 ); m_EmptyHistogram->Initialize( histogramSize ); m_EmptyStatistics.Reset(); } ImageStatisticsCalculator::~ImageStatisticsCalculator() { } void ImageStatisticsCalculator::SetImage( const mitk::Image *image ) { if ( m_Image != image ) { m_Image = image; this->Modified(); unsigned int numberOfTimeSteps = image->GetTimeSteps(); // Initialize vectors to time-size of this image m_ImageHistogramVector.resize( numberOfTimeSteps ); m_MaskedImageHistogramVector.resize( numberOfTimeSteps ); m_PlanarFigureHistogramVector.resize( numberOfTimeSteps ); m_ImageStatisticsVector.resize( numberOfTimeSteps ); m_MaskedImageStatisticsVector.resize( numberOfTimeSteps ); m_PlanarFigureStatisticsVector.resize( numberOfTimeSteps ); m_ImageStatisticsTimeStampVector.resize( numberOfTimeSteps ); m_MaskedImageStatisticsTimeStampVector.resize( numberOfTimeSteps ); m_PlanarFigureStatisticsTimeStampVector.resize( numberOfTimeSteps ); m_ImageStatisticsCalculationTriggerVector.resize( numberOfTimeSteps ); m_MaskedImageStatisticsCalculationTriggerVector.resize( numberOfTimeSteps ); m_PlanarFigureStatisticsCalculationTriggerVector.resize( numberOfTimeSteps ); for ( unsigned int t = 0; t < image->GetTimeSteps(); ++t ) { m_ImageStatisticsTimeStampVector[t].Modified(); m_ImageStatisticsCalculationTriggerVector[t] = true; } } } void ImageStatisticsCalculator::SetImageMask( const mitk::Image *imageMask ) { if ( m_Image.IsNull() ) { itkExceptionMacro( << "Image needs to be set first!" ); } if ( m_Image->GetTimeSteps() != imageMask->GetTimeSteps() ) { itkExceptionMacro( << "Image and image mask need to have equal number of time steps!" ); } if ( m_ImageMask != imageMask ) { m_ImageMask = imageMask; this->Modified(); for ( unsigned int t = 0; t < m_Image->GetTimeSteps(); ++t ) { m_MaskedImageStatisticsTimeStampVector[t].Modified(); m_MaskedImageStatisticsCalculationTriggerVector[t] = true; } } } void ImageStatisticsCalculator::SetPlanarFigure( mitk::PlanarFigure *planarFigure ) { if ( m_Image.IsNull() ) { itkExceptionMacro( << "Image needs to be set first!" ); } if ( m_PlanarFigure != planarFigure ) { m_PlanarFigure = planarFigure; this->Modified(); for ( unsigned int t = 0; t < m_Image->GetTimeSteps(); ++t ) { m_PlanarFigureStatisticsTimeStampVector[t].Modified(); m_PlanarFigureStatisticsCalculationTriggerVector[t] = true; } } } void ImageStatisticsCalculator::SetMaskingMode( unsigned int mode ) { if ( m_MaskingMode != mode ) { m_MaskingMode = mode; m_MaskingModeChanged = true; this->Modified(); } } void ImageStatisticsCalculator::SetMaskingModeToNone() { if ( m_MaskingMode != MASKING_MODE_NONE ) { m_MaskingMode = MASKING_MODE_NONE; m_MaskingModeChanged = true; this->Modified(); } } void ImageStatisticsCalculator::SetMaskingModeToImage() { if ( m_MaskingMode != MASKING_MODE_IMAGE ) { m_MaskingMode = MASKING_MODE_IMAGE; m_MaskingModeChanged = true; this->Modified(); } } void ImageStatisticsCalculator::SetMaskingModeToPlanarFigure() { if ( m_MaskingMode != MASKING_MODE_PLANARFIGURE ) { m_MaskingMode = MASKING_MODE_PLANARFIGURE; m_MaskingModeChanged = true; this->Modified(); } } void ImageStatisticsCalculator::SetIgnorePixelValue(double value) { if ( m_IgnorePixelValue != value ) { m_IgnorePixelValue = value; if(m_DoIgnorePixelValue) { m_IgnorePixelValueChanged = true; } this->Modified(); } } double ImageStatisticsCalculator::GetIgnorePixelValue() { return m_IgnorePixelValue; } void ImageStatisticsCalculator::SetDoIgnorePixelValue(bool value) { if ( m_DoIgnorePixelValue != value ) { m_DoIgnorePixelValue = value; m_IgnorePixelValueChanged = true; this->Modified(); } } bool ImageStatisticsCalculator::GetDoIgnorePixelValue() { return m_DoIgnorePixelValue; } bool ImageStatisticsCalculator::ComputeStatistics( unsigned int timeStep ) { if (m_Image.IsNull() ) { mitkThrow() << "Image not set!"; } if (!m_Image->IsInitialized()) { mitkThrow() << "Image not initialized!"; } if ( m_Image->GetReferenceCount() == 1 ) { // Image no longer valid; we are the only ones to still hold a reference on it return false; } if ( timeStep >= m_Image->GetTimeSteps() ) { throw std::runtime_error( "Error: invalid time step!" ); } // If a mask was set but we are the only ones to still hold a reference on // it, delete it. if ( m_ImageMask.IsNotNull() && (m_ImageMask->GetReferenceCount() == 1) ) { m_ImageMask = NULL; } // Check if statistics is already up-to-date unsigned long imageMTime = m_ImageStatisticsTimeStampVector[timeStep].GetMTime(); unsigned long maskedImageMTime = m_MaskedImageStatisticsTimeStampVector[timeStep].GetMTime(); unsigned long planarFigureMTime = m_PlanarFigureStatisticsTimeStampVector[timeStep].GetMTime(); bool imageStatisticsCalculationTrigger = m_ImageStatisticsCalculationTriggerVector[timeStep]; bool maskedImageStatisticsCalculationTrigger = m_MaskedImageStatisticsCalculationTriggerVector[timeStep]; bool planarFigureStatisticsCalculationTrigger = m_PlanarFigureStatisticsCalculationTriggerVector[timeStep]; if ( !m_IgnorePixelValueChanged && ((m_MaskingMode != MASKING_MODE_NONE) || (imageMTime > m_Image->GetMTime() && !imageStatisticsCalculationTrigger)) && ((m_MaskingMode != MASKING_MODE_IMAGE) || (maskedImageMTime > m_ImageMask->GetMTime() && !maskedImageStatisticsCalculationTrigger)) && ((m_MaskingMode != MASKING_MODE_PLANARFIGURE) || (planarFigureMTime > m_PlanarFigure->GetMTime() && !planarFigureStatisticsCalculationTrigger)) ) { // Statistics is up to date! if ( m_MaskingModeChanged ) { m_MaskingModeChanged = false; return true; } else { return false; } } // Reset state changed flag m_MaskingModeChanged = false; m_IgnorePixelValueChanged = false; // Depending on masking mode, extract and/or generate the required image // and mask data from the user input this->ExtractImageAndMask( timeStep ); StatisticsContainer *statisticsContainer; HistogramContainer *histogramContainer; switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: if(!m_DoIgnorePixelValue) { statisticsContainer = &m_ImageStatisticsVector[timeStep]; histogramContainer = &m_ImageHistogramVector[timeStep]; m_ImageStatisticsTimeStampVector[timeStep].Modified(); m_ImageStatisticsCalculationTriggerVector[timeStep] = false; } else { statisticsContainer = &m_MaskedImageStatisticsVector[timeStep]; histogramContainer = &m_MaskedImageHistogramVector[timeStep]; m_MaskedImageStatisticsTimeStampVector[timeStep].Modified(); m_MaskedImageStatisticsCalculationTriggerVector[timeStep] = false; } break; case MASKING_MODE_IMAGE: statisticsContainer = &m_MaskedImageStatisticsVector[timeStep]; histogramContainer = &m_MaskedImageHistogramVector[timeStep]; m_MaskedImageStatisticsTimeStampVector[timeStep].Modified(); m_MaskedImageStatisticsCalculationTriggerVector[timeStep] = false; break; case MASKING_MODE_PLANARFIGURE: statisticsContainer = &m_PlanarFigureStatisticsVector[timeStep]; histogramContainer = &m_PlanarFigureHistogramVector[timeStep]; m_PlanarFigureStatisticsTimeStampVector[timeStep].Modified(); m_PlanarFigureStatisticsCalculationTriggerVector[timeStep] = false; break; } // Calculate statistics and histogram(s) if ( m_InternalImage->GetDimension() == 3 ) { if ( m_MaskingMode == MASKING_MODE_NONE && !m_DoIgnorePixelValue ) { AccessFixedDimensionByItk_2( m_InternalImage, InternalCalculateStatisticsUnmasked, 3, statisticsContainer, histogramContainer ); } else { AccessFixedDimensionByItk_3( m_InternalImage, InternalCalculateStatisticsMasked, 3, m_InternalImageMask3D.GetPointer(), statisticsContainer, histogramContainer ); } } else if ( m_InternalImage->GetDimension() == 2 ) { if ( m_MaskingMode == MASKING_MODE_NONE && !m_DoIgnorePixelValue ) { AccessFixedDimensionByItk_2( m_InternalImage, InternalCalculateStatisticsUnmasked, 2, statisticsContainer, histogramContainer ); } else { AccessFixedDimensionByItk_3( m_InternalImage, InternalCalculateStatisticsMasked, 2, m_InternalImageMask2D.GetPointer(), statisticsContainer, histogramContainer ); } } else { MITK_ERROR << "ImageStatistics: Image dimension not supported!"; } // Release unused image smart pointers to free memory m_InternalImage = mitk::Image::ConstPointer(); m_InternalImageMask3D = MaskImage3DType::Pointer(); m_InternalImageMask2D = MaskImage2DType::Pointer(); return true; } const ImageStatisticsCalculator::HistogramType * ImageStatisticsCalculator::GetHistogram( unsigned int timeStep, unsigned int label ) const { if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) ) { return NULL; } switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: { if(m_DoIgnorePixelValue) return m_MaskedImageHistogramVector[timeStep][label]; return m_ImageHistogramVector[timeStep][label]; } case MASKING_MODE_IMAGE: return m_MaskedImageHistogramVector[timeStep][label]; case MASKING_MODE_PLANARFIGURE: return m_PlanarFigureHistogramVector[timeStep][label]; } } const ImageStatisticsCalculator::HistogramContainer & ImageStatisticsCalculator::GetHistogramVector( unsigned int timeStep ) const { if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) ) { return m_EmptyHistogramContainer; } switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: { if(m_DoIgnorePixelValue) return m_MaskedImageHistogramVector[timeStep]; return m_ImageHistogramVector[timeStep]; } case MASKING_MODE_IMAGE: return m_MaskedImageHistogramVector[timeStep]; case MASKING_MODE_PLANARFIGURE: return m_PlanarFigureHistogramVector[timeStep]; } } const ImageStatisticsCalculator::Statistics & ImageStatisticsCalculator::GetStatistics( unsigned int timeStep, unsigned int label ) const { if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) ) { return m_EmptyStatistics; } switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: { if(m_DoIgnorePixelValue) return m_MaskedImageStatisticsVector[timeStep][label]; return m_ImageStatisticsVector[timeStep][label]; } case MASKING_MODE_IMAGE: return m_MaskedImageStatisticsVector[timeStep][label]; case MASKING_MODE_PLANARFIGURE: return m_PlanarFigureStatisticsVector[timeStep][label]; } } const ImageStatisticsCalculator::StatisticsContainer & ImageStatisticsCalculator::GetStatisticsVector( unsigned int timeStep ) const { if ( m_Image.IsNull() || (timeStep >= m_Image->GetTimeSteps()) ) { return m_EmptyStatisticsContainer; } switch ( m_MaskingMode ) { case MASKING_MODE_NONE: default: { if(m_DoIgnorePixelValue) return m_MaskedImageStatisticsVector[timeStep]; return m_ImageStatisticsVector[timeStep]; } case MASKING_MODE_IMAGE: return m_MaskedImageStatisticsVector[timeStep]; case MASKING_MODE_PLANARFIGURE: return m_PlanarFigureStatisticsVector[timeStep]; } } void ImageStatisticsCalculator::ExtractImageAndMask( unsigned int timeStep ) { if ( m_Image.IsNull() ) { throw std::runtime_error( "Error: image empty!" ); } if ( timeStep >= m_Image->GetTimeSteps() ) { throw std::runtime_error( "Error: invalid time step!" ); } ImageTimeSelector::Pointer imageTimeSelector = ImageTimeSelector::New(); imageTimeSelector->SetInput( m_Image ); imageTimeSelector->SetTimeNr( timeStep ); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image *timeSliceImage = imageTimeSelector->GetOutput(); switch ( m_MaskingMode ) { case MASKING_MODE_NONE: { m_InternalImage = timeSliceImage; m_InternalImageMask2D = NULL; m_InternalImageMask3D = NULL; if(m_DoIgnorePixelValue) { if( m_InternalImage->GetDimension() == 3 ) { CastToItkImage( timeSliceImage, m_InternalImageMask3D ); m_InternalImageMask3D->FillBuffer(1); } if( m_InternalImage->GetDimension() == 2 ) { CastToItkImage( timeSliceImage, m_InternalImageMask2D ); m_InternalImageMask2D->FillBuffer(1); } } break; } case MASKING_MODE_IMAGE: { if ( m_ImageMask.IsNotNull() && (m_ImageMask->GetReferenceCount() > 1) ) { if ( timeStep < m_ImageMask->GetTimeSteps() ) { ImageTimeSelector::Pointer maskedImageTimeSelector = ImageTimeSelector::New(); maskedImageTimeSelector->SetInput( m_ImageMask ); maskedImageTimeSelector->SetTimeNr( timeStep ); maskedImageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image *timeSliceMaskedImage = maskedImageTimeSelector->GetOutput(); m_InternalImage = timeSliceImage; CastToItkImage( timeSliceMaskedImage, m_InternalImageMask3D ); } else { throw std::runtime_error( "Error: image mask has not enough time steps!" ); } } else { throw std::runtime_error( "Error: image mask empty!" ); } break; } case MASKING_MODE_PLANARFIGURE: { m_InternalImageMask2D = NULL; if ( m_PlanarFigure.IsNull() ) { throw std::runtime_error( "Error: planar figure empty!" ); } if ( !m_PlanarFigure->IsClosed() ) { throw std::runtime_error( "Masking not possible for non-closed figures" ); } const Geometry3D *imageGeometry = timeSliceImage->GetGeometry(); if ( imageGeometry == NULL ) { throw std::runtime_error( "Image geometry invalid!" ); } const Geometry2D *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); if ( planarFigureGeometry2D == NULL ) { throw std::runtime_error( "Planar-Figure not yet initialized!" ); } const PlaneGeometry *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigureGeometry2D ); if ( planarFigureGeometry == NULL ) { throw std::runtime_error( "Non-planar planar figures not supported!" ); } // Find principal direction of PlanarFigure in input image unsigned int axis; if ( !this->GetPrincipalAxis( imageGeometry, planarFigureGeometry->GetNormal(), axis ) ) { throw std::runtime_error( "Non-aligned planar figures not supported!" ); } m_PlanarFigureAxis = axis; // Find slice number corresponding to PlanarFigure in input image MaskImage3DType::IndexType index; imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index ); unsigned int slice = index[axis]; m_PlanarFigureSlice = slice; // Extract slice with given position and direction from image - ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New(); - imageExtractor->SetInput( timeSliceImage ); - imageExtractor->SetSliceDimension( axis ); - imageExtractor->SetSliceIndex( slice ); - imageExtractor->Update(); - m_InternalImage = imageExtractor->GetOutput(); + unsigned int dimension = timeSliceImage->GetDimension(); + if (dimension != 2) + { + ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New(); + imageExtractor->SetInput( timeSliceImage ); + imageExtractor->SetSliceDimension( axis ); + imageExtractor->SetSliceIndex( slice ); + imageExtractor->Update(); + m_InternalImage = imageExtractor->GetOutput(); + } + else + { + m_InternalImage = timeSliceImage; + } // Compute mask from PlanarFigure AccessFixedDimensionByItk_1( m_InternalImage, InternalCalculateMaskFromPlanarFigure, 2, axis ); } } if(m_DoIgnorePixelValue) { if ( m_InternalImage->GetDimension() == 3 ) { AccessFixedDimensionByItk_1( m_InternalImage, InternalMaskIgnoredPixels, 3, m_InternalImageMask3D.GetPointer() ); } else if ( m_InternalImage->GetDimension() == 2 ) { AccessFixedDimensionByItk_1( m_InternalImage, InternalMaskIgnoredPixels, 2, m_InternalImageMask2D.GetPointer() ); } } } bool ImageStatisticsCalculator::GetPrincipalAxis( const Geometry3D *geometry, Vector3D vector, unsigned int &axis ) { vector.Normalize(); for ( unsigned int i = 0; i < 3; ++i ) { Vector3D axisVector = geometry->GetAxisVector( i ); axisVector.Normalize(); if ( fabs( fabs( axisVector * vector ) - 1.0) < mitk::eps ) { axis = i; return true; } } return false; } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked( const itk::Image< TPixel, VImageDimension > *image, StatisticsContainer *statisticsContainer, HistogramContainer* histogramContainer ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typedef typename ImageType::IndexType IndexType; typedef itk::Statistics::ScalarImageToHistogramGenerator< ImageType > HistogramGeneratorType; statisticsContainer->clear(); histogramContainer->clear(); // Progress listening... typedef itk::SimpleMemberCommand< ImageStatisticsCalculator > ITKCommandType; ITKCommandType::Pointer progressListener; progressListener = ITKCommandType::New(); progressListener->SetCallbackFunction( this, &ImageStatisticsCalculator::UnmaskedStatisticsProgressUpdate ); // Issue 100 artificial progress events since ScalarIMageToHistogramGenerator // does not (yet?) support progress reporting this->InvokeEvent( itk::StartEvent() ); for ( unsigned int i = 0; i < 100; ++i ) { this->UnmaskedStatisticsProgressUpdate(); } // Calculate statistics (separate filter) typedef itk::StatisticsImageFilter< ImageType > StatisticsFilterType; typename StatisticsFilterType::Pointer statisticsFilter = StatisticsFilterType::New(); statisticsFilter->SetInput( image ); unsigned long observerTag = statisticsFilter->AddObserver( itk::ProgressEvent(), progressListener ); statisticsFilter->Update(); statisticsFilter->RemoveObserver( observerTag ); this->InvokeEvent( itk::EndEvent() ); // Calculate minimum and maximum typedef itk::MinimumMaximumImageCalculator< ImageType > MinMaxFilterType; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetImage( image ); unsigned long observerTag2 = minMaxFilter->AddObserver( itk::ProgressEvent(), progressListener ); minMaxFilter->Compute(); minMaxFilter->RemoveObserver( observerTag2 ); this->InvokeEvent( itk::EndEvent() ); Statistics statistics; statistics.Reset(); statistics.Label = 1; statistics.N = image->GetBufferedRegion().GetNumberOfPixels(); statistics.Min = statisticsFilter->GetMinimum(); statistics.Max = statisticsFilter->GetMaximum(); statistics.Mean = statisticsFilter->GetMean(); statistics.Median = 0.0; statistics.Sigma = statisticsFilter->GetSigma(); statistics.RMS = sqrt( statistics.Mean * statistics.Mean + statistics.Sigma * statistics.Sigma ); statistics.MinIndex.set_size(image->GetImageDimension()); statistics.MaxIndex.set_size(image->GetImageDimension()); for (int i=0; iGetIndexOfMaximum()[i]; statistics.MinIndex[i] = minMaxFilter->GetIndexOfMinimum()[i]; } statisticsContainer->push_back( statistics ); // Calculate histogram typename HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New(); histogramGenerator->SetInput( image ); histogramGenerator->SetMarginalScale( 100 ); histogramGenerator->SetNumberOfBins( 768 ); histogramGenerator->SetHistogramMin( statistics.Min ); histogramGenerator->SetHistogramMax( statistics.Max ); histogramGenerator->Compute(); histogramContainer->push_back( histogramGenerator->GetOutput() ); } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalMaskIgnoredPixels( const itk::Image< TPixel, VImageDimension > *image, itk::Image< unsigned short, VImageDimension > *maskImage ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; itk::ImageRegionIterator itmask(maskImage, maskImage->GetLargestPossibleRegion()); itk::ImageRegionConstIterator itimage(image, image->GetLargestPossibleRegion()); itmask.GoToBegin(); itimage.GoToBegin(); while( !itmask.IsAtEnd() ) { if(m_IgnorePixelValue == itimage.Get()) { itmask.Set(0); } ++itmask; ++itimage; } } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsMasked( const itk::Image< TPixel, VImageDimension > *image, itk::Image< unsigned short, VImageDimension > *maskImage, StatisticsContainer* statisticsContainer, HistogramContainer* histogramContainer ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typedef typename ImageType::IndexType IndexType; typedef typename ImageType::PointType PointType; typedef typename ImageType::SpacingType SpacingType; typedef itk::LabelStatisticsImageFilter< ImageType, MaskImageType > LabelStatisticsFilterType; typedef itk::ChangeInformationImageFilter< MaskImageType > ChangeInformationFilterType; typedef itk::ExtractImageFilter< ImageType, ImageType > ExtractImageFilterType; statisticsContainer->clear(); histogramContainer->clear(); // Make sure that mask is set if ( maskImage == NULL ) { itkExceptionMacro( << "Mask image needs to be set!" ); } // Make sure that spacing of mask and image are the same SpacingType imageSpacing = image->GetSpacing(); SpacingType maskSpacing = maskImage->GetSpacing(); PointType zeroPoint; zeroPoint.Fill( 0.0 ); if ( (zeroPoint + imageSpacing).SquaredEuclideanDistanceTo( (zeroPoint + maskSpacing) ) > mitk::eps ) { itkExceptionMacro( << "Mask needs to have same spacing as image! (Image spacing: " << imageSpacing << "; Mask spacing: " << maskSpacing << ")" ); } // Make sure that orientation of mask and image are the same typedef typename ImageType::DirectionType DirectionType; DirectionType imageDirection = image->GetDirection(); DirectionType maskDirection = maskImage->GetDirection(); for( int i = 0; i < imageDirection.ColumnDimensions; ++i ) { for( int j = 0; j < imageDirection.ColumnDimensions; ++j ) { double differenceDirection = imageDirection[i][j] - maskDirection[i][j]; if ( fabs( differenceDirection ) > mitk::eps ) { itkExceptionMacro( << "Mask needs to have same direction as image! (Image direction: " << imageDirection << "; Mask direction: " << maskDirection << ")" ); } } } // Make sure that the voxels of mask and image are correctly "aligned", i.e., voxel boundaries are the same in both images PointType imageOrigin = image->GetOrigin(); PointType maskOrigin = maskImage->GetOrigin(); long offset[ImageType::ImageDimension]; typedef itk::ContinuousIndex ContinousIndexType; ContinousIndexType maskOriginContinousIndex, imageOriginContinousIndex; image->TransformPhysicalPointToContinuousIndex(maskOrigin, maskOriginContinousIndex); image->TransformPhysicalPointToContinuousIndex(imageOrigin, imageOriginContinousIndex); for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i ) { double misalignment = maskOriginContinousIndex[i] - floor( maskOriginContinousIndex[i] + 0.5 ); if ( fabs( misalignment ) > mitk::eps ) { itkExceptionMacro( << "Pixels/voxels of mask and image are not sufficiently aligned! (Misalignment: " << misalignment << ")" ); } double indexCoordDistance = maskOriginContinousIndex[i] - imageOriginContinousIndex[i]; offset[i] = (int) indexCoordDistance + image->GetBufferedRegion().GetIndex()[i]; } // Adapt the origin and region (index/size) of the mask so that the origin of both are the same typename ChangeInformationFilterType::Pointer adaptMaskFilter; adaptMaskFilter = ChangeInformationFilterType::New(); adaptMaskFilter->ChangeOriginOn(); adaptMaskFilter->ChangeRegionOn(); adaptMaskFilter->SetInput( maskImage ); adaptMaskFilter->SetOutputOrigin( image->GetOrigin() ); adaptMaskFilter->SetOutputOffset( offset ); adaptMaskFilter->Update(); typename MaskImageType::Pointer adaptedMaskImage = adaptMaskFilter->GetOutput(); // Make sure that mask region is contained within image region if ( !image->GetLargestPossibleRegion().IsInside( adaptedMaskImage->GetLargestPossibleRegion() ) ) { itkExceptionMacro( << "Mask region needs to be inside of image region! (Image region: " << image->GetLargestPossibleRegion() << "; Mask region: " << adaptedMaskImage->GetLargestPossibleRegion() << ")" ); } // If mask region is smaller than image region, extract the sub-sampled region from the original image typename ImageType::SizeType imageSize = image->GetBufferedRegion().GetSize(); typename ImageType::SizeType maskSize = maskImage->GetBufferedRegion().GetSize(); bool maskSmallerImage = false; for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i ) { if ( maskSize[i] < imageSize[i] ) { maskSmallerImage = true; } } typename ImageType::ConstPointer adaptedImage; if ( maskSmallerImage ) { typename ExtractImageFilterType::Pointer extractImageFilter = ExtractImageFilterType::New(); extractImageFilter->SetInput( image ); extractImageFilter->SetExtractionRegion( adaptedMaskImage->GetBufferedRegion() ); extractImageFilter->Update(); adaptedImage = extractImageFilter->GetOutput(); } else { adaptedImage = image; } // Initialize Filter typedef itk::StatisticsImageFilter< ImageType > StatisticsFilterType; typename StatisticsFilterType::Pointer statisticsFilter = StatisticsFilterType::New(); statisticsFilter->SetInput( adaptedImage ); statisticsFilter->Update(); int numberOfBins = ( m_DoIgnorePixelValue && (m_MaskingMode == MASKING_MODE_NONE) ) ? 768 : 384; typename LabelStatisticsFilterType::Pointer labelStatisticsFilter; labelStatisticsFilter = LabelStatisticsFilterType::New(); labelStatisticsFilter->SetInput( adaptedImage ); labelStatisticsFilter->SetLabelInput( adaptedMaskImage ); labelStatisticsFilter->UseHistogramsOn(); labelStatisticsFilter->SetHistogramParameters( numberOfBins, statisticsFilter->GetMinimum(), statisticsFilter->GetMaximum() ); // Add progress listening typedef itk::SimpleMemberCommand< ImageStatisticsCalculator > ITKCommandType; ITKCommandType::Pointer progressListener; progressListener = ITKCommandType::New(); progressListener->SetCallbackFunction( this, &ImageStatisticsCalculator::MaskedStatisticsProgressUpdate ); unsigned long observerTag = labelStatisticsFilter->AddObserver( itk::ProgressEvent(), progressListener ); // Execute filter this->InvokeEvent( itk::StartEvent() ); // Make sure that only the mask region is considered (otherwise, if the mask region is smaller // than the image region, the Update() would result in an exception). labelStatisticsFilter->GetOutput()->SetRequestedRegion( adaptedMaskImage->GetLargestPossibleRegion() ); // Execute the filter labelStatisticsFilter->Update(); this->InvokeEvent( itk::EndEvent() ); labelStatisticsFilter->RemoveObserver( observerTag ); // Find all relevant labels of mask (other than 0) std::list< int > relevantLabels; bool maskNonEmpty = false; unsigned int i; for ( i = 1; i < 4096; ++i ) { if ( labelStatisticsFilter->HasLabel( i ) ) { relevantLabels.push_back( i ); maskNonEmpty = true; } } if ( maskNonEmpty ) { std::list< int >::iterator it; for ( it = relevantLabels.begin(), i = 0; it != relevantLabels.end(); ++it, ++i ) { histogramContainer->push_back( HistogramType::ConstPointer( labelStatisticsFilter->GetHistogram( (*it) ) ) ); Statistics statistics; statistics.Label = (*it); statistics.N = labelStatisticsFilter->GetCount( *it ); statistics.Min = labelStatisticsFilter->GetMinimum( *it ); statistics.Max = labelStatisticsFilter->GetMaximum( *it ); statistics.Mean = labelStatisticsFilter->GetMean( *it ); statistics.Median = labelStatisticsFilter->GetMedian( *it ); statistics.Sigma = labelStatisticsFilter->GetSigma( *it ); statistics.RMS = sqrt( statistics.Mean * statistics.Mean + statistics.Sigma * statistics.Sigma ); // restrict image to mask area for min/max index calculation typedef itk::MaskImageFilter< ImageType, MaskImageType, ImageType > MaskImageFilterType; typename MaskImageFilterType::Pointer masker = MaskImageFilterType::New(); masker->SetOutsideValue( (statistics.Min+statistics.Max)/2 ); masker->SetInput1(adaptedImage); masker->SetInput2(adaptedMaskImage); masker->Update(); // get index of minimum and maximum typedef itk::MinimumMaximumImageCalculator< ImageType > MinMaxFilterType; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetImage( masker->GetOutput() ); unsigned long observerTag2 = minMaxFilter->AddObserver( itk::ProgressEvent(), progressListener ); minMaxFilter->Compute(); minMaxFilter->RemoveObserver( observerTag2 ); this->InvokeEvent( itk::EndEvent() ); statistics.MinIndex.set_size(adaptedImage->GetImageDimension()); statistics.MaxIndex.set_size(adaptedImage->GetImageDimension()); typename MinMaxFilterType::IndexType tempMaxIndex = minMaxFilter->GetIndexOfMaximum(); typename MinMaxFilterType::IndexType tempMinIndex = minMaxFilter->GetIndexOfMinimum(); // FIX BUG 14644 //If a PlanarFigure is used for segmentation the //adaptedImage is a single slice (2D). Adding the // 3. dimension. if (m_MaskingMode == MASKING_MODE_PLANARFIGURE && m_Image->GetDimension()==3) { statistics.MaxIndex.set_size(m_Image->GetDimension()); statistics.MaxIndex[m_PlanarFigureCoordinate0]=tempMaxIndex[0]; statistics.MaxIndex[m_PlanarFigureCoordinate1]=tempMaxIndex[1]; statistics.MaxIndex[m_PlanarFigureAxis]=m_PlanarFigureSlice; statistics.MinIndex.set_size(m_Image->GetDimension()); statistics.MinIndex[m_PlanarFigureCoordinate0]=tempMinIndex[0]; statistics.MinIndex[m_PlanarFigureCoordinate1]=tempMinIndex[1]; statistics.MinIndex[m_PlanarFigureAxis]=m_PlanarFigureSlice; } else { for (int i = 0; ipush_back( statistics ); } } else { histogramContainer->push_back( HistogramType::ConstPointer( m_EmptyHistogram ) ); statisticsContainer->push_back( Statistics() );; } } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::CastImageFilter< ImageType, MaskImage2DType > CastFilterType; // Generate mask image as new image with same header as input image and // initialize with "1". typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput( image ); castFilter->Update(); castFilter->GetOutput()->FillBuffer( 1 ); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. // These points are used by the vtkLassoStencilSource to create // a vtkImageStencil. const mitk::Geometry2D *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::Geometry3D *imageGeometry3D = m_Image->GetGeometry( 0 ); // Determine x- and y-dimensions depending on principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } m_PlanarFigureCoordinate0= i0; m_PlanarFigureCoordinate1= i1; // store the polyline contour as vtkPoints object bool outOfBounds = false; vtkSmartPointer points = vtkSmartPointer::New(); typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected // image planarFigureGeometry2D->Map( it->Point, point3D ); // Polygons (partially) outside of the image bounds can not be processed // further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); points->InsertNextPoint( point3D[i0], point3D[i1], 0 ); } // mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds // this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero double bounds[6] = {0, 0, 0, 0, 0, 0}; points->GetBounds( bounds ); bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps; bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps; bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps; // throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent if ( m_PlanarFigure->IsClosed() && ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z))) { mitkThrow() << "Figure has a zero area and cannot be used for masking."; } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } // create a vtkLassoStencilSource and set the points of the Polygon vtkSmartPointer lassoStencil = vtkSmartPointer::New(); lassoStencil->SetShapeToPolygon(); lassoStencil->SetPoints( points ); // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< MaskImage2DType > ImageImportType; typedef itk::VTKImageExport< MaskImage2DType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( castFilter->GetOutput() ); vtkSmartPointer vtkImporter = vtkSmartPointer::New(); this->ConnectPipelines( itkExporter, vtkImporter ); // Apply the generated image stencil to the input image vtkSmartPointer imageStencilFilter = vtkSmartPointer::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencil( lassoStencil->GetOutput() ); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); // Export from VTK back to ITK vtkSmartPointer vtkExporter = vtkImageExport::New(); // TODO: this is WRONG, should be vtkSmartPointer::New(), but bug # 14455 //vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputConnection( imageStencilFilter->GetOutputPort() ); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); // Store mask m_InternalImageMask2D = itkImporter->GetOutput(); } void ImageStatisticsCalculator::UnmaskedStatisticsProgressUpdate() { // Need to throw away every second progress event to reach a final count of // 100 since two consecutive filters are used in this case static int updateCounter = 0; if ( updateCounter++ % 2 == 0 ) { this->InvokeEvent( itk::ProgressEvent() ); } } void ImageStatisticsCalculator::MaskedStatisticsProgressUpdate() { this->InvokeEvent( itk::ProgressEvent() ); } } diff --git a/Modules/MitkExt/Rendering/mitkEnhancedPointSetVtkMapper3D.cpp b/Modules/MitkExt/Rendering/mitkEnhancedPointSetVtkMapper3D.cpp index 4beb25e5db..00b95f3fe3 100644 --- a/Modules/MitkExt/Rendering/mitkEnhancedPointSetVtkMapper3D.cpp +++ b/Modules/MitkExt/Rendering/mitkEnhancedPointSetVtkMapper3D.cpp @@ -1,444 +1,438 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkEnhancedPointSetVtkMapper3D.h" //#include #include #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkLookupTables.h" #include "mitkColorProperty.h" //#include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include const mitk::PointSet* mitk::EnhancedPointSetVtkMapper3D::GetInput() { return static_cast ( GetDataNode()->GetData() ); } mitk::EnhancedPointSetVtkMapper3D::EnhancedPointSetVtkMapper3D() { m_Contour = vtkActor::New(); m_ContourSource = vtkTubeFilter::New(); m_PropAssembly = vtkAssembly::New(); } vtkProp* mitk::EnhancedPointSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer* /*renderer*/) { return m_PropAssembly; } mitk::EnhancedPointSetVtkMapper3D::~EnhancedPointSetVtkMapper3D() { m_Contour->Delete(); m_ContourSource->Delete(); m_PropAssembly->Delete(); // TODO: do cleanup correctly // Clean up all remaining actors and poly-data sources //std::for_each(m_PointActors.begin(), m_PointActors.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_SphereSources.begin(), m_SphereSources.end(), &mitk::EnhancedPointSetVtgkMapper3D::DeleteVtkObject); // std::for_each(m_CubeSources.begin(), m_CubeSources.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_ConeSources.begin(), m_ConeSources.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_CylinderSources.begin(), m_CylinderSources.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // } -void mitk::EnhancedPointSetVtkMapper3D::ReleaseGraphicsResources(vtkWindow * /*renWin*/) -{ - // TODO: Do we have to call this for all actors?? - //m_Actor->ReleaseGraphicsResources(renWin); -} - void mitk::EnhancedPointSetVtkMapper3D::UpdateVtkObjects() { // get and update the PointSet const mitk::PointSet* pointset = this->GetInput(); //pointset->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType* itkPointSet = pointset->GetPointSet( timestep ); mitk::PointSet::PointsContainer* points = itkPointSet->GetPoints(); mitk::PointSet::PointDataContainer* pointData = itkPointSet->GetPointData(); assert(points->Size() == pointData->Size()); mitk::PointSet::PointsIterator pIt; mitk::PointSet::PointDataIterator pdIt; /* search removed points and delete the corresponding source/actor/mapper objects */ for (ActorMap::iterator it = m_PointActors.begin(); it != m_PointActors.end(); ) { PointIdentifier id = it->first; if (!points->IndexExists(id)) { this->RemoveEntryFromSourceMaps(id); m_PropAssembly->GetParts()->RemoveItem(it->second.first); // remove from prop assembly if (it->second.first != NULL) it->second.first->Delete(); // Delete actor, which deletes mapper too (reference count) ActorMap::iterator er = it; // save iterator for deleting ++it; // advance iterator to next object m_PointActors.erase(er); // erase element from map. This invalidates er, therefore we had to advance it before deletion. } else ++it; } /* iterate over each point in the pointset and create corresponding vtk objects */ for (pIt = points->Begin(), pdIt = pointData->Begin(); pIt != itkPointSet->GetPoints()->End(); ++pIt, ++pdIt) { PointIdentifier pointID = pIt->Index(); assert (pointID == pdIt->Index()); mitk::PointSet::PointType point = pIt->Value(); mitk::PointSet::PointDataType data = pdIt->Value(); ActorMap::iterator aIt = m_PointActors.find(pointID); // Does an actor exist for the point? /* Create/Update sources for the point */ vtkActor* a = NULL; bool newPoint = (aIt == m_PointActors.end()); // current point is new bool specChanged = (!newPoint && data.pointSpec != aIt->second.second); // point spec of current point has changed if (newPoint) // point did not exist before, we have to create vtk objects for it { // create actor and mapper for the new point a = vtkActor::New(); vtkPolyDataMapper* m = vtkPolyDataMapper::New(); a->SetMapper(m); m->UnRegister( NULL ); aIt = m_PointActors.insert(std::make_pair(pointID, std::make_pair(a, data.pointSpec))).first; // insert element and update actormap iterator to point to new element m_PropAssembly->AddPart(a); } else { a = aIt->second.first; if (specChanged) // point exists, but point spec has changed { this->RemoveEntryFromSourceMaps( pointID ); } } if ( newPoint || specChanged ) // new point OR existing point but point spec changed { vtkPolyDataAlgorithm* source = NULL; // works only in VTK 5+ switch (data.pointSpec) // add to new map { //TODO: look up representation in a representationlookuptable case PTSTART: //cube m_CubeSources[pointID] = vtkCubeSource::New(); source = m_CubeSources[pointID]; break; case PTCORNER: //cone m_ConeSources[pointID] = vtkConeSource::New(); source = m_ConeSources[pointID]; break; case PTEDGE: // cylinder m_CylinderSources[pointID] = vtkCylinderSource::New(); source = m_CylinderSources[pointID]; break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID] = vtkSphereSource::New(); source = m_SphereSources[pointID]; break; } vtkPolyDataMapper* m = dynamic_cast(a->GetMapper()); assert(m != NULL); m->SetInput(source->GetOutput()); aIt->second.second = data.pointSpec; // update point spec in actormap } } // for each point } void mitk::EnhancedPointSetVtkMapper3D::ApplyProperties( mitk::BaseRenderer * renderer ) { this->UpdateVtkObjects(); /* iterate over all points in pointset and apply properties to corresponding vtk objects */ // get and update the PointSet const mitk::PointSet* pointset = this->GetInput(); int timestep = this->GetTimestep(); mitk::PointSet::DataType* itkPointSet = pointset->GetPointSet( timestep ); mitk::PointSet::PointsContainer* points = itkPointSet->GetPoints(); mitk::PointSet::PointDataContainer* pointData = itkPointSet->GetPointData(); assert(points->Size() == pointData->Size()); mitk::PointSet::PointsIterator pIt; mitk::PointSet::PointDataIterator pdIt; mitk::DataNode* n = this->GetDataNode(); assert(n != NULL); for (pIt = points->Begin(), pdIt = pointData->Begin(); pIt != itkPointSet->GetPoints()->End(); ++pIt, ++pdIt) // for each point in the pointset { PointIdentifier pointID = pIt->Index(); assert (pointID == pdIt->Index()); mitk::PointSet::PointType point = pIt->Value(); mitk::PointSet::PointDataType data = pdIt->Value(); ActorMap::iterator aIt = m_PointActors.find(pointID); // Does an actor exist for the point? assert(aIt != m_PointActors.end()); // UpdateVtkObjects() must ensure that actor exists vtkActor* a = aIt->second.first; assert(a != NULL); SetVtkMapperImmediateModeRendering(a->GetMapper()); /* update properties */ // visibility bool pointVisibility = true; bool visValueFound = false; mitk::BaseProperty* visProp = n->GetProperty("visibility", renderer); mitk::BoolLookupTableProperty* visLTProp = dynamic_cast(visProp); if (visLTProp != NULL) { mitk::BoolLookupTable visLookupTable = visLTProp->GetValue(); //if (visLookupTable != NULL) //{ try { pointVisibility = visLookupTable.GetTableValue(pointID); visValueFound = true; } catch (...) { } //} } if (visValueFound == false) { pointVisibility = n->IsVisible(renderer, "show points"); // use BoolProperty instead } a->SetVisibility(pointVisibility); // opacity float opacity = 1.0; bool opValueFound = false; mitk::BaseProperty* opProp = n->GetProperty("opacity", renderer); mitk::FloatLookupTableProperty* opLTProp = dynamic_cast(opProp); if (opLTProp != NULL) { mitk::FloatLookupTable opLookupTable = opLTProp->GetValue(); //if (opLookupTable != NULL) //{ try { opacity = opLookupTable.GetTableValue(pointID); opValueFound = true; } catch (...) { } //} } if (opValueFound == false) { n->GetOpacity(opacity, renderer); } a->GetProperty()->SetOpacity(opacity); ////////////////////// continue here /////////////////// // pointsize & point position float pointSize = 1.0; n->GetFloatProperty( "pointsize", pointSize, renderer); switch (data.pointSpec) { //TODO: look up representation in a representationlookuptable case PTSTART: //cube m_CubeSources[pointID]->SetXLength(pointSize); m_CubeSources[pointID]->SetYLength(pointSize); m_CubeSources[pointID]->SetZLength(pointSize); //m_CubeSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTCORNER: //cone m_ConeSources[pointID]->SetRadius(pointSize/2); m_ConeSources[pointID]->SetHeight(pointSize); m_ConeSources[pointID]->SetResolution(2); // two crossed triangles. Maybe introduce an extra property for //m_ConeSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTEDGE: // cylinder m_CylinderSources[pointID]->SetRadius(pointSize/2); m_CylinderSources[pointID]->SetHeight(pointSize); m_CylinderSources[pointID]->CappingOn(); m_CylinderSources[pointID]->SetResolution(6); //m_CylinderSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID]->SetRadius(pointSize/2); m_SphereSources[pointID]->SetThetaResolution(10); m_SphereSources[pointID]->SetPhiResolution(10); //m_SphereSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; } // set position mitk::Point3D pos = pIt->Value(); aIt->second.first->SetPosition(pos[0], pos[1], pos[2]); // selectedcolor & color float color[3]; if (data.selected) { if(!n->GetColor(color, renderer, "selectedcolor")) n->GetColor(color, renderer); } else { mitk::BaseProperty* a = n->GetProperty("colorLookupTable", renderer); mitk::LookupTableProperty* b = dynamic_cast(a); if (b != NULL) { mitk::LookupTable::Pointer c = b->GetLookupTable(); vtkLookupTable *d = c->GetVtkLookupTable(); double *e=d->GetTableValue(pointID); color[0]=e[0]; color[1]=e[1]; color[2]=e[2]; } else { if(!n->GetColor(color, renderer, "unselectedcolor")) n->GetColor(color, renderer); } } // TODO: What about "color" property? 2D Mapper only uses unselected and selected color properties a->GetProperty()->SetColor(color[0], color[1], color[2]); // TODO: label property } //TODO test different pointSpec // TODO "line width" "show contour" "contourcolor" "contoursize" "close contour" "show label", "label" // TODO "show points" vs "visibility" - is visibility evaluated at all? in a superclass maybe? // TODO create lookup tables for all properties that should be evaluated per point. also create editor widgets for these lookup tables! // TODO check if property changes and pointset changes are reflected in the render window immediately. // TODO check behavior with large PointSets // TODO check for memory leaks on adding/deleting points } void mitk::EnhancedPointSetVtkMapper3D::GenerateDataForRenderer( mitk::BaseRenderer * renderer ) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired( renderer, this, GetDataNode() ); if(needGenerateData) { ls->UpdateGenerateDataTime(); this->UpdateVtkObjects(); } ApplyProperties(renderer); } void mitk::EnhancedPointSetVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { // TODO: apply new transform if time step changed //vtkLinearTransform * vtktransform = // this->GetDataNode()->GetVtkTransform(this->GetTimestep()); //m_SelectedActor->SetUserTransform(vtktransform); //m_UnselectedActor->SetUserTransform(vtktransform); //m_ContourActor->SetUserTransform(vtktransform); } void mitk::EnhancedPointSetVtkMapper3D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "line width", mitk::IntProperty::New(2), renderer, overwrite ); node->AddProperty( "pointsize", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty( "selectedcolor", mitk::ColorProperty::New(1.0f, 1.0f, 0.0f), renderer, overwrite); //yellow for selected node->AddProperty( "unselectedcolor", mitk::ColorProperty::New(0.5f, 1.0f, 0.5f), renderer, overwrite); // middle green for unselected node->AddProperty( "color", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); // red as standard node->AddProperty( "show contour", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "contourcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); node->AddProperty( "contoursize", mitk::FloatProperty::New(0.5), renderer, overwrite ); node->AddProperty( "show points", mitk::BoolProperty::New(true), renderer, overwrite ); node->AddProperty( "show label", mitk::BoolProperty::New(false), renderer, overwrite ); node->AddProperty( "label", mitk::StringProperty::New("P"), renderer, overwrite ); node->AddProperty( "opacity", mitk::FloatProperty::New(1.0), renderer, overwrite ); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject( vtkObject* o) { if (o != NULL) o->Delete(); } void mitk::EnhancedPointSetVtkMapper3D::RemoveEntryFromSourceMaps( mitk::PointSet::PointIdentifier pointID ) { ActorMap::iterator aIt = m_PointActors.find(pointID); if (aIt == m_PointActors.end()) return; switch (aIt->second.second) // erase in old map { //TODO: look up representation in a representationlookuptable case PTSTART: //cube m_CubeSources[pointID]->Delete(); m_CubeSources.erase(pointID); break; case PTCORNER: //cone m_ConeSources[pointID]->Delete(); m_ConeSources.erase(pointID); break; case PTEDGE: // cylinder m_CylinderSources[pointID]->Delete(); m_CylinderSources.erase(pointID); break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID]->Delete(); m_SphereSources.erase(pointID); break; } } diff --git a/Modules/MitkExt/Rendering/mitkEnhancedPointSetVtkMapper3D.h b/Modules/MitkExt/Rendering/mitkEnhancedPointSetVtkMapper3D.h index c72e8899e8..aeda02ed7f 100644 --- a/Modules/MitkExt/Rendering/mitkEnhancedPointSetVtkMapper3D.h +++ b/Modules/MitkExt/Rendering/mitkEnhancedPointSetVtkMapper3D.h @@ -1,125 +1,128 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKEnhancedPointSetVtkMapper3D_H_HEADER_INCLUDED_C1907273 #define MITKEnhancedPointSetVtkMapper3D_H_HEADER_INCLUDED_C1907273 #include "mitkCommon.h" #include "MitkExtExports.h" #include "mitkVtkMapper.h" #include "mitkBaseRenderer.h" #include "mitkPointSet.h" #include "mitkVector.h" class vtkActor; class vtkAssembly; class vtkSphereSource; class vtkCubeSource; class vtkConeSource; class vtkCylinderSource; class vtkTubeFilter; class vtkProp; namespace mitk { /** * \brief Alternative Vtk-based 3D mapper for mitk::PointSet * * This class renders mitk::PointSet objects in 3D views. It resembles the * standard mitk::PointSetVtkMapper3D, but is designed to enable single * points to be rendered with individual appearances. * * Instead of assembling one vtkPolyData object containing all points, * a list of VTK source objects (spheres, cubes, cones, ...) is maintained. * Therefore, the application can change the appearance and/or type of a * specific point at runtime, without having to rebuild the * * You should use this class instead of the standard mapper if you * * - change the PointSet very often (by adding or removing points) * - need different representations for points (+++) * - want to change the point representation frequently (+++) * * Note: the class is still in experimental stage, and the points above * marked with (+++) are not yet working correctly. Also, drawing lines * between points (contour mode) is not yet supported. The class will be * extended so that point representations are stored in a lookup table, * which is indexed by point data from the rendered PointSet. * * \warn This mapper requires the PointData container to be the same size * as the point container. * * \sa PointSetVtkMapper3D */ class MitkExt_EXPORT EnhancedPointSetVtkMapper3D : public VtkMapper { public: mitkClassMacro(EnhancedPointSetVtkMapper3D, VtkMapper); itkNewMacro(Self); virtual const mitk::PointSet* GetInput(); virtual vtkProp* GetVtkProp(mitk::BaseRenderer* renderer); virtual void UpdateVtkTransform(mitk::BaseRenderer *renderer); static void SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer = NULL, bool overwrite = false); - void ReleaseGraphicsResources(vtkWindow *renWin); + /* + * \deprecatedSince{2013_12} Use ReleaseGraphicsResources(mitk::BaseRenderer* renderer) instead + */ + DEPRECATED(void ReleaseGraphicsResources(vtkWindow *renWin)){}; LocalStorageHandler m_LSH; protected: EnhancedPointSetVtkMapper3D(); virtual ~EnhancedPointSetVtkMapper3D(); void RemoveEntryFromSourceMaps( mitk::PointSet::PointIdentifier pointID ); void DeleteVtkObject(vtkObject* o); // functor for stl_each in destructor // update all vtk sources, mappers, actors with current data and properties void UpdateVtkObjects(); virtual void GenerateDataForRenderer(mitk::BaseRenderer* renderer); virtual void ApplyProperties(mitk::BaseRenderer* renderer); typedef mitk::PointSet::PointIdentifier PointIdentifier; typedef std::map SphereSourceMap; typedef std::map CubeSourceMap; typedef std::map ConeSourceMap; typedef std::map CylinderSourceMap; typedef std::pair ActorAndPointType; typedef std::map ActorMap; SphereSourceMap m_SphereSources; // stores all sphere sources CubeSourceMap m_CubeSources; // stores all cube sources ConeSourceMap m_ConeSources; // stores all cone sources CylinderSourceMap m_CylinderSources; // stores all cylinder sources ActorMap m_PointActors; // stores an actor for each point(referenced by its ID) and the currently used pointspec = which source type is generating the polydata vtkActor* m_Contour; vtkTubeFilter* m_ContourSource; vtkAssembly* m_PropAssembly; // this contains everything, this will be returned by GetVtkProp() }; } // namespace mitk #endif /* MITKEnhancedPointSetVtkMapper3D_H_HEADER_INCLUDED_C1907273 */ diff --git a/Modules/MitkExt/Rendering/vtkMitkGPUVolumeRayCastMapper.h b/Modules/MitkExt/Rendering/vtkMitkGPUVolumeRayCastMapper.h index fbdd0fa35e..883b6d702b 100644 --- a/Modules/MitkExt/Rendering/vtkMitkGPUVolumeRayCastMapper.h +++ b/Modules/MitkExt/Rendering/vtkMitkGPUVolumeRayCastMapper.h @@ -1,309 +1,312 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkMitkGPUVolumeRayCastMapper.h,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ // .NAME vtkMitkGPUVolumeRayCastMapper - Ray casting performed on the GPU. // .SECTION Description // vtkMitkGPUVolumeRayCastMapper is a volume mapper that performs ray casting on // the GPU using fragment programs. // #ifndef __vtkMitkGPUVolumeRayCastMapper_h #define __vtkMitkGPUVolumeRayCastMapper_h #include "vtkVolumeMapper.h" #include "mitkCommon.h" #include "MitkExtExports.h" // Only with VTK 5.6 or above #if ((VTK_MAJOR_VERSION > 5) || ((VTK_MAJOR_VERSION==5) && (VTK_MINOR_VERSION>=6) )) class vtkVolumeProperty; class vtkRenderWindow; //class vtkKWAMRVolumeMapper; // friend class. class MitkExt_EXPORT vtkMitkGPUVolumeRayCastMapper : public vtkVolumeMapper { public: static vtkMitkGPUVolumeRayCastMapper *New(); vtkTypeRevisionMacro(vtkMitkGPUVolumeRayCastMapper,vtkVolumeMapper); void PrintSelf( ostream& os, vtkIndent indent ); // Description: // If AutoAdjustSampleDistances is on, the the ImageSampleDistance // will be varied to achieve the allocated render time of this // prop (controlled by the desired update rate and any culling in // use). vtkSetClampMacro( AutoAdjustSampleDistances, int, 0, 1 ); vtkGetMacro( AutoAdjustSampleDistances, int ); vtkBooleanMacro( AutoAdjustSampleDistances, int ); // Description: // Set/Get the distance between samples used for rendering // when AutoAdjustSampleDistances is off, or when this mapper // has more than 1 second allocated to it for rendering. // Initial value is 1.0. vtkSetMacro( SampleDistance, float ); vtkGetMacro( SampleDistance, float ); // Description: // Sampling distance in the XY image dimensions. Default value of 1 meaning // 1 ray cast per pixel. If set to 0.5, 4 rays will be cast per pixel. If // set to 2.0, 1 ray will be cast for every 4 (2 by 2) pixels. This value // will be adjusted to meet a desired frame rate when AutoAdjustSampleDistances // is on. vtkSetClampMacro( ImageSampleDistance, float, 0.1f, 100.0f ); vtkGetMacro( ImageSampleDistance, float ); // Description: // This is the minimum image sample distance allow when the image // sample distance is being automatically adjusted. vtkSetClampMacro( MinimumImageSampleDistance, float, 0.1f, 100.0f ); vtkGetMacro( MinimumImageSampleDistance, float ); // Description: // This is the maximum image sample distance allow when the image // sample distance is being automatically adjusted. vtkSetClampMacro( MaximumImageSampleDistance, float, 0.1f, 100.0f ); vtkGetMacro( MaximumImageSampleDistance, float ); // Description: // Set/Get the window / level applied to the final color. // This allows brightness / contrast adjustments on the // final image. // window is the width of the window. // level is the center of the window. // Initial window value is 1.0 // Initial level value is 0.5 // window cannot be null but can be negative, this way // values will be reversed. // |window| can be larger than 1.0 // level can be any real value. vtkSetMacro( FinalColorWindow, float ); vtkGetMacro( FinalColorWindow, float ); vtkSetMacro( FinalColorLevel, float ); vtkGetMacro( FinalColorLevel, float ); // Description: // Maximum size of the 3D texture in GPU memory. // Will default to the size computed from the graphics // card. Can be adjusted by the user. vtkSetMacro( MaxMemoryInBytes, vtkIdType ); vtkGetMacro( MaxMemoryInBytes, vtkIdType ); // Description: // Maximum fraction of the MaxMemoryInBytes that should // be used to hold the texture. Valid values are 0.1 to // 1.0. vtkSetClampMacro( MaxMemoryFraction, float, 0.1f, 1.0f ); vtkGetMacro( MaxMemoryFraction, float ); // Description: // Tells if the mapper will report intermediate progress. // Initial value is true. As the progress works with a GL blocking // call (glFinish()), this can be useful for huge dataset but can // slow down rendering of small dataset. It should be set to true // for big dataset or complex shading and streaming but to false for // small datasets. vtkSetMacro(ReportProgress,bool); vtkGetMacro(ReportProgress,bool); // Description: // Based on hardware and properties, we may or may not be able to // render using 3D texture mapping. This indicates if 3D texture // mapping is supported by the hardware, and if the other extensions // necessary to support the specific properties are available. virtual int IsRenderSupported(vtkRenderWindow *vtkNotUsed(window), vtkVolumeProperty *vtkNotUsed(property)) { return 0; } void CreateCanonicalView( vtkRenderer *ren, vtkVolume *volume, vtkImageData *image, int blend_mode, double viewDirection[3], double viewUp[3] ); void SetMaskInput(vtkImageData *mask); vtkGetObjectMacro(MaskInput, vtkImageData); // Description: // Tells how much mask color transfer function is used compared to the // standard color transfer function when the mask is true. // 0.0 means only standard color transfer function. // 1.0 means only mask color tranfer function. // Initial value is 1.0. vtkSetClampMacro(MaskBlendFactor,float,0.0f,1.0f); vtkGetMacro(MaskBlendFactor,float); //BTX // Description: // WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE // Initialize rendering for this volume. void Render( vtkRenderer *, vtkVolume * ); // Description: // Handled in the subclass - the actual render method // \pre input is up-to-date. virtual void GPURender( vtkRenderer *, vtkVolume *) {} // Description: // WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE // Release any graphics resources that are being consumed by this mapper. // The parameter window could be used to determine which graphic // resources to release. - void ReleaseGraphicsResources(vtkWindow *) {}; + // \deprecatedSince{2013_12} + DEPRECATED(void ReleaseGraphicsResources(vtkWindow *)) + { + }; // Description: // Return how much the dataset has to be reduced in each dimension to // fit on the GPU. If the value is 1.0, there is no need to reduce the // dataset. // \pre the calling thread has a current OpenGL context. // \pre mapper_supported: IsRenderSupported(renderer->GetRenderWindow(),0) // The computation is based on hardware limits (3D texture indexable size) // and MaxMemoryInBytes. // \post valid_i_ratio: ratio[0]>0 && ratio[0]<=1.0 // \post valid_j_ratio: ratio[1]>0 && ratio[1]<=1.0 // \post valid_k_ratio: ratio[2]>0 && ratio[2]<=1.0 virtual void GetReductionRatio(double ratio[3])=0; //ETX protected: vtkMitkGPUVolumeRayCastMapper(); ~vtkMitkGPUVolumeRayCastMapper(); // Check to see that the render will be OK int ValidateRender( vtkRenderer *, vtkVolume * ); // Special version of render called during the creation // of a canonical view. void CanonicalViewRender( vtkRenderer *, vtkVolume * ); // Methods called by the AMR Volume Mapper. virtual void PreRender(vtkRenderer *ren, vtkVolume *vol, double datasetBounds[6], double scalarRange[2], int numberOfScalarComponents, unsigned int numberOfLevels)=0; // \pre input is up-to-date virtual void RenderBlock(vtkRenderer *ren, vtkVolume *vol, unsigned int level)=0; virtual void PostRender(vtkRenderer *ren, int numberOfScalarComponents)=0; // Description: // Called by the AMR Volume Mapper. // Set the flag that tells if the scalars are on point data (0) or // cell data (1). void SetCellFlag(int cellFlag); // The distance between sample points along the ray float SampleDistance; float ImageSampleDistance; float MinimumImageSampleDistance; float MaximumImageSampleDistance; int AutoAdjustSampleDistances; int SmallVolumeRender; double BigTimeToDraw; double SmallTimeToDraw; float FinalColorWindow; float FinalColorLevel; vtkIdType MaxMemoryInBytes; float MaxMemoryFraction; // 1 if we are generating the canonical image, 0 otherwise int GeneratingCanonicalView; vtkImageData *CanonicalViewImageData; // Description: // Set the mapper in AMR Mode or not. Initial value is false. // Called only by the vtkKWAMRVolumeMapper vtkSetClampMacro(AMRMode,int,0,1); vtkGetMacro(AMRMode,int); vtkBooleanMacro(AMRMode,int); int AMRMode; int CellFlag; // point data or cell data (or field data, not handled) ? // Description: // Compute the cropping planes clipped by the bounds of the volume. // The result is put into this->ClippedCroppingRegionPlanes. // NOTE: IT WILL BE MOVED UP TO vtkVolumeMapper after bullet proof usage // in this mapper. Other subclasses will use the ClippedCroppingRegionsPlanes // members instead of CroppingRegionPlanes. // \pre volume_exists: this->GetInput()!=0 // \pre valid_cropping: this->Cropping && // this->CroppingRegionPlanes[0]CroppingRegionPlanes[1] && // this->CroppingRegionPlanes[2]CroppingRegionPlanes[3] && // this->CroppingRegionPlanes[4]CroppingRegionPlanes[5]) virtual void ClipCroppingRegionPlanes(); double ClippedCroppingRegionPlanes[6]; bool ReportProgress; vtkImageData *MaskInput; float MaskBlendFactor; vtkGetObjectMacro(TransformedInput, vtkImageData); void SetTransformedInput(vtkImageData*); vtkImageData* TransformedInput; // Description: // This is needed only to check if the input data has been changed since the last // Render() call. vtkImageData* LastInput; private: vtkMitkGPUVolumeRayCastMapper(const vtkMitkGPUVolumeRayCastMapper&); // Not implemented. void operator=(const vtkMitkGPUVolumeRayCastMapper&); // Not implemented. }; #endif #endif diff --git a/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp b/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp index 584d0b5d28..f5486a7af1 100644 --- a/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp +++ b/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.cpp @@ -1,7357 +1,7494 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkMitkOpenGLGPUVolumeRayCastMapper.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkMitkOpenGLGPUVolumeRayCastMapper.h" // Only with VTK 5.6 or above #if ((VTK_MAJOR_VERSION > 5) || ((VTK_MAJOR_VERSION==5) && (VTK_MINOR_VERSION>=6) )) #include "vtkObjectFactory.h" #include "vtkVolume.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkCamera.h" #include "vtkMatrix4x4.h" #include "vtkImageData.h" #include "vtkTimerLog.h" #include "vtkVolumeProperty.h" #include "vtkColorTransferFunction.h" #include "vtkPiecewiseFunction.h" #include "vtkOpenGLExtensionManager.h" #include "vtkgl.h" #ifndef VTK_IMPLEMENT_MESA_CXX # include "vtkOpenGL.h" #endif #include #include #include #include #include #include "vtkClipDataSet.h" #include "vtkCellArray.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkGeometryFilter.h" #include "vtkMath.h" #include "vtkPlane.h" #include "vtkPlaneCollection.h" #include "vtkPlanes.h" #include "vtkPolyData.h" #include "vtkPointData.h" #include "vtkCellData.h" #include "vtkPoints.h" #include "vtkUnsignedCharArray.h" #include "vtkUnsignedShortArray.h" #include "vtkUnsignedIntArray.h" #include "vtkUnstructuredGrid.h" #include "vtkVoxel.h" #include "vtkClipConvexPolyData.h" #include "vtkClipPolyData.h" #include "vtkDensifyPolyData.h" #include "vtkImageResample.h" #include #include // qsort() #include "vtkDataSetTriangleFilter.h" #include "vtkAbstractArray.h" // required if compiled against VTK 5.0 #include "vtkTessellatedBoxSource.h" #include "vtkCleanPolyData.h" #include "vtkCommand.h" // for VolumeMapperRender{Start|End|Progress}Event #include "vtkPerlinNoise.h" #include #include "vtkStdString.h" //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkUnsupportedRequiredExtensionsStringStream { public: vtkstd::ostringstream Stream; vtkUnsupportedRequiredExtensionsStringStream() { } private: // undefined copy constructor. vtkUnsupportedRequiredExtensionsStringStream(const vtkUnsupportedRequiredExtensionsStringStream &other); // undefined assignment operator. vtkUnsupportedRequiredExtensionsStringStream &operator=(const vtkUnsupportedRequiredExtensionsStringStream &other); }; class vtkMapDataArrayTextureId { public: vtkstd::map Map; vtkMapDataArrayTextureId() { } private: // undefined copy constructor. vtkMapDataArrayTextureId(const vtkMapDataArrayTextureId &other); // undefined assignment operator. vtkMapDataArrayTextureId &operator=(const vtkMapDataArrayTextureId &other); }; class vtkMapMaskTextureId { public: vtkstd::map Map; vtkMapMaskTextureId() { } private: // undefined copy constructor. vtkMapMaskTextureId(const vtkMapMaskTextureId &other); // undefined assignment operator. vtkMapMaskTextureId &operator=(const vtkMapMaskTextureId &other); }; //----------------------------------------------------------------------------- extern const char *vtkMitkGPUVolumeRayCastMapper_CompositeFS; extern const char *vtkMitkGPUVolumeRayCastMapper_CompositeCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_CompositeNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_HeaderFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPFourDependentFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPFourDependentCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPFourDependentNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MIPNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_ParallelProjectionFS; extern const char *vtkMitkGPUVolumeRayCastMapper_PerspectiveProjectionFS; extern const char *vtkMitkGPUVolumeRayCastMapper_ScaleBiasFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPFourDependentFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPFourDependentCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPFourDependentNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_MinIPNoCroppingFS; extern const char *vtkMitkGPUVolumeRayCastMapper_CompositeMaskFS; extern const char *vtkMitkGPUVolumeRayCastMapper_NoShadeFS; extern const char *vtkMitkGPUVolumeRayCastMapper_ShadeFS; extern const char *vtkMitkGPUVolumeRayCastMapper_OneComponentFS; extern const char *vtkMitkGPUVolumeRayCastMapper_FourComponentsFS; enum { vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized=-1, // not init vtkMitkOpenGLGPUVolumeRayCastMapperProjectionPerspective=0, // false vtkMitkOpenGLGPUVolumeRayCastMapperProjectionParallel=1 // true }; enum { vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized, vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP, vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIPFourDependent, vtkMitkOpenGLGPUVolumeRayCastMapperMethodComposite, vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIP, vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIPFourDependent, vtkMitkOpenGLGPUVolumeRayCastMapperMethodCompositeMask }; // component implementation enum { vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized=-1, // not init vtkMitkOpenGLGPUVolumeRayCastMapperComponentOne=0, // false vtkMitkOpenGLGPUVolumeRayCastMapperComponentFour=1, // true vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotUsed=2 // when not composite }; // Shade implementation enum { vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized=-1, // not init vtkMitkOpenGLGPUVolumeRayCastMapperShadeNo=0, // false vtkMitkOpenGLGPUVolumeRayCastMapperShadeYes=1, // true vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotUsed=2 // when not composite }; // Cropping implementation enum { vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized, vtkMitkOpenGLGPUVolumeRayCastMapperCompositeCropping, vtkMitkOpenGLGPUVolumeRayCastMapperCompositeNoCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMIPCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMIPNoCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMIPFourDependentCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMIPFourDependentNoCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMinIPCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMinIPNoCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMinIPFourDependentCropping, vtkMitkOpenGLGPUVolumeRayCastMapperMinIPFourDependentNoCropping }; enum { vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectDepthMap=0, // 2d texture vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront // 2d texture }; const int vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects=vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+2; const int vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize=1024; //power of two #ifndef VTK_IMPLEMENT_MESA_CXX vtkCxxRevisionMacro(vtkMitkOpenGLGPUVolumeRayCastMapper, "$Revision: 1.9 $"); vtkStandardNewMacro(vtkMitkOpenGLGPUVolumeRayCastMapper); #endif //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkOpacityTable { public: vtkOpacityTable() { this->TextureId=0; this->LastBlendMode=vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND; this->LastSampleDistance=1.0; this->Table=0; this->Loaded=false; this->LastLinearInterpolation=false; } ~vtkOpacityTable() { if(this->TextureId!=0) { glDeleteTextures(1,&this->TextureId); this->TextureId=0; } if(this->Table!=0) { delete[] this->Table; this->Table=0; } } bool IsLoaded() { return this->Loaded; } void Bind() { assert("pre: uptodate" && this->Loaded); glBindTexture(GL_TEXTURE_1D,this->TextureId); } // \pre the active texture is set to TEXTURE2 void Update(vtkPiecewiseFunction *scalarOpacity, int blendMode, double sampleDistance, double range[2], double unitDistance, bool linearInterpolation) { assert("pre: scalarOpacity_exists" && scalarOpacity!=0); bool needUpdate=false; if(this->TextureId==0) { glGenTextures(1,&this->TextureId); needUpdate=true; } glBindTexture(GL_TEXTURE_1D,this->TextureId); if(needUpdate) { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); } if(scalarOpacity->GetMTime() > this->BuildTime || (this->LastBlendMode!=blendMode) || (blendMode==vtkVolumeMapper::COMPOSITE_BLEND && this->LastSampleDistance!=sampleDistance) || needUpdate || !this->Loaded) { this->Loaded=false; if(this->Table==0) { this->Table= new float[vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize]; } scalarOpacity->GetTable(range[0],range[1], vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize, this->Table); this->LastBlendMode=blendMode; // Correct the opacity array for the spacing between the planes if we // are using a composite blending operation if(blendMode==vtkVolumeMapper::COMPOSITE_BLEND) { float *ptr=this->Table; double factor=sampleDistance/unitDistance; int i=0; while(i0.0001f) { *ptr=static_cast(1.0-pow(1.0-static_cast(*ptr), factor)); } ++ptr; ++i; } this->LastSampleDistance=sampleDistance; } glTexImage1D(GL_TEXTURE_1D,0,GL_ALPHA16, vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize,0, GL_ALPHA,GL_FLOAT,this->Table); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("1d opacity texture is too large"); this->Loaded=true; this->BuildTime.Modified(); } needUpdate=needUpdate || this->LastLinearInterpolation!=linearInterpolation; if(needUpdate) { this->LastLinearInterpolation=linearInterpolation; GLint value; if(linearInterpolation) { value=GL_LINEAR; } else { value=GL_NEAREST; } glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,value); glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,value); } } protected: GLuint TextureId; int LastBlendMode; double LastSampleDistance; vtkTimeStamp BuildTime; float *Table; bool Loaded; bool LastLinearInterpolation; }; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkOpacityTables { public: vtkstd::vector Vector; vtkOpacityTables(size_t numberOfLevels) : Vector(numberOfLevels) { } private: // undefined copy constructor. vtkOpacityTables(const vtkOpacityTables &other); // undefined assignment operator. vtkOpacityTables &operator=(const vtkOpacityTables &other); }; //----------------------------------------------------------------------------- class vtkRGBTable { public: vtkRGBTable() { this->TextureId=0; this->Table=0; this->Loaded=false; this->LastLinearInterpolation=false; } ~vtkRGBTable() { if(this->TextureId!=0) { glDeleteTextures(1,&this->TextureId); this->TextureId=0; } if(this->Table!=0) { delete[] this->Table; this->Table=0; } } bool IsLoaded() { return this->Loaded; } void Bind() { assert("pre: uptodate" && this->Loaded); glBindTexture(GL_TEXTURE_1D,this->TextureId); } // \pre the active texture is set properly. (default color, // mask1, mask2,..) void Update(vtkColorTransferFunction *scalarRGB, double range[2], bool linearInterpolation) { assert("pre: scalarRGB_exists" && scalarRGB!=0); bool needUpdate=false; if(this->TextureId==0) { glGenTextures(1,&this->TextureId); needUpdate=true; } glBindTexture(GL_TEXTURE_1D,this->TextureId); if(needUpdate) { glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); } if(scalarRGB->GetMTime() > this->BuildTime || needUpdate || !this->Loaded) { this->Loaded=false; if(this->Table==0) { this->Table= new float[vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize*3]; } scalarRGB->GetTable(range[0],range[1], vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize, this->Table); glTexImage1D(GL_TEXTURE_1D,0,GL_RGB16, vtkMitkOpenGLGPUVolumeRayCastMapperOpacityTableSize,0, GL_RGB,GL_FLOAT,this->Table); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("1d RGB texture is too large"); this->Loaded=true; this->BuildTime.Modified(); } needUpdate=needUpdate || this->LastLinearInterpolation!=linearInterpolation; if(needUpdate) { this->LastLinearInterpolation=linearInterpolation; GLint value; if(linearInterpolation) { value=GL_LINEAR; } else { value=GL_NEAREST; } glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,value); glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,value); } } protected: GLuint TextureId; vtkTimeStamp BuildTime; float *Table; bool Loaded; bool LastLinearInterpolation; }; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkKWScalarField { public: vtkKWScalarField() { this->TextureId=0; this->Loaded=false; this->Supports_GL_ARB_texture_float=false; this->LoadedTableRange[0]=0.0; this->LoadedTableRange[1]=1.0; this->LoadedExtent[0]=VTK_INT_MAX; this->LoadedExtent[1]=VTK_INT_MIN; this->LoadedExtent[2]=VTK_INT_MAX; this->LoadedExtent[3]=VTK_INT_MIN; this->LoadedExtent[4]=VTK_INT_MAX; this->LoadedExtent[5]=VTK_INT_MIN; } ~vtkKWScalarField() { if(this->TextureId!=0) { glDeleteTextures(1,&this->TextureId); this->TextureId=0; } } vtkTimeStamp GetBuildTime() { return this->BuildTime; } void Bind() { assert("pre: uptodate" && this->Loaded); glBindTexture(vtkgl::TEXTURE_3D,this->TextureId); } void Update(vtkImageData *input, int cellFlag, int textureExtent[6], int scalarMode, int arrayAccessMode, int arrayId, const char *arrayName, bool linearInterpolation, double tableRange[2], int maxMemoryInBytes) { bool needUpdate=false; bool modified=false; if(this->TextureId==0) { glGenTextures(1,&this->TextureId); needUpdate=true; } glBindTexture(vtkgl::TEXTURE_3D,this->TextureId); int obsolete=needUpdate || !this->Loaded || input->GetMTime()>this->BuildTime; if(!obsolete) { obsolete=cellFlag!=this->LoadedCellFlag; int i=0; while(!obsolete && i<6) { obsolete=obsolete || this->LoadedExtent[i]>textureExtent[i]; ++i; obsolete=obsolete || this->LoadedExtent[i]LoadedTableRange[0]!=tableRange[0] || this->LoadedTableRange[1]!=tableRange[1]; } if(obsolete) { this->Loaded=false; int dim[3]; input->GetDimensions(dim); GLint internalFormat=0; GLenum format=0; GLenum type=0; // shift then scale: y:=(x+shift)*scale double shift=0.0; double scale=1.0; int needTypeConversion=0; vtkDataArray *sliceArray=0; vtkDataArray *scalars= vtkAbstractMapper::GetScalars(input,scalarMode,arrayAccessMode, arrayId,arrayName, this->LoadedCellFlag); // DONT USE GetScalarType() or GetNumberOfScalarComponents() on // ImageData as it deals only with point data... int scalarType=scalars->GetDataType(); if(scalars->GetNumberOfComponents()==4) { // this is RGBA, unsigned char only internalFormat=GL_RGBA16; format=GL_RGBA; type=GL_UNSIGNED_BYTE; } else { // input->GetNumberOfScalarComponents()==1 switch(scalarType) { case VTK_FLOAT: if(this->Supports_GL_ARB_texture_float) { internalFormat=vtkgl::INTENSITY16F_ARB; } else { internalFormat=GL_INTENSITY16; } format=GL_RED; type=GL_FLOAT; shift=-tableRange[0]; scale=1/(tableRange[1]-tableRange[0]); break; case VTK_UNSIGNED_CHAR: internalFormat=GL_INTENSITY8; format=GL_RED; type=GL_UNSIGNED_BYTE; shift=-tableRange[0]/VTK_UNSIGNED_CHAR_MAX; scale= VTK_UNSIGNED_CHAR_MAX/(tableRange[1]-tableRange[0]); break; case VTK_SIGNED_CHAR: internalFormat=GL_INTENSITY8; format=GL_RED; type=GL_BYTE; shift=-(2*tableRange[0]+1)/VTK_UNSIGNED_CHAR_MAX; scale=VTK_SIGNED_CHAR_MAX/(tableRange[1]-tableRange[0]); break; case VTK_CHAR: // not supported assert("check: impossible case" && 0); break; case VTK_BIT: // not supported assert("check: impossible case" && 0); break; case VTK_ID_TYPE: // not supported assert("check: impossible case" && 0); break; case VTK_INT: internalFormat=GL_INTENSITY16; format=GL_RED; type=GL_INT; shift=-(2*tableRange[0]+1)/VTK_UNSIGNED_INT_MAX; scale=VTK_INT_MAX/(tableRange[1]-tableRange[0]); break; case VTK_DOUBLE: case VTK___INT64: case VTK_LONG: case VTK_LONG_LONG: case VTK_UNSIGNED___INT64: case VTK_UNSIGNED_LONG: case VTK_UNSIGNED_LONG_LONG: needTypeConversion=1; // to float if(this->Supports_GL_ARB_texture_float) { internalFormat=vtkgl::INTENSITY16F_ARB; } else { internalFormat=GL_INTENSITY16; } format=GL_RED; type=GL_FLOAT; shift=-tableRange[0]; scale=1/(tableRange[1]-tableRange[0]); sliceArray=vtkFloatArray::New(); break; case VTK_SHORT: internalFormat=GL_INTENSITY16; format=GL_RED; type=GL_SHORT; shift=-(2*tableRange[0]+1)/VTK_UNSIGNED_SHORT_MAX; scale=VTK_SHORT_MAX/(tableRange[1]-tableRange[0]); break; case VTK_STRING: // not supported assert("check: impossible case" && 0); break; case VTK_UNSIGNED_SHORT: internalFormat=GL_INTENSITY16; format=GL_RED; type=GL_UNSIGNED_SHORT; shift=-tableRange[0]/VTK_UNSIGNED_SHORT_MAX; scale= VTK_UNSIGNED_SHORT_MAX/(tableRange[1]-tableRange[0]); break; case VTK_UNSIGNED_INT: internalFormat=GL_INTENSITY16; format=GL_RED; type=GL_UNSIGNED_INT; shift=-tableRange[0]/VTK_UNSIGNED_INT_MAX; scale=VTK_UNSIGNED_INT_MAX/(tableRange[1]-tableRange[0]); break; default: assert("check: impossible case" && 0); break; } } // Enough memory? int textureSize[3]; int i=0; while(i<3) { textureSize[i]=textureExtent[2*i+1]-textureExtent[2*i]+1; ++i; } GLint width; glGetIntegerv(vtkgl::MAX_3D_TEXTURE_SIZE,&width); this->Loaded=textureSize[0]<=width && textureSize[1]<=width && textureSize[2]<=width; if(this->Loaded) { // so far, so good. the texture size is theorically small enough // for OpenGL vtkgl::TexImage3D(vtkgl::PROXY_TEXTURE_3D,0,internalFormat, textureSize[0],textureSize[1],textureSize[2],0, format,type,0); glGetTexLevelParameteriv(vtkgl::PROXY_TEXTURE_3D,0,GL_TEXTURE_WIDTH, &width); this->Loaded=width!=0; if(this->Loaded) { // so far, so good but some cards always succeed with a proxy texture // let's try to actually allocate.. vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,internalFormat,textureSize[0], textureSize[1],textureSize[2],0,format,type,0); GLenum errorCode=glGetError(); this->Loaded=errorCode!=GL_OUT_OF_MEMORY; if(this->Loaded) { // so far, so good, actual allocation succeeded. if(errorCode!=GL_NO_ERROR) { cout<<"after try to load the texture"; cout<<" ERROR (x"<(errorCode)); cout<Loaded=textureSize[0]*textureSize[1]* textureSize[2]*vtkAbstractArray::GetDataTypeSize(scalarType)* scalars->GetNumberOfComponents()<=maxMemoryInBytes; if(this->Loaded) { // OK, we consider the allocation above succeeded... // If it actually didn't the only to fix it for the user // is to decrease the value of this->MaxMemoryInBytes. // enough memory! We can load the scalars! double bias=shift*scale; // we don't clamp to edge because for the computation of the // gradient on the border we need some external value. glTexParameterf(vtkgl::TEXTURE_3D,vtkgl::TEXTURE_WRAP_R,vtkgl::CLAMP_TO_EDGE); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_WRAP_S,vtkgl::CLAMP_TO_EDGE); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_WRAP_T,vtkgl::CLAMP_TO_EDGE); GLfloat borderColor[4]={0.0,0.0,0.0,0.0}; glTexParameterfv(vtkgl::TEXTURE_3D,GL_TEXTURE_BORDER_COLOR, borderColor); if(needTypeConversion) { // Convert and send to the GPU, z-slice by z-slice. // Allocate memory on the GPU (NULL data pointer with the right // dimensions) // Here we are assuming that GL_ARB_texture_non_power_of_two is // available glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // memory allocation is already done. // Send the slices: // allocate CPU memory for a slice. sliceArray->SetNumberOfComponents(1); // FB TODO CHECK THAT sliceArray->SetNumberOfTuples(textureSize[0]*textureSize[1]); void *slicePtr=sliceArray->GetVoidPointer(0); int k=0; int kInc=(dim[0]-cellFlag)*(dim[1]-cellFlag); int kOffset=(textureExtent[4]*(dim[1]-cellFlag) +textureExtent[2])*(dim[0]-cellFlag) +textureExtent[0]; while(kSetTuple1(jDestOffset+i, (scalars->GetTuple1(kOffset+jOffset +i) +shift)*scale); ++i; } ++j; jOffset+=dim[0]-cellFlag; jDestOffset+=textureSize[0]; } // Here we are assuming that GL_ARB_texture_non_power_of_two is // available vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D, 0, 0,0,k, textureSize[0],textureSize[1], 1, // depth is 1, not 0! format,type, slicePtr); ++k; kOffset+=kInc; } sliceArray->Delete(); } else { // One chunk of data to the GPU. // It works for the whole volume or for a subvolume. // Here we are assuming that GL_ARB_texture_non_power_of_two is // available // make sure any previous OpenGL call is executed and will not // be disturbed by our PixelTransfer value glFinish(); glPixelTransferf(GL_RED_SCALE,static_cast(scale)); glPixelTransferf(GL_RED_BIAS,static_cast(bias)); glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); if(!(textureExtent[1]-textureExtent[0]+cellFlag==dim[0])) { glPixelStorei(GL_UNPACK_ROW_LENGTH,dim[0]-cellFlag); } if(!(textureExtent[3]-textureExtent[2]+cellFlag==dim[1])) { glPixelStorei(vtkgl::UNPACK_IMAGE_HEIGHT_EXT, dim[1]-cellFlag); } void *dataPtr=scalars->GetVoidPointer( ((textureExtent[4]*(dim[1]-cellFlag)+textureExtent[2]) *(dim[0]-cellFlag)+textureExtent[0]) *scalars->GetNumberOfComponents()); if(1) // !this->SupportsPixelBufferObjects) { vtkgl::TexImage3D(vtkgl::TEXTURE_3D, 0, internalFormat, textureSize[0],textureSize[1],textureSize[2], 0,format,type,dataPtr); } else { GLuint pbo=0; vtkgl::GenBuffers(1,&pbo); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("genbuffer"); vtkgl::BindBuffer(vtkgl::PIXEL_UNPACK_BUFFER,pbo); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("binbuffer"); vtkgl::GLsizeiptr texSize= textureSize[0]*textureSize[1]*textureSize[2]* vtkAbstractArray::GetDataTypeSize(scalarType)* scalars->GetNumberOfComponents(); vtkgl::BufferData(vtkgl::PIXEL_UNPACK_BUFFER,texSize,dataPtr, vtkgl::STREAM_DRAW); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("bufferdata"); vtkgl::TexImage3D(vtkgl::TEXTURE_3D, 0, internalFormat, textureSize[0],textureSize[1],textureSize[2], 0,format,type,0); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("teximage3d"); vtkgl::BindBuffer(vtkgl::PIXEL_UNPACK_BUFFER,0); vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("bindbuffer to 0"); vtkgl::DeleteBuffers(1,&pbo); } vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError("3d texture is too large2"); // make sure TexImage3D is executed with our PixelTransfer mode glFinish(); // Restore the default values. glPixelStorei(GL_UNPACK_ROW_LENGTH,0); glPixelStorei(vtkgl::UNPACK_IMAGE_HEIGHT_EXT,0); glPixelTransferf(GL_RED_SCALE,1.0); glPixelTransferf(GL_RED_BIAS,0.0); } this->LoadedCellFlag=cellFlag; i=0; while(i<6) { this->LoadedExtent[i]=textureExtent[i]; ++i; } double spacing[3]; double origin[3]; input->GetSpacing(spacing); input->GetOrigin(origin); int swapBounds[3]; swapBounds[0]=(spacing[0]<0); swapBounds[1]=(spacing[1]<0); swapBounds[2]=(spacing[2]<0); if(!this->LoadedCellFlag) // loaded extents represent points { // slabsPoints[i]=(slabsDataSet[i] - origin[i/2]) / spacing[i/2]; // in general, x=o+i*spacing. // if spacing is positive min extent match the min of the // bounding box // and the max extent match the max of the bounding box // if spacing is negative min extent match the max of the // bounding box // and the max extent match the min of the bounding box // if spacing is negative, we may have to rethink the equation // between real point and texture coordinate... this->LoadedBounds[0]=origin[0]+ static_cast(this->LoadedExtent[0+swapBounds[0]])*spacing[0]; this->LoadedBounds[2]=origin[1]+ static_cast(this->LoadedExtent[2+swapBounds[1]])*spacing[1]; this->LoadedBounds[4]=origin[2]+ static_cast(this->LoadedExtent[4+swapBounds[2]])*spacing[2]; this->LoadedBounds[1]=origin[0]+ static_cast(this->LoadedExtent[1-swapBounds[0]])*spacing[0]; this->LoadedBounds[3]=origin[1]+ static_cast(this->LoadedExtent[3-swapBounds[1]])*spacing[1]; this->LoadedBounds[5]=origin[2]+ static_cast(this->LoadedExtent[5-swapBounds[2]])*spacing[2]; } else // loaded extents represent cells { int wholeTextureExtent[6]; input->GetExtent(wholeTextureExtent); i=1; while(i<6) { wholeTextureExtent[i]--; i+=2; } i=0; while(i<3) { if(this->LoadedExtent[2*i]==wholeTextureExtent[2*i]) { this->LoadedBounds[2*i+swapBounds[i]]=origin[i]; } else { this->LoadedBounds[2*i+swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i])+0.5)*spacing[i]; } if(this->LoadedExtent[2*i+1]==wholeTextureExtent[2*i+1]) { this->LoadedBounds[2*i+1-swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i+1])+1.0)*spacing[i]; } else { this->LoadedBounds[2*i+1-swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i+1])+0.5)*spacing[i]; } ++i; } } this->LoadedTableRange[0]=tableRange[0]; this->LoadedTableRange[1]=tableRange[1]; modified=true; } // if enough memory else { } } //load fail with out of memory else { } } // proxy ok else { // proxy failed } } else { // out of therical limitationa } } // if obsolete if(this->Loaded && (needUpdate || modified || linearInterpolation!=this->LinearInterpolation)) { this->LinearInterpolation=linearInterpolation; if(this->LinearInterpolation) { glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } modified=true; } if(modified) { this->BuildTime.Modified(); } } double *GetLoadedBounds() { assert("pre: loaded" && this->Loaded); return this->LoadedBounds; } vtkIdType *GetLoadedExtent() { assert("pre: loaded" && this->Loaded); return this->LoadedExtent; } int GetLoadedCellFlag() { assert("pre: loaded" && this->Loaded); return this->LoadedCellFlag; } bool IsLoaded() { return this->Loaded; } bool GetSupports_GL_ARB_texture_float() { return this->Supports_GL_ARB_texture_float; } void SetSupports_GL_ARB_texture_float(bool value) { this->Supports_GL_ARB_texture_float=value; } protected: GLuint TextureId; vtkTimeStamp BuildTime; double LoadedBounds[6]; vtkIdType LoadedExtent[6]; int LoadedCellFlag; bool Loaded; bool LinearInterpolation; bool Supports_GL_ARB_texture_float; double LoadedTableRange[2]; }; //----------------------------------------------------------------------------- class vtkKWMask { public: vtkKWMask() { this->TextureId=0; this->Loaded=false; this->LoadedExtent[0]=VTK_INT_MAX; this->LoadedExtent[1]=VTK_INT_MIN; this->LoadedExtent[2]=VTK_INT_MAX; this->LoadedExtent[3]=VTK_INT_MIN; this->LoadedExtent[4]=VTK_INT_MAX; this->LoadedExtent[5]=VTK_INT_MIN; } ~vtkKWMask() { if(this->TextureId!=0) { glDeleteTextures(1,&this->TextureId); this->TextureId=0; } } vtkTimeStamp GetBuildTime() { return this->BuildTime; } // \pre vtkgl::ActiveTexture(vtkgl::TEXTURE7) has to be called first. void Bind() { assert("pre: uptodate" && this->Loaded); glBindTexture(vtkgl::TEXTURE_3D,this->TextureId); } // \pre vtkgl::ActiveTexture(vtkgl::TEXTURE7) has to be called first. void Update(vtkImageData *input, int cellFlag, int textureExtent[6], int scalarMode, int arrayAccessMode, int arrayId, const char *arrayName, int maxMemoryInBytes) { bool needUpdate=false; bool modified=false; if(this->TextureId==0) { glGenTextures(1,&this->TextureId); needUpdate=true; } glBindTexture(vtkgl::TEXTURE_3D,this->TextureId); int obsolete=needUpdate || !this->Loaded || input->GetMTime()>this->BuildTime; if(!obsolete) { obsolete=cellFlag!=this->LoadedCellFlag; int i=0; while(!obsolete && i<6) { obsolete=obsolete || this->LoadedExtent[i]>textureExtent[i]; ++i; obsolete=obsolete || this->LoadedExtent[i]Loaded=false; int dim[3]; input->GetDimensions(dim); vtkDataArray *scalars= vtkAbstractMapper::GetScalars(input,scalarMode,arrayAccessMode, arrayId,arrayName, this->LoadedCellFlag); // DONT USE GetScalarType() or GetNumberOfScalarComponents() on // ImageData as it deals only with point data... int scalarType=scalars->GetDataType(); if(scalarType!=VTK_UNSIGNED_CHAR) { cout <<"mask should be VTK_UNSIGNED_CHAR." << endl; } if(scalars->GetNumberOfComponents()!=1) { cout <<"mask should be a one-component scalar field." << endl; } GLint internalFormat=GL_ALPHA8; GLenum format=GL_ALPHA; GLenum type=GL_UNSIGNED_BYTE; // Enough memory? int textureSize[3]; int i=0; while(i<3) { textureSize[i]=textureExtent[2*i+1]-textureExtent[2*i]+1; ++i; } GLint width; glGetIntegerv(vtkgl::MAX_3D_TEXTURE_SIZE,&width); this->Loaded=textureSize[0]<=width && textureSize[1]<=width && textureSize[2]<=width; if(this->Loaded) { // so far, so good. the texture size is theorically small enough // for OpenGL vtkgl::TexImage3D(vtkgl::PROXY_TEXTURE_3D,0,internalFormat, textureSize[0],textureSize[1],textureSize[2],0, format,type,0); glGetTexLevelParameteriv(vtkgl::PROXY_TEXTURE_3D,0,GL_TEXTURE_WIDTH, &width); this->Loaded=width!=0; if(this->Loaded) { // so far, so good but some cards always succeed with a proxy texture // let's try to actually allocate.. vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,internalFormat,textureSize[0], textureSize[1],textureSize[2],0,format,type,0); GLenum errorCode=glGetError(); this->Loaded=errorCode!=GL_OUT_OF_MEMORY; if(this->Loaded) { // so far, so good, actual allocation succeeded. if(errorCode!=GL_NO_ERROR) { cout<<"after try to load the texture"; cout<<" ERROR (x"<(errorCode)); cout<Loaded=textureSize[0]*textureSize[1]* textureSize[2]*vtkAbstractArray::GetDataTypeSize(scalarType)* scalars->GetNumberOfComponents()<=maxMemoryInBytes; if(this->Loaded) { // OK, we consider the allocation above succeeded... // If it actually didn't the only to fix it for the user // is to decrease the value of this->MaxMemoryInBytes. // enough memory! We can load the scalars! // we don't clamp to edge because for the computation of the // gradient on the border we need some external value. glTexParameterf(vtkgl::TEXTURE_3D,vtkgl::TEXTURE_WRAP_R,vtkgl::CLAMP_TO_EDGE); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_WRAP_S,vtkgl::CLAMP_TO_EDGE); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_WRAP_T,vtkgl::CLAMP_TO_EDGE); GLfloat borderColor[4]={0.0,0.0,0.0,0.0}; glTexParameterfv(vtkgl::TEXTURE_3D,GL_TEXTURE_BORDER_COLOR, borderColor); glPixelTransferf(GL_ALPHA_SCALE,1.0); glPixelTransferf(GL_ALPHA_BIAS,0.0); glPixelStorei(GL_UNPACK_ALIGNMENT,1); if(!(textureExtent[1]-textureExtent[0]+cellFlag==dim[0])) { glPixelStorei(GL_UNPACK_ROW_LENGTH,dim[0]-cellFlag); } if(!(textureExtent[3]-textureExtent[2]+cellFlag==dim[1])) { glPixelStorei(vtkgl::UNPACK_IMAGE_HEIGHT_EXT, dim[1]-cellFlag); } void *dataPtr=scalars->GetVoidPointer( ((textureExtent[4]*(dim[1]-cellFlag)+textureExtent[2]) *(dim[0]-cellFlag)+textureExtent[0]) *scalars->GetNumberOfComponents()); vtkgl::TexImage3D(vtkgl::TEXTURE_3D, 0, internalFormat, textureSize[0],textureSize[1],textureSize[2], 0,format,type,dataPtr); // Restore the default values. glPixelStorei(GL_UNPACK_ROW_LENGTH,0); glPixelStorei(vtkgl::UNPACK_IMAGE_HEIGHT_EXT,0); glPixelTransferf(GL_ALPHA_SCALE,1.0); glPixelTransferf(GL_ALPHA_BIAS,0.0); this->LoadedCellFlag=cellFlag; i=0; while(i<6) { this->LoadedExtent[i]=textureExtent[i]; ++i; } double spacing[3]; double origin[3]; input->GetSpacing(spacing); input->GetOrigin(origin); int swapBounds[3]; swapBounds[0]=(spacing[0]<0); swapBounds[1]=(spacing[1]<0); swapBounds[2]=(spacing[2]<0); if(!this->LoadedCellFlag) // loaded extents represent points { // slabsPoints[i]=(slabsDataSet[i] - origin[i/2]) / spacing[i/2]; // in general, x=o+i*spacing. // if spacing is positive min extent match the min of the // bounding box // and the max extent match the max of the bounding box // if spacing is negative min extent match the max of the // bounding box // and the max extent match the min of the bounding box // if spacing is negative, we may have to rethink the equation // between real point and texture coordinate... this->LoadedBounds[0]=origin[0]+ static_cast(this->LoadedExtent[0+swapBounds[0]])*spacing[0]; this->LoadedBounds[2]=origin[1]+ static_cast(this->LoadedExtent[2+swapBounds[1]])*spacing[1]; this->LoadedBounds[4]=origin[2]+ static_cast(this->LoadedExtent[4+swapBounds[2]])*spacing[2]; this->LoadedBounds[1]=origin[0]+ static_cast(this->LoadedExtent[1-swapBounds[0]])*spacing[0]; this->LoadedBounds[3]=origin[1]+ static_cast(this->LoadedExtent[3-swapBounds[1]])*spacing[1]; this->LoadedBounds[5]=origin[2]+ static_cast(this->LoadedExtent[5-swapBounds[2]])*spacing[2]; } else // loaded extents represent cells { int wholeTextureExtent[6]; input->GetExtent(wholeTextureExtent); i=1; while(i<6) { wholeTextureExtent[i]--; i+=2; } i=0; while(i<3) { if(this->LoadedExtent[2*i]==wholeTextureExtent[2*i]) { this->LoadedBounds[2*i+swapBounds[i]]=origin[i]; } else { this->LoadedBounds[2*i+swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i])+0.5)*spacing[i]; } if(this->LoadedExtent[2*i+1]==wholeTextureExtent[2*i+1]) { this->LoadedBounds[2*i+1-swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i+1])+1.0)*spacing[i]; } else { this->LoadedBounds[2*i+1-swapBounds[i]]=origin[i]+ (static_cast(this->LoadedExtent[2*i+1])+0.5)*spacing[i]; } ++i; } } modified=true; } // if enough memory else { } } //load fail with out of memory else { } } // proxy ok else { // proxy failed } } else { // out of therical limitationa } } // if obsolete if(this->Loaded && (needUpdate || modified)) { glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf(vtkgl::TEXTURE_3D,GL_TEXTURE_MAG_FILTER, GL_NEAREST ); modified=true; } if(modified) { this->BuildTime.Modified(); } } double *GetLoadedBounds() { assert("pre: loaded" && this->Loaded); return this->LoadedBounds; } vtkIdType *GetLoadedExtent() { assert("pre: loaded" && this->Loaded); return this->LoadedExtent; } int GetLoadedCellFlag() { assert("pre: loaded" && this->Loaded); return this->LoadedCellFlag; } bool IsLoaded() { return this->Loaded; } protected: GLuint TextureId; vtkTimeStamp BuildTime; double LoadedBounds[6]; vtkIdType LoadedExtent[6]; int LoadedCellFlag; bool Loaded; }; //----------------------------------------------------------------------------- // Display the status of the current framebuffer on the standard output. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CheckFrameBufferStatus() { GLenum status; status = vtkgl::CheckFramebufferStatusEXT(vtkgl::FRAMEBUFFER_EXT); switch(status) { case 0: cout << "call to vtkgl::CheckFramebufferStatusEXT generates an error." << endl; break; case vtkgl::FRAMEBUFFER_COMPLETE_EXT: break; case vtkgl::FRAMEBUFFER_UNSUPPORTED_EXT: cout << "framebuffer is unsupported" << endl; break; case vtkgl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: cout << "framebuffer has an attachment error"<DisplayFrameBufferAttachments(); // this->DisplayReadAndDrawBuffers(); } //----------------------------------------------------------------------------- vtkStdString vtkMitkOpenGLGPUVolumeRayCastMapper::BufferToString(int buffer) { vtkStdString result; vtksys_ios::ostringstream ost; GLint size; GLint b=static_cast(buffer); switch(b) { case GL_NONE: ost << "GL_NONE"; break; case GL_FRONT_LEFT: ost << "GL_FRONT_LEFT"; break; case GL_FRONT_RIGHT: ost << "GL_FRONT_RIGHT"; break; case GL_BACK_LEFT: ost << "GL_BACK_LEFT"; break; case GL_BACK_RIGHT: ost << "GL_BACK_RIGHT"; break; case GL_FRONT: ost << "GL_FRONT"; break; case GL_BACK: ost << "GL_BACK"; break; case GL_LEFT: ost << "GL_LEFT"; break; case GL_RIGHT: ost << "GL_RIGHT"; break; case GL_FRONT_AND_BACK: ost << "GL_FRONT_AND_BACK"; break; default: glGetIntegerv(GL_AUX_BUFFERS,&size); if(buffer>=GL_AUX0 && buffer<(GL_AUX0+size)) { ost << "GL_AUX" << (buffer-GL_AUX0); } else { glGetIntegerv(vtkgl::MAX_COLOR_ATTACHMENTS_EXT,&size); if(static_cast(buffer)>=vtkgl::COLOR_ATTACHMENT0_EXT && static_cast(buffer)< (vtkgl::COLOR_ATTACHMENT0_EXT+static_cast(size))) { ost << "GL_COLOR_ATTACHMENT" << (static_cast(buffer)-vtkgl::COLOR_ATTACHMENT0_EXT) << "_EXT"; } else { ost << "unknown color buffer type=0x"<(value); vtkStdString s; GLenum i=0; while(iBufferToString(static_cast(value)); cout << "draw buffer " << i << "=" << s << endl; ++i; } glGetIntegerv(GL_READ_BUFFER,&value); s=this->BufferToString(static_cast(value)); cout << "read buffer=" << s << endl; } // ---------------------------------------------------------------------------- // Description: // Display all the attachments of the current framebuffer object. // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::DisplayFrameBufferAttachments() { GLint framebufferBinding; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&framebufferBinding); this->PrintError("after getting FRAMEBUFFER_BINDING_EXT"); if(framebufferBinding==0) { cout<<"Current framebuffer is bind to the system one"<(value); this->PrintError("after getting MAX_COLOR_ATTACHMENTS_EXT"); GLenum i=0; while(iDisplayFrameBufferAttachment(vtkgl::COLOR_ATTACHMENT0_EXT+i); ++i; } cout<<"depth attachement :"<DisplayFrameBufferAttachment(vtkgl::DEPTH_ATTACHMENT_EXT); cout<<"stencil attachement :"<DisplayFrameBufferAttachment(vtkgl::STENCIL_ATTACHMENT_EXT); } } // ---------------------------------------------------------------------------- // Description: // Display a given attachment for the current framebuffer object. //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::DisplayFrameBufferAttachment( unsigned int uattachment) { GLenum attachment=static_cast(uattachment); GLint params; vtkgl::GetFramebufferAttachmentParameterivEXT( vtkgl::FRAMEBUFFER_EXT,attachment, vtkgl::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT,¶ms); this->PrintError("after getting FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT"); switch(params) { case GL_NONE: cout<<" this attachment is empty"<PrintError("after getting FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT"); cout<<" this attachment is a texture with name: "<PrintError( "after getting FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT"); cout<<" its mipmap level is: "<PrintError( "after getting FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT"); if(params==0) { cout<<" this is not a cube map texture."<PrintError( "after getting FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT"); if(params==0) { cout<<" this is not 3D texture."<PrintError("after getting FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT"); cout<<" this attachment is a renderbuffer with name: "<(params)); this->PrintError( "after getting binding the current RENDERBUFFER_EXT to params"); vtkgl::GetRenderbufferParameterivEXT(vtkgl::RENDERBUFFER_EXT, vtkgl::RENDERBUFFER_WIDTH_EXT, ¶ms); this->PrintError("after getting RENDERBUFFER_WIDTH_EXT"); cout<<" renderbuffer width="<PrintError("after getting RENDERBUFFER_HEIGHT_EXT"); cout<<" renderbuffer height="<PrintError("after getting RENDERBUFFER_INTERNAL_FORMAT_EXT"); cout<<" renderbuffer internal format=0x"<< hex<PrintError("after getting RENDERBUFFER_RED_SIZE_EXT"); cout<<" renderbuffer actual resolution for the red component="<PrintError("after getting RENDERBUFFER_GREEN_SIZE_EXT"); cout<<" renderbuffer actual resolution for the green component="<PrintError("after getting RENDERBUFFER_BLUE_SIZE_EXT"); cout<<" renderbuffer actual resolution for the blue component="<PrintError("after getting RENDERBUFFER_ALPHA_SIZE_EXT"); cout<<" renderbuffer actual resolution for the alpha component="<PrintError("after getting RENDERBUFFER_DEPTH_SIZE_EXT"); cout<<" renderbuffer actual resolution for the depth component="<PrintError("after getting RENDERBUFFER_STENCIL_SIZE_EXT"); cout<<" renderbuffer actual resolution for the stencil component=" <(errorCode)) { case GL_NO_ERROR: result="No error"; break; case GL_INVALID_ENUM: result="Invalid enum"; break; case GL_INVALID_VALUE: result="Invalid value"; break; case GL_INVALID_OPERATION: result="Invalid operation"; break; case GL_STACK_OVERFLOW: result="stack overflow"; break; case GL_STACK_UNDERFLOW: result="stack underflow"; break; case GL_OUT_OF_MEMORY: result="out of memory"; break; case vtkgl::TABLE_TOO_LARGE: // GL_ARB_imaging result="Table too large"; break; case vtkgl::INVALID_FRAMEBUFFER_OPERATION_EXT: // GL_EXT_framebuffer_object, 310 result="invalid framebuffer operation ext"; break; case vtkgl::TEXTURE_TOO_LARGE_EXT: // GL_EXT_texture result="Texture too large"; break; default: result="unknown error"; } assert("post: result_exists" && result!=0); return result; } //----------------------------------------------------------------------------- // Display headerMessage on the standard output and the last OpenGL error // message if any. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::PrintError(const char *headerMessage) { GLenum errorCode=glGetError(); if(errorCode!=GL_NO_ERROR) { if ( headerMessage ) { cout<(errorCode)); cout<UnsupportedRequiredExtensions=0; this->OpenGLObjectsCreated=0; this->LoadExtensionsSucceeded=0; this->NumberOfFrameBuffers=0; this->m_BindMax = false; // up to 2 frame buffer 2D textures (left/right) // 1 dataset 3D texture // 1 colormap 1D texture // 1 opacitymap 1d texture // 1 grabbed depth buffer 2d texture int i=0; while(iTextureObjects[i]=0; ++i; } this->DepthRenderBufferObject=0; this->FrameBufferObject=0; for ( int j = 0; j < 8; j++ ) { for (i = 0; i < 3; i++ ) { this->BoundingBox[j][i] = 0.0; } } this->LastSize[0]=0; this->LastSize[1]=0; this->ReductionFactor = 1.0; this->Supports_GL_ARB_texture_float=0; this->SupportsPixelBufferObjects=0; i=0; while(i<3) { this->TempMatrix[i]=vtkMatrix4x4::New(); ++i; } this->ErrorLine=0; this->ErrorColumn=0; this->ErrorString=0; this->LastParallelProjection= vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized; this->LastRayCastMethod= vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized; this->LastCroppingMode= vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized; this->LastComponent= vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized; this->LastShade=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized; this->ClippedBoundingBox = NULL; this->SmallInput = NULL; this->MaxValueFrameBuffer=0; this->MaxValueFrameBuffer2=0; this->ReducedSize[0]=0; this->ReducedSize[1]=0; this->NumberOfCroppingRegions=0; this->PolyDataBoundingBox=0; this->Planes=0; this->NearPlane=0; this->Clip=0; this->Densify=0; this->InvVolumeMatrix=vtkMatrix4x4::New(); this->ScaleBiasProgramShader=0; this->UFrameBufferTexture=-1; this->UScale=-1; this->UBias=-1; this->SavedFrameBuffer=0; this->BoxSource=0; this->NoiseTexture=0; this->NoiseTextureSize=0; this->NoiseTextureId=0; this->IgnoreSampleDistancePerPixel=true; this->ScalarsTextures=new vtkMapDataArrayTextureId; this->MaskTextures=new vtkMapMaskTextureId; this->RGBTable=0; this->Mask1RGBTable=0; this->Mask2RGBTable=0; this->OpacityTables=0; this->CurrentScalar=0; this->CurrentMask=0; this->ActualSampleDistance=1.0; this->LastProgressEventTime=0.0; // date in seconds this->PreserveOrientation=true; } //----------------------------------------------------------------------------- // Destruct a vtkMitkOpenGLGPUVolumeRayCastMapper - clean up any memory used //----------------------------------------------------------------------------- vtkMitkOpenGLGPUVolumeRayCastMapper::~vtkMitkOpenGLGPUVolumeRayCastMapper() { if(this->UnsupportedRequiredExtensions!=0) { delete this->UnsupportedRequiredExtensions; this->UnsupportedRequiredExtensions=0; } int i=0; while(i<3) { this->TempMatrix[i]->Delete(); this->TempMatrix[i]=0; ++i; } if(this->ErrorString!=0) { delete[] this->ErrorString; this->ErrorString=0; } if ( this->SmallInput ) { this->SmallInput->UnRegister(this); } if(this->PolyDataBoundingBox!=0) { this->PolyDataBoundingBox->UnRegister(this); this->PolyDataBoundingBox=0; } if(this->Planes!=0) { this->Planes->UnRegister(this); this->Planes=0; } if(this->NearPlane!=0) { this->NearPlane->UnRegister(this); this->NearPlane=0; } if(this->Clip!=0) { this->Clip->UnRegister(this); this->Clip=0; } if(this->Densify!=0) { this->Densify->UnRegister(this); this->Densify=0; } if(this->BoxSource!=0) { this->BoxSource->UnRegister(this); this->BoxSource=0; } this->InvVolumeMatrix->UnRegister(this); this->InvVolumeMatrix=0; if(this->NoiseTexture!=0) { delete[] this->NoiseTexture; this->NoiseTexture=0; this->NoiseTextureSize=0; } if(this->ScalarsTextures!=0) { delete this->ScalarsTextures; this->ScalarsTextures=0; } if(this->MaskTextures!=0) { delete this->MaskTextures; this->MaskTextures=0; } } //----------------------------------------------------------------------------- // Based on hardware and properties, we may or may not be able to // render using 3D texture mapping. This indicates if 3D texture // mapping is supported by the hardware, and if the other extensions // necessary to support the specific properties are available. // //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::IsRenderSupported( vtkRenderWindow *window, vtkVolumeProperty *vtkNotUsed(property)) { window->MakeCurrent(); if(!this->LoadExtensionsSucceeded) { this->LoadExtensions(window); } if(!this->LoadExtensionsSucceeded) { vtkDebugMacro( "The following OpenGL extensions are required but not supported: " << (this->UnsupportedRequiredExtensions->Stream.str()).c_str()); return 0; } return 1; } //----------------------------------------------------------------------------- // Return if the required OpenGL extension `extensionName' is supported. // If not, its name is added to the string of unsupported but required // extensions. // \pre extensions_exist: extensions!=0 // \pre extensionName_exists: extensionName!=0 //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::TestRequiredExtension( vtkOpenGLExtensionManager *extensions, const char *extensionName) { assert("pre: extensions_exist" && extensions!=0); assert("pre: extensionName_exists" && extensionName!=0); int result=extensions->ExtensionSupported(extensionName); if(!result) { if(this->LoadExtensionsSucceeded) { this->UnsupportedRequiredExtensions->Stream<LoadExtensionsSucceeded=0; } else { this->UnsupportedRequiredExtensions->Stream<<", "<LoadExtensionsSucceeded will be set to 0 or 1 // - this->UnsupportedRequiredExtensions will have a message indicating // any failure codes //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::LoadExtensions( vtkRenderWindow *window) { // We may already have a string stream for the unsupported extensions // from the last time this method was called. If so, delete it. if(this->UnsupportedRequiredExtensions!=0) { delete this->UnsupportedRequiredExtensions; } // Create a string stream to hold the unsupported extensions so we can // report something meaningful back this->UnsupportedRequiredExtensions = new vtkUnsupportedRequiredExtensionsStringStream; // It does not work on Apple OS X Snow Leopard with nVidia. // There is a bug in the OpenGL driver with an error in the // Cg compiler about an infinite loop. #ifdef __APPLE__ this->LoadExtensionsSucceeded=0; return; #endif // Assume success this->LoadExtensionsSucceeded=1; const char *gl_vendor=reinterpret_cast(glGetString(GL_VENDOR)); /* if(strstr(gl_vendor,"ATI")!=0) { this->LoadExtensionsSucceeded=0; return; }*/ const char *gl_version=reinterpret_cast(glGetString(GL_VERSION)); if(strstr(gl_version,"Mesa")!=0) { // - GL_VENDOR cannot be used because it can be "Brian Paul" or // "Mesa project" // - GL_RENDERER cannot be used because it can be "Software Rasterizer" or // "Mesa X11" // - GL_VERSION is more robust. It has things like "2.0 Mesa 7.0.4" or // "2.1 Mesa 7.2" or "2.1 Mesa 7.3-devel" // Mesa does not work with multiple draw buffers: // "framebuffer has bad draw buffer" // "render clipped 1 ERROR (x506) invalid framebuffer operation ext" this->LoadExtensionsSucceeded=0; return; } // Create an extension manager vtkOpenGLExtensionManager *extensions=vtkOpenGLExtensionManager::New(); extensions->SetRenderWindow(window); // GL_ARB_draw_buffers requires OpenGL 1.3, so we must have OpenGL 1.3 // We don't need to check for some extensions that become part of OpenGL // core after 1.3. Among them: // - texture_3d is in core OpenGL since 1.2 // - texture_edge_clamp is in core OpenGL since 1.2 // (GL_SGIS_texture_edge_clamp or GL_EXT_texture_edge_clamp (nVidia) ) // - multitexture is in core OpenGL since 1.3 int supports_GL_1_3=extensions->ExtensionSupported("GL_VERSION_1_3"); int supports_GL_2_0=0; // No 1.3 support - give up if(!supports_GL_1_3) { this->LoadExtensionsSucceeded=0; this->UnsupportedRequiredExtensions->Stream<< " OpenGL 1.3 is required but not supported"; extensions->Delete(); return; } // Check for 2.0 support supports_GL_2_0=extensions->ExtensionSupported("GL_VERSION_2_0"); // Some extensions that are supported in 2.0, but if we don't // have 2.0 we'll need to check further int supports_shading_language_100 = 1; int supports_shader_objects = 1; int supports_fragment_shader = 1; int supports_texture_non_power_of_two = 1; int supports_draw_buffers = 1; if(!supports_GL_2_0) { supports_shading_language_100= extensions->ExtensionSupported("GL_ARB_shading_language_100"); supports_shader_objects= extensions->ExtensionSupported("GL_ARB_shader_objects"); supports_fragment_shader= extensions->ExtensionSupported("GL_ARB_fragment_shader"); supports_texture_non_power_of_two= extensions->ExtensionSupported("GL_ARB_texture_non_power_of_two"); supports_draw_buffers= extensions->ExtensionSupported("GL_ARB_draw_buffers"); } // We have to check for framebuffer objects int supports_GL_EXT_framebuffer_object= extensions->ExtensionSupported("GL_EXT_framebuffer_object" ); // Find out if we have OpenGL 1.4 support int supports_GL_1_4=extensions->ExtensionSupported("GL_VERSION_1_4"); // Find out if we have the depth texture ARB extension int supports_GL_ARB_depth_texture= extensions->ExtensionSupported("GL_ARB_depth_texture"); // Depth textures are support if we either have OpenGL 1.4 // or if the depth texture ARB extension is supported int supports_depth_texture = supports_GL_1_4 || supports_GL_ARB_depth_texture; // Now start adding messages to the UnsupportedRequiredExtensions string // Log message if shading language 100 is not supported if(!supports_shading_language_100) { this->UnsupportedRequiredExtensions->Stream<< " shading_language_100 (or OpenGL 2.0) is required but not supported"; this->LoadExtensionsSucceeded=0; } else { // We can query the GLSL version, we need >=1.20 const char *glsl_version= reinterpret_cast(glGetString(vtkgl::SHADING_LANGUAGE_VERSION)); int glslMajor, glslMinor; vtksys_ios::istringstream ist(glsl_version); ist >> glslMajor; char c; ist.get(c); // '.' ist >> glslMinor; //sscanf(version, "%d.%d", &glslMajor, &glslMinor); if(glslMajor<1 || (glslMajor==1 && glslMinor<20)) { this->LoadExtensionsSucceeded=0; } } // Log message if shader objects are not supported if(!supports_shader_objects) { this->UnsupportedRequiredExtensions->Stream<< " shader_objects (or OpenGL 2.0) is required but not supported"; this->LoadExtensionsSucceeded=0; } // Log message if fragment shaders are not supported if(!supports_fragment_shader) { this->UnsupportedRequiredExtensions->Stream<< " fragment_shader (or OpenGL 2.0) is required but not supported"; this->LoadExtensionsSucceeded=0; } // Log message if non power of two textures are not supported if(!supports_texture_non_power_of_two) { this->UnsupportedRequiredExtensions->Stream<< " texture_non_power_of_two (or OpenGL 2.0) is required but not " << "supported"; this->LoadExtensionsSucceeded=0; } // Log message if draw buffers are not supported if(!supports_draw_buffers) { this->UnsupportedRequiredExtensions->Stream<< " draw_buffers (or OpenGL 2.0) is required but not supported"; this->LoadExtensionsSucceeded=0; } // Log message if depth textures are not supported if(!supports_depth_texture) { this->UnsupportedRequiredExtensions->Stream<< " depth_texture (or OpenGL 1.4) is required but not supported"; this->LoadExtensionsSucceeded=0; } // Log message if framebuffer objects are not supported if(!supports_GL_EXT_framebuffer_object) { this->UnsupportedRequiredExtensions->Stream<< " framebuffer_object is required but not supported"; this->LoadExtensionsSucceeded=0; } // Have we succeeded so far? If not, just return. if(!this->LoadExtensionsSucceeded) { extensions->Delete(); return; } // Now start loading the extensions // First load all 1.2 and 1.3 extensions (we know we // support at least up to 1.3) extensions->LoadExtension("GL_VERSION_1_2"); extensions->LoadExtension("GL_VERSION_1_3"); // Load the 2.0 extensions if supported if(supports_GL_2_0) { extensions->LoadExtension("GL_VERSION_2_0"); } // Otherwise, we'll need to specifically load the // shader objects, fragment shader, and draw buffers // extensions else { extensions->LoadCorePromotedExtension("GL_ARB_shader_objects"); extensions->LoadCorePromotedExtension("GL_ARB_fragment_shader"); extensions->LoadCorePromotedExtension("GL_ARB_draw_buffers"); } // Load the framebuffer object extension extensions->LoadExtension("GL_EXT_framebuffer_object"); // Optional extension (does not fail if not present) // Load it if supported which will allow us to store // textures as floats this->Supports_GL_ARB_texture_float= extensions->ExtensionSupported("GL_ARB_texture_float" ); if(this->Supports_GL_ARB_texture_float) { extensions->LoadExtension( "GL_ARB_texture_float" ); } // Optional extension (does not fail if not present) // Used to minimize memory footprint when loading large 3D textures // of scalars. // VBO or 1.5 is required by PBO or 2.1 int supports_GL_1_5=extensions->ExtensionSupported("GL_VERSION_1_5"); int supports_vertex_buffer_object=supports_GL_1_5 || extensions->ExtensionSupported("GL_ARB_vertex_buffer_object"); int supports_GL_2_1=extensions->ExtensionSupported("GL_VERSION_2_1"); this->SupportsPixelBufferObjects=supports_vertex_buffer_object && (supports_GL_2_1 || extensions->ExtensionSupported("GL_ARB_pixel_buffer_object")); if(this->SupportsPixelBufferObjects) { if(supports_GL_1_5) { extensions->LoadExtension("GL_VERSION_1_5"); } else { extensions->LoadCorePromotedExtension("GL_ARB_vertex_buffer_object"); } if(supports_GL_2_1) { extensions->LoadExtension("GL_VERSION_2_1"); } else { extensions->LoadCorePromotedExtension("GL_ARB_pixel_buffer_object"); } } // Ultimate test. Some old cards support OpenGL 2.0 but not while // statements in a fragment shader (example: nVidia GeForce FX 5200) // It does not fail when compiling each shader source but at linking // stage because the parser underneath only check for syntax during // compilation and the actual native code generation happens during // the linking stage. this->CreateGLSLObjects(); this->NumberOfCroppingRegions=1; this->BuildProgram(1,vtkMitkOpenGLGPUVolumeRayCastMapperMethodComposite, vtkMitkOpenGLGPUVolumeRayCastMapperShadeNo, vtkMitkOpenGLGPUVolumeRayCastMapperComponentOne); GLint params; vtkgl::GetProgramiv(static_cast(this->ProgramShader), vtkgl::LINK_STATUS,¶ms); if(params==GL_FALSE) { this->LoadExtensionsSucceeded=0; this->UnsupportedRequiredExtensions->Stream<< " this card does not support while statements in fragment shaders."; } // FB debug this->CheckLinkage(this->ProgramShader); // Release GLSL Objects. GLuint programShader=static_cast(this->ProgramShader); vtkgl::DeleteProgram(programShader); this->LastParallelProjection= vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized; this->LastRayCastMethod= vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized; this->LastCroppingMode= vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized; this->LastComponent= vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized; this->LastShade=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized; extensions->Delete(); } //----------------------------------------------------------------------------- // Create GLSL OpenGL objects such fragment program Ids. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CreateGLSLObjects() { GLuint programShader; GLuint fragmentMainShader; programShader=vtkgl::CreateProgram(); fragmentMainShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentMainShader); vtkgl::DeleteShader(fragmentMainShader); // reference counting vtkgl::ShaderSource( fragmentMainShader,1, const_cast(&vtkMitkGPUVolumeRayCastMapper_HeaderFS),0); vtkgl::CompileShader(fragmentMainShader); this->CheckCompilation(static_cast(fragmentMainShader)); GLuint fragmentProjectionShader; GLuint fragmentTraceShader; GLuint fragmentCroppingShader; GLuint fragmentComponentShader; GLuint fragmentShadeShader; fragmentProjectionShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentProjectionShader); vtkgl::DeleteShader(fragmentProjectionShader); // reference counting fragmentTraceShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentTraceShader); vtkgl::DeleteShader(fragmentTraceShader); // reference counting fragmentCroppingShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentCroppingShader); vtkgl::DeleteShader(fragmentCroppingShader); // reference counting fragmentComponentShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); // don't delete it, it is optionally attached. fragmentShadeShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); // Save GL objects by static casting to standard C types. GL* types // are not allowed in VTK header files. this->ProgramShader=static_cast(programShader); this->FragmentMainShader=static_cast(fragmentMainShader); this->FragmentProjectionShader= static_cast(fragmentProjectionShader); this->FragmentTraceShader=static_cast(fragmentTraceShader); this->FragmentCroppingShader= static_cast(fragmentCroppingShader); this->FragmentComponentShader= static_cast(fragmentComponentShader); this->FragmentShadeShader= static_cast(fragmentShadeShader); } void vtkMitkOpenGLGPUVolumeRayCastMapper::BindFramebuffer() { vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT,GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront], 0); GLenum err = glGetError(); if(m_BindMax) { vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D,this->MaxValueFrameBuffer,0); } } //----------------------------------------------------------------------------- // Create OpenGL objects such as textures, buffers and fragment program Ids. // It only registers Ids, there is no actual initialization of textures or // fragment program. // // Pre-conditions: // This method assumes that this->LoadedExtensionsSucceeded is 1. // // Post-conditions: // When this method completes successfully, this->OpenGLObjectsCreated // will be 1. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CreateOpenGLObjects() { // Do nothing if the OpenGL objects have already been created if ( this->OpenGLObjectsCreated ) { return; } // We need only two color buffers (ping-pong) this->NumberOfFrameBuffers=2; // TODO: clean this up! // 2*Frame buffers(2d textures)+colorMap (1d texture) +dataset (3d texture) // + opacitymap (1d texture) + grabbed depthMap (2d texture) // Frame buffers(2d textures)+colorMap (1d texture) +dataset (3d texture) // + opacity (1d texture)+grabbed depth buffer (2d texture) GLuint frameBufferObject; GLuint depthRenderBufferObject; GLuint textureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects]; // Create the various objects we will need - one frame buffer // which will contain a render buffer for depth and a texture // for color. vtkgl::GenFramebuffersEXT(1, &frameBufferObject); // color vtkgl::GenRenderbuffersEXT(1, &depthRenderBufferObject); // depth glGenTextures(vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects,textureObjects); // Color buffers GLint value; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&value); GLuint savedFrameBuffer=static_cast(value); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT,frameBufferObject); // Depth buffer vtkgl::BindRenderbufferEXT(vtkgl::RENDERBUFFER_EXT, depthRenderBufferObject); vtkgl::FramebufferRenderbufferEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::DEPTH_ATTACHMENT_EXT, vtkgl::RENDERBUFFER_EXT, depthRenderBufferObject); // Restore default frame buffer. vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT,savedFrameBuffer); this->CreateGLSLObjects(); // Save GL objects by static casting to standard C types. GL* types // are not allowed in VTK header files. this->FrameBufferObject=static_cast(frameBufferObject); this->DepthRenderBufferObject=static_cast(depthRenderBufferObject); int i=0; while(iTextureObjects[i]=static_cast(textureObjects[i]); ++i; } this->OpenGLObjectsCreated=1; } //----------------------------------------------------------------------------- // Check the compilation status of some fragment shader source. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CheckCompilation( unsigned int fragmentShader) { GLuint fs=static_cast(fragmentShader); GLint params; vtkgl::GetShaderiv(fs,vtkgl::COMPILE_STATUS,¶ms); if(params==GL_TRUE) { vtkDebugMacro(<<"shader source compiled successfully"); } else { vtkErrorMacro(<<"shader source compile error"); // include null terminator vtkgl::GetShaderiv(fs,vtkgl::INFO_LOG_LENGTH,¶ms); if(params>0) { char *buffer=new char[params]; vtkgl::GetShaderInfoLog(fs,params,0,buffer); vtkErrorMacro(<<"log: "<(programShader); // info about the list of active uniform variables vtkgl::GetProgramiv(prog,vtkgl::ACTIVE_UNIFORMS,¶ms); cout<<"There are "<(params); vtkgl::GetProgramiv(prog,vtkgl::OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB, ¶ms); GLint buffSize=params; char *name=new char[buffSize+1]; GLint size; GLenum type; while(i(programShader); vtkgl::GetProgramiv(prog,vtkgl::LINK_STATUS,¶ms); int status = 0; if(params==GL_TRUE) { status = 1; vtkDebugMacro(<<"program linked successfully"); } else { vtkErrorMacro(<<"program link error"); vtkgl::GetProgramiv(prog,vtkgl::INFO_LOG_LENGTH,¶ms); if(params>0) { char *buffer=new char[params]; vtkgl::GetProgramInfoLog(prog,params,0,buffer); vtkErrorMacro(<<"log: "<OpenGLObjectsCreated==0 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::ReleaseGraphicsResources( vtkWindow *window) { if(this->OpenGLObjectsCreated) { window->MakeCurrent(); this->LastSize[0]=0; this->LastSize[1]=0; GLuint frameBufferObject=static_cast(this->FrameBufferObject); vtkgl::DeleteFramebuffersEXT(1,&frameBufferObject); GLuint depthRenderBufferObject= static_cast(this->DepthRenderBufferObject); vtkgl::DeleteRenderbuffersEXT(1,&depthRenderBufferObject); GLuint textureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects]; int i=0; while(i<(vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+this->NumberOfFrameBuffers)) { textureObjects[i]=static_cast(this->TextureObjects[i]); ++i; } glDeleteTextures(vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+this->NumberOfFrameBuffers,textureObjects); if(this->MaxValueFrameBuffer!=0) { GLuint maxValueFrameBuffer= static_cast(this->MaxValueFrameBuffer); glDeleteTextures(1,&maxValueFrameBuffer); this->MaxValueFrameBuffer=0; } if(this->MaxValueFrameBuffer2!=0) { GLuint maxValueFrameBuffer2= static_cast(this->MaxValueFrameBuffer2); glDeleteTextures(1,&maxValueFrameBuffer2); this->MaxValueFrameBuffer2=0; } GLuint programShader=static_cast(this->ProgramShader); vtkgl::DeleteProgram(programShader); this->ProgramShader=0; GLuint fragmentComponentShader= static_cast(this->FragmentComponentShader); vtkgl::DeleteShader(fragmentComponentShader); GLuint fragmentShadeShader= static_cast(this->FragmentShadeShader); vtkgl::DeleteShader(fragmentShadeShader); GLuint scaleBiasProgramShader= static_cast(this->ScaleBiasProgramShader); if(scaleBiasProgramShader!=0) { vtkgl::DeleteProgram(scaleBiasProgramShader); this->ScaleBiasProgramShader=0; } this->LastParallelProjection= vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized; this->LastRayCastMethod= vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized; this->LastCroppingMode= vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized; this->LastComponent= vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized; this->LastShade=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized; this->OpenGLObjectsCreated=0; } if(this->NoiseTextureId!=0) { window->MakeCurrent(); GLuint noiseTextureObjects=static_cast(this->NoiseTextureId); glDeleteTextures(1,&noiseTextureObjects); this->NoiseTextureId=0; } if(this->ScalarsTextures!=0) { if(!this->ScalarsTextures->Map.empty()) { vtkstd::map::iterator it=this->ScalarsTextures->Map.begin(); while(it!=this->ScalarsTextures->Map.end()) { vtkKWScalarField *texture=(*it).second; delete texture; ++it; } this->ScalarsTextures->Map.clear(); } } if(this->MaskTextures!=0) { if(!this->MaskTextures->Map.empty()) { vtkstd::map::iterator it=this->MaskTextures->Map.begin(); while(it!=this->MaskTextures->Map.end()) { vtkKWMask *texture=(*it).second; delete texture; ++it; } this->MaskTextures->Map.clear(); } } if(this->RGBTable!=0) { delete this->RGBTable; this->RGBTable=0; } if(this->Mask1RGBTable!=0) { delete this->Mask1RGBTable; this->Mask1RGBTable=0; } if(this->Mask2RGBTable!=0) { delete this->Mask2RGBTable; this->Mask2RGBTable=0; } if(this->OpacityTables!=0) { delete this->OpacityTables; this->OpacityTables=0; } } + + +//----------------------------------------------------------------------------- +// Delete OpenGL objects. +// \post done: this->OpenGLObjectsCreated==0 +//----------------------------------------------------------------------------- +void vtkMitkOpenGLGPUVolumeRayCastMapper::ReleaseGraphicsResources( + mitk::BaseRenderer * renderer) +{ + if(this->OpenGLObjectsCreated) + { + vtkWindow * window = renderer->GetVtkRenderer()->GetRenderWindow(); + window->MakeCurrent(); + this->LastSize[0]=0; + this->LastSize[1]=0; + GLuint frameBufferObject=static_cast(this->FrameBufferObject); + vtkgl::DeleteFramebuffersEXT(1,&frameBufferObject); + GLuint depthRenderBufferObject= + static_cast(this->DepthRenderBufferObject); + vtkgl::DeleteRenderbuffersEXT(1,&depthRenderBufferObject); + GLuint textureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperNumberOfTextureObjects]; + int i=0; + while(i<(vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+this->NumberOfFrameBuffers)) + { + textureObjects[i]=static_cast(this->TextureObjects[i]); + ++i; + } + glDeleteTextures(vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+this->NumberOfFrameBuffers,textureObjects); + + if(this->MaxValueFrameBuffer!=0) + { + GLuint maxValueFrameBuffer= + static_cast(this->MaxValueFrameBuffer); + glDeleteTextures(1,&maxValueFrameBuffer); + this->MaxValueFrameBuffer=0; + } + if(this->MaxValueFrameBuffer2!=0) + { + GLuint maxValueFrameBuffer2= + static_cast(this->MaxValueFrameBuffer2); + glDeleteTextures(1,&maxValueFrameBuffer2); + this->MaxValueFrameBuffer2=0; + } + + GLuint programShader=static_cast(this->ProgramShader); + vtkgl::DeleteProgram(programShader); + this->ProgramShader=0; + GLuint fragmentComponentShader= + static_cast(this->FragmentComponentShader); + vtkgl::DeleteShader(fragmentComponentShader); + GLuint fragmentShadeShader= + static_cast(this->FragmentShadeShader); + vtkgl::DeleteShader(fragmentShadeShader); + + GLuint scaleBiasProgramShader= + static_cast(this->ScaleBiasProgramShader); + if(scaleBiasProgramShader!=0) + { + vtkgl::DeleteProgram(scaleBiasProgramShader); + this->ScaleBiasProgramShader=0; + } + this->LastParallelProjection= + vtkMitkOpenGLGPUVolumeRayCastMapperProjectionNotInitialized; + this->LastRayCastMethod= + vtkMitkOpenGLGPUVolumeRayCastMapperMethodNotInitialized; + this->LastCroppingMode= + vtkMitkOpenGLGPUVolumeRayCastMapperCroppingNotInitialized; + this->LastComponent= + vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotInitialized; + this->LastShade=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotInitialized; + this->OpenGLObjectsCreated=0; + } + + if(this->NoiseTextureId!=0) + { + window->MakeCurrent(); + GLuint noiseTextureObjects=static_cast(this->NoiseTextureId); + glDeleteTextures(1,&noiseTextureObjects); + this->NoiseTextureId=0; + } + + if(this->ScalarsTextures!=0) + { + if(!this->ScalarsTextures->Map.empty()) + { + vtkstd::map::iterator it=this->ScalarsTextures->Map.begin(); + while(it!=this->ScalarsTextures->Map.end()) + { + vtkKWScalarField *texture=(*it).second; + delete texture; + ++it; + } + this->ScalarsTextures->Map.clear(); + } + } + + if(this->MaskTextures!=0) + { + if(!this->MaskTextures->Map.empty()) + { + vtkstd::map::iterator it=this->MaskTextures->Map.begin(); + while(it!=this->MaskTextures->Map.end()) + { + vtkKWMask *texture=(*it).second; + delete texture; + ++it; + } + this->MaskTextures->Map.clear(); + } + } + + if(this->RGBTable!=0) + { + delete this->RGBTable; + this->RGBTable=0; + } + + if(this->Mask1RGBTable!=0) + { + delete this->Mask1RGBTable; + this->Mask1RGBTable=0; + } + + if(this->Mask2RGBTable!=0) + { + delete this->Mask2RGBTable; + this->Mask2RGBTable=0; + } + + if(this->OpacityTables!=0) + { + delete this->OpacityTables; + this->OpacityTables=0; + } +} + + //----------------------------------------------------------------------------- // Allocate memory on the GPU for the framebuffers according to the size of // the window or reallocate if the size has changed. Return true if // allocation succeeded. // \pre ren_exists: ren!=0 // \pre opengl_objects_created: this->OpenGLObjectsCreated // \post right_size: LastSize[]=window size. //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::AllocateFrameBuffers(vtkRenderer *ren) { assert("pre: ren_exists" && ren!=0); assert("pre: opengl_objects_created" && this->OpenGLObjectsCreated); int result=1; int size[2]; ren->GetTiledSize(&size[0],&size[1]); int sizeChanged=this->LastSize[0]!=size[0] || this->LastSize[1]!=size[1]; GLenum errorCode=glGetError(); // Need allocation? if(sizeChanged) { int i=0; GLenum errorCode=glGetError(); while(i NumberOfFrameBuffers && errorCode==GL_NO_ERROR) { glBindTexture(GL_TEXTURE_2D,static_cast(this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+i])); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Here we are assuming that GL_ARB_texture_non_power_of_two is available if(this->Supports_GL_ARB_texture_float) { glTexImage2D(GL_TEXTURE_2D,0,vtkgl::RGBA16F_ARB,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } else { glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } errorCode=glGetError(); ++i; } if(errorCode==GL_NO_ERROR) { // grabbed depth buffer glBindTexture(GL_TEXTURE_2D,static_cast(this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectDepthMap])); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri(GL_TEXTURE_2D, vtkgl::DEPTH_TEXTURE_MODE, GL_LUMINANCE); glTexImage2D(GL_TEXTURE_2D, 0, vtkgl::DEPTH_COMPONENT32, size[0],size[1], 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL ); // Set up the depth render buffer GLint savedFrameBuffer; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&savedFrameBuffer); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(this->FrameBufferObject)); this->BindFramebuffer(); vtkgl::BindRenderbufferEXT( vtkgl::RENDERBUFFER_EXT, static_cast(this->DepthRenderBufferObject)); vtkgl::RenderbufferStorageEXT(vtkgl::RENDERBUFFER_EXT, vtkgl::DEPTH_COMPONENT24,size[0],size[1]); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(savedFrameBuffer)); errorCode=glGetError(); if(errorCode==GL_NO_ERROR) { this->LastSize[0]=size[0]; this->LastSize[1]=size[1]; } } result=errorCode==GL_NO_ERROR; } int needNewMaxValueBuffer=this->MaxValueFrameBuffer==0 && (this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND); if(needNewMaxValueBuffer) { // blend mode changed and need max value buffer. // create and bind second color buffer (we use only the red component // to store the max scalar). We cant use a one component color buffer // because all color buffer have to have the same format. // max scalar frame buffer GLuint maxValueFrameBuffer; glGenTextures(1,&maxValueFrameBuffer); this->MaxValueFrameBuffer= static_cast(maxValueFrameBuffer); // Color buffers this->m_BindMax = true; // max scalar frame buffer2 GLuint maxValueFrameBuffer2; glGenTextures(1,&maxValueFrameBuffer2); glBindTexture(GL_TEXTURE_2D,maxValueFrameBuffer2); this->MaxValueFrameBuffer2= static_cast(maxValueFrameBuffer2); } else { if(this->MaxValueFrameBuffer!=0 && (this->BlendMode!=vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND && this->BlendMode!=vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND)) { // blend mode changed and does not need max value buffer anymore. GLint savedFrameBuffer; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&savedFrameBuffer); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(this->FrameBufferObject)); vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D,0,0); // not scalar buffer vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(savedFrameBuffer)); GLuint maxValueFrameBuffer= static_cast(this->MaxValueFrameBuffer); glDeleteTextures(1,&maxValueFrameBuffer); this->MaxValueFrameBuffer=0; m_BindMax = false; GLuint maxValueFrameBuffer2= static_cast(this->MaxValueFrameBuffer2); glDeleteTextures(1,&maxValueFrameBuffer2); this->MaxValueFrameBuffer2=0; } } if((this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) && (sizeChanged || needNewMaxValueBuffer)) { // max scalar frame buffer GLuint maxValueFrameBuffer=static_cast(this->MaxValueFrameBuffer); glBindTexture(GL_TEXTURE_2D,maxValueFrameBuffer); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Here we are assuming that GL_ARB_texture_non_power_of_two is available if(this->Supports_GL_ARB_texture_float) { glTexImage2D(GL_TEXTURE_2D,0,vtkgl::RGBA16F_ARB,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } else { glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } // max scalar frame buffer 2 GLuint maxValueFrameBuffer2=static_cast(this->MaxValueFrameBuffer2); glBindTexture(GL_TEXTURE_2D,maxValueFrameBuffer2); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Here we are assuming that GL_ARB_texture_non_power_of_two is available if(this->Supports_GL_ARB_texture_float) { glTexImage2D(GL_TEXTURE_2D,0,vtkgl::RGBA16F_ARB,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } else { glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16,size[0],size[1], 0, GL_RGBA, GL_FLOAT, NULL ); } } PrintError("AllocateFrameBuffers"); return result; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::GetTextureFormat( vtkImageData *input, unsigned int *internalFormat, unsigned int *format, unsigned int *type, int *componentSize) { *internalFormat=0; *format=0; *type=0; *componentSize=0; vtkDataArray *scalars=this->GetScalars(input,this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag); int scalarType=scalars->GetDataType(); int components=scalars->GetNumberOfComponents(); *componentSize=vtkAbstractArray::GetDataTypeSize(scalarType)*components; if(components==4) { // this is RGBA, unsigned char only *internalFormat=GL_RGBA16; *format=GL_RGBA; *type=GL_UNSIGNED_BYTE; } else { // components==1 switch(scalarType) { case VTK_FLOAT: if(this->Supports_GL_ARB_texture_float) { *internalFormat=vtkgl::INTENSITY16F_ARB; } else { *internalFormat=GL_INTENSITY16; } *format=GL_RED; *type=GL_FLOAT; break; case VTK_UNSIGNED_CHAR: *internalFormat=GL_INTENSITY8; *format=GL_RED; *type=GL_UNSIGNED_BYTE; break; case VTK_SIGNED_CHAR: *internalFormat=GL_INTENSITY8; *format=GL_RED; *type=GL_BYTE; break; case VTK_CHAR: // not supported assert("check: impossible case" && 0); break; case VTK_BIT: // not supported assert("check: impossible case" && 0); break; case VTK_ID_TYPE: // not supported assert("check: impossible case" && 0); break; case VTK_INT: *internalFormat=GL_INTENSITY16; *format=GL_RED; *type=GL_INT; break; case VTK_DOUBLE: case VTK___INT64: case VTK_LONG: case VTK_LONG_LONG: case VTK_UNSIGNED___INT64: case VTK_UNSIGNED_LONG: case VTK_UNSIGNED_LONG_LONG: if(this->Supports_GL_ARB_texture_float) { *internalFormat=vtkgl::INTENSITY16F_ARB; } else { *internalFormat=GL_INTENSITY16; } *format=GL_RED; *type=GL_FLOAT; break; case VTK_SHORT: *internalFormat=GL_INTENSITY16; *format=GL_RED; *type=GL_SHORT; break; case VTK_STRING: // not supported assert("check: impossible case" && 0); break; case VTK_UNSIGNED_SHORT: *internalFormat=GL_INTENSITY16; *format=GL_RED; *type=GL_UNSIGNED_SHORT; break; case VTK_UNSIGNED_INT: *internalFormat=GL_INTENSITY16; *format=GL_RED; *type=GL_UNSIGNED_INT; break; default: assert("check: impossible case" && 0); break; } } } //----------------------------------------------------------------------------- // Assuming the textureSize[3] is less of equal to the maximum size of an // OpenGL 3D texture, try to see if the texture can fit on the card. //----------------------------------------------------------------------------- bool vtkMitkOpenGLGPUVolumeRayCastMapper::TestLoadingScalar( unsigned int internalFormat, unsigned int format, unsigned int type, int textureSize[3], int componentSize) { // componentSize=vtkAbstractArray::GetDataTypeSize(scalarType)*input->GetNumberOfScalarComponents() bool result; vtkgl::TexImage3D(vtkgl::PROXY_TEXTURE_3D,0, static_cast(internalFormat), textureSize[0],textureSize[1],textureSize[2],0, format, type,0); GLint width; glGetTexLevelParameteriv(vtkgl::PROXY_TEXTURE_3D,0,GL_TEXTURE_WIDTH, &width); result=width!=0; if(result) { // so far, so good but some cards always succeed with a proxy texture // let's try to actually allocate.. vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,static_cast(internalFormat), textureSize[0], textureSize[1],textureSize[2],0, format, type,0); GLenum errorCode=glGetError(); result=errorCode!=GL_OUT_OF_MEMORY; if(result) { if(errorCode!=GL_NO_ERROR) { cout<<"after try to load the texture"; cout<<" ERROR (x"<(errorCode)); cout<(this->MaxMemoryInBytes)*this->MaxMemoryFraction; } } return result; } //----------------------------------------------------------------------------- // Load the scalar field (one or four component scalar field), cell or point // based for a given subextent of the whole extent (can be the whole extent) // as a 3D texture on the GPU. // Extents are expressed in point if the cell flag is false or in cells of // the cell flag is true. // It returns true if it succeeded, false if there is not enough memory on // the GPU. // If succeeded, it updates the LoadedExtent, LoadedBounds, LoadedCellFlag // and LoadedTime. It also succeed if the scalar field is already loaded // (ie since last load, input has not changed and cell flag has not changed // and requested texture extents are enclosed in the loaded extent). // \pre input_exists: input!=0 // \pre valid_point_extent: (this->CellFlag || // (textureExtent[0]CellFlag || // (textureExtent[0]<=textureExtent[1] && // textureExtent[2]<=textureExtent[3] && // textureExtent[4]<=textureExtent[5]))) //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::LoadScalarField(vtkImageData *input, vtkImageData *maskInput, int textureExtent[6], vtkVolume *volume) { assert("pre: input_exists" && input!=0); assert("pre: valid_point_extent" && (this->CellFlag || (textureExtent[0]CellFlag || (textureExtent[0]<=textureExtent[1] && textureExtent[2]<=textureExtent[3] && textureExtent[4]<=textureExtent[5]))); int result=1; // succeeded // make sure we rebind our texture object to texture0 even if we don't have // to load the data themselves because the binding might be changed by // another mapper between two rendering calls. vtkgl::ActiveTexture(vtkgl::TEXTURE0); // Find the texture. vtkstd::map::iterator it= this->ScalarsTextures->Map.find(input); vtkKWScalarField *texture; if(it==this->ScalarsTextures->Map.end()) { texture=new vtkKWScalarField; this->ScalarsTextures->Map[input]=texture; texture->SetSupports_GL_ARB_texture_float(this->Supports_GL_ARB_texture_float==1); } else { texture=(*it).second; } texture->Update(input,this->CellFlag,textureExtent,this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, volume->GetProperty()->GetInterpolationType() ==VTK_LINEAR_INTERPOLATION, this->TableRange, static_cast(static_cast(this->MaxMemoryInBytes)*this->MaxMemoryFraction)); result=texture->IsLoaded(); this->CurrentScalar=texture; // Mask if(maskInput!=0) { vtkgl::ActiveTexture(vtkgl::TEXTURE7); // Find the texture. vtkstd::map::iterator it2= this->MaskTextures->Map.find(maskInput); vtkKWMask *mask; if(it2==this->MaskTextures->Map.end()) { mask=new vtkKWMask; this->MaskTextures->Map[maskInput]=mask; } else { mask=(*it2).second; } mask->Update(maskInput,this->CellFlag,textureExtent,this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, static_cast(static_cast(this->MaxMemoryInBytes)*this->MaxMemoryFraction)); result=result && mask->IsLoaded(); this->CurrentMask=mask; vtkgl::ActiveTexture(vtkgl::TEXTURE0); } return result; } //----------------------------------------------------------------------------- // Allocate memory and load color table on the GPU or // reload it if the transfer function changed. // \pre vol_exists: vol!=0 // \pre valid_numberOfScalarComponents: numberOfScalarComponents==1 || numberOfScalarComponents==4 //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::UpdateColorTransferFunction( vtkVolume *vol, int numberOfScalarComponents) { assert("pre: vol_exists" && vol!=0); assert("pre: valid_numberOfScalarComponents" && (numberOfScalarComponents==1 || numberOfScalarComponents==4)); // Build the colormap in a 1D texture. // 1D RGB-texture=mapping from scalar values to color values // build the table if(numberOfScalarComponents==1) { vtkVolumeProperty *volumeProperty=vol->GetProperty(); vtkColorTransferFunction *colorTransferFunction=volumeProperty->GetRGBTransferFunction(0); vtkgl::ActiveTexture(vtkgl::TEXTURE1); this->RGBTable->Update( colorTransferFunction,this->TableRange, volumeProperty->GetInterpolationType()==VTK_LINEAR_INTERPOLATION); // Restore default vtkgl::ActiveTexture( vtkgl::TEXTURE0); } if(this->MaskInput!=0) { vtkVolumeProperty *volumeProperty=vol->GetProperty(); vtkColorTransferFunction *c=volumeProperty->GetRGBTransferFunction(1); vtkgl::ActiveTexture(vtkgl::TEXTURE8); this->Mask1RGBTable->Update(c,this->TableRange,false); c=volumeProperty->GetRGBTransferFunction(2); vtkgl::ActiveTexture(vtkgl::TEXTURE9); this->Mask2RGBTable->Update(c,this->TableRange,false); // Restore default vtkgl::ActiveTexture( vtkgl::TEXTURE0); } return 1; } //----------------------------------------------------------------------------- // Allocate memory and load opacity table on the GPU or // reload it if the transfert function changed. // \pre vol_exists: vol!=0 // \pre valid_numberOfScalarComponents: numberOfScalarComponents==1 || numberOfScalarComponents==4 //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::UpdateOpacityTransferFunction( vtkVolume *vol, int numberOfScalarComponents, unsigned int level) { assert("pre: vol_exists" && vol!=0); assert("pre: valid_numberOfScalarComponents" && (numberOfScalarComponents==1 || numberOfScalarComponents==4)); (void)numberOfScalarComponents; // remove warning in release mode. vtkVolumeProperty *volumeProperty=vol->GetProperty(); vtkPiecewiseFunction *scalarOpacity=volumeProperty->GetScalarOpacity(); vtkgl::ActiveTexture( vtkgl::TEXTURE2); //stay here this->OpacityTables->Vector[level].Update( scalarOpacity,this->BlendMode, this->ActualSampleDistance, this->TableRange, volumeProperty->GetScalarOpacityUnitDistance(0), volumeProperty->GetInterpolationType()==VTK_LINEAR_INTERPOLATION); // Restore default active texture vtkgl::ActiveTexture( vtkgl::TEXTURE0); return 1; } //----------------------------------------------------------------------------- // Prepare rendering in the offscreen framebuffer. // \pre ren_exists: ren!=0 // \pre vol_exists: vol!=0 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::SetupRender(vtkRenderer *ren, vtkVolume *vol) { assert("pre: ren_exists" && ren!=0); assert("pre: vol_exists" && vol!=0); double aspect[2]; int lowerLeft[2]; int usize, vsize; ren->GetTiledSizeAndOrigin(&usize,&vsize,lowerLeft,lowerLeft+1); usize = static_cast(usize*this->ReductionFactor); vsize = static_cast(vsize*this->ReductionFactor); this->ReducedSize[0]=usize; this->ReducedSize[1]=vsize; // the FBO has the size of the renderer (not the renderwindow), // we always starts at 0,0. glViewport(0,0, usize, vsize); glEnable( GL_SCISSOR_TEST ); // scissor on the FBO, on the reduced part. glScissor(0,0, usize, vsize); glClearColor(0.0, 0.0, 0.0, 0.0); // maxvalue is 1 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ren->ComputeAspect(); ren->GetAspect(aspect); double aspect2[2]; ren->vtkViewport::ComputeAspect(); ren->vtkViewport::GetAspect(aspect2); double aspectModification = aspect[0]*aspect2[1]/(aspect[1]*aspect2[0]); vtkCamera *cam = ren->GetActiveCamera(); glMatrixMode( GL_PROJECTION); if(usize && vsize) { this->TempMatrix[0]->DeepCopy(cam->GetProjectionTransformMatrix( aspectModification*usize/vsize, -1,1)); this->TempMatrix[0]->Transpose(); glLoadMatrixd(this->TempMatrix[0]->Element[0]); } else { glLoadIdentity(); } // push the model view matrix onto the stack, make sure we // adjust the mode first glMatrixMode(GL_MODELVIEW); glPushMatrix(); this->TempMatrix[0]->DeepCopy(vol->GetMatrix()); this->TempMatrix[0]->Transpose(); // insert camera view transformation glMultMatrixd(this->TempMatrix[0]->Element[0]); glShadeModel(GL_SMOOTH); glDisable( GL_LIGHTING); glEnable (GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); // very important, otherwise the first image looks dark. this->PrintError("SetupRender"); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::DebugDisplayBox(vtkPolyData *box) { vtkPoints *points=box->GetPoints(); vtkCellArray *polys=box->GetPolys(); cout<<"npts="<GetNumberOfPoints()<GetNumberOfPoints()) { double coords[3]; points->GetPoint(pointId,coords); cout<<"pointId="<GetMatrix( this->InvVolumeMatrix ); this->InvVolumeMatrix->Invert(); if(this->BoxSource==0) { this->BoxSource=vtkTessellatedBoxSource::New(); } this->BoxSource->SetBounds(worldBounds); this->BoxSource->SetLevel(0); this->BoxSource->QuadsOn(); if(this->Planes==0) { this->Planes=vtkPlaneCollection::New(); } this->Planes->RemoveAllItems(); vtkCamera *cam = ren->GetActiveCamera(); double camWorldRange[2]; double camWorldPos[4]; double camFocalWorldPoint[4]; double camWorldDirection[3]; double range[2]; double camPos[4]; double focalPoint[4]; double direction[3]; cam->GetPosition(camWorldPos); camWorldPos[3] = 1.0; this->InvVolumeMatrix->MultiplyPoint( camWorldPos, camPos ); if ( camPos[3] ) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } cam->GetFocalPoint(camFocalWorldPoint); camFocalWorldPoint[3]=1.0; this->InvVolumeMatrix->MultiplyPoint( camFocalWorldPoint,focalPoint ); if ( focalPoint[3] ) { focalPoint[0] /= focalPoint[3]; focalPoint[1] /= focalPoint[3]; focalPoint[2] /= focalPoint[3]; } // Compute the normalized view direction direction[0] = focalPoint[0] - camPos[0]; direction[1] = focalPoint[1] - camPos[1]; direction[2] = focalPoint[2] - camPos[2]; vtkMath::Normalize(direction); // The range (near/far) must also be transformed // into the local coordinate system. camWorldDirection[0] = camFocalWorldPoint[0] - camWorldPos[0]; camWorldDirection[1] = camFocalWorldPoint[1] - camWorldPos[1]; camWorldDirection[2] = camFocalWorldPoint[2] - camWorldPos[2]; vtkMath::Normalize(camWorldDirection); double camNearWorldPoint[4]; double camFarWorldPoint[4]; double camNearPoint[4]; double camFarPoint[4]; cam->GetClippingRange(camWorldRange); camNearWorldPoint[0] = camWorldPos[0] + camWorldRange[0]*camWorldDirection[0]; camNearWorldPoint[1] = camWorldPos[1] + camWorldRange[0]*camWorldDirection[1]; camNearWorldPoint[2] = camWorldPos[2] + camWorldRange[0]*camWorldDirection[2]; camNearWorldPoint[3] = 1.; camFarWorldPoint[0] = camWorldPos[0] + camWorldRange[1]*camWorldDirection[0]; camFarWorldPoint[1] = camWorldPos[1] + camWorldRange[1]*camWorldDirection[1]; camFarWorldPoint[2] = camWorldPos[2] + camWorldRange[1]*camWorldDirection[2]; camFarWorldPoint[3] = 1.; this->InvVolumeMatrix->MultiplyPoint( camNearWorldPoint, camNearPoint ); if (camNearPoint[3]) { camNearPoint[0] /= camNearPoint[3]; camNearPoint[1] /= camNearPoint[3]; camNearPoint[2] /= camNearPoint[3]; } this->InvVolumeMatrix->MultiplyPoint( camFarWorldPoint, camFarPoint ); if (camFarPoint[3]) { camFarPoint[0] /= camFarPoint[3]; camFarPoint[1] /= camFarPoint[3]; camFarPoint[2] /= camFarPoint[3]; } range[0] = sqrt(vtkMath::Distance2BetweenPoints(camNearPoint, camPos)); range[1] = sqrt(vtkMath::Distance2BetweenPoints(camFarPoint, camPos)); //double nearPoint[3], farPoint[3]; double dist = range[1] - range[0]; range[0] += dist / (2<<16); range[1] -= dist / (2<<16); if(this->NearPlane==0) { this->NearPlane= vtkPlane::New(); } //this->NearPlane->SetOrigin( nearPoint ); this->NearPlane->SetOrigin( camNearPoint ); this->NearPlane->SetNormal( direction ); this->Planes->AddItem(this->NearPlane); if ( this->ClippingPlanes ) { this->ClippingPlanes->InitTraversal(); vtkPlane *plane; while ( (plane = this->ClippingPlanes->GetNextItem()) ) { // Planes are in world coordinates, we need to // convert them in local coordinates double planeOrigin[4], planeNormal[4], planeP1[4]; plane->GetOrigin(planeOrigin); planeOrigin[3] = 1.; plane->GetNormal(planeNormal); planeP1[0] = planeOrigin[0] + planeNormal[0]; planeP1[1] = planeOrigin[1] + planeNormal[1]; planeP1[2] = planeOrigin[2] + planeNormal[2]; planeP1[3] = 1.; this->InvVolumeMatrix->MultiplyPoint(planeOrigin, planeOrigin); this->InvVolumeMatrix->MultiplyPoint(planeP1, planeP1); if( planeOrigin[3]) { planeOrigin[0] /= planeOrigin[3]; planeOrigin[1] /= planeOrigin[3]; planeOrigin[2] /= planeOrigin[3]; } if( planeP1[3]) { planeP1[0] /= planeP1[3]; planeP1[1] /= planeP1[3]; planeP1[2] /= planeP1[3]; } planeNormal[0] = planeP1[0] - planeOrigin[0]; planeNormal[1] = planeP1[1] - planeOrigin[1]; planeNormal[2] = planeP1[2] - planeOrigin[2]; vtkMath::Normalize(planeNormal); vtkPlane* localPlane = vtkPlane::New(); localPlane->SetOrigin(planeOrigin); localPlane->SetNormal(planeNormal); this->Planes->AddItem(localPlane); localPlane->Delete(); } } if(this->Clip==0) { this->Clip=vtkClipConvexPolyData::New(); this->Clip->SetInputConnection(this->BoxSource->GetOutputPort()); this->Clip->SetPlanes( this->Planes ); } this->Clip->Update(); if(this->Densify==0) { this->Densify=vtkDensifyPolyData::New(); this->Densify->SetInputConnection(this->Clip->GetOutputPort()); this->Densify->SetNumberOfSubdivisions(2); } this->Densify->Update(); this->ClippedBoundingBox = this->Densify->GetOutput(); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::RenderClippedBoundingBox( int tcoordFlag, size_t currentBlock, size_t numberOfBlocks, vtkRenderWindow *renWin ) { assert("pre: valid_currentBlock" && currentBlockClippedBoundingBox->GetPoints(); vtkCellArray *polys = this->ClippedBoundingBox->GetPolys(); vtkIdType npts; vtkIdType *pts; vtkIdType i, j; double center[3] = {0,0,0}; double min[3] = {VTK_DOUBLE_MAX, VTK_DOUBLE_MAX, VTK_DOUBLE_MAX}; double max[3] = {VTK_DOUBLE_MIN, VTK_DOUBLE_MIN, VTK_DOUBLE_MIN}; // First compute center point npts = points->GetNumberOfPoints(); for ( i = 0; i < npts; i++ ) { double pt[3]; points->GetPoint( i, pt ); for ( j = 0; j < 3; j++ ) { min[j] = (pt[j]max[j])?(pt[j]):(max[j]); } } center[0] = 0.5*(min[0]+max[0]); center[1] = 0.5*(min[1]+max[1]); center[2] = 0.5*(min[2]+max[2]); double *loadedBounds=0; vtkIdType *loadedExtent=0; if ( tcoordFlag ) { loadedBounds=this->CurrentScalar->GetLoadedBounds(); loadedExtent=this->CurrentScalar->GetLoadedExtent(); } double *spacing=this->GetInput()->GetSpacing(); double spacingSign[3]; i=0; while(i<3) { if(spacing[i]<0) { spacingSign[i]=-1.0; } else { spacingSign[i]=1.0; } ++i; } // make it double for the ratio of the progress. int polyId=0; double polyCount=static_cast(polys->GetNumberOfCells()); polys->InitTraversal(); int abort=0; while ( !abort && polys->GetNextCell(npts, pts) ) { vtkIdType start, end, inc; // Need to have at least a triangle if ( npts > 2 ) { // Check the cross product of the first two // vectors dotted with the vector from the // center to the second point. Is it positive or // negative? double p1[3], p2[3], p3[3]; double v1[3], v2[3], v3[3], v4[3]; points->GetPoint(pts[0], p1 ); points->GetPoint(pts[1], p2 ); points->GetPoint(pts[2], p3 ); v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2]; v2[0] = p2[0] - p3[0]; v2[1] = p2[1] - p3[1]; v2[2] = p2[2] - p3[2]; vtkMath::Cross( v1, v2, v3 ); vtkMath::Normalize(v3); v4[0] = p2[0] - center[0]; v4[1] = p2[1] - center[1]; v4[2] = p2[2] - center[2]; vtkMath::Normalize(v4); double dot = vtkMath::Dot( v3, v4 ); if (( dot < 0) && this->PreserveOrientation) { start = 0; end = npts; inc = 1; } else { start = npts-1; end = -1; inc = -1; } glBegin( GL_TRIANGLE_FAN ); // GL_POLYGON -> GL_TRIANGLE_FAN double vert[3]; double tcoord[3]; for ( i = start; i != end; i += inc ) { points->GetPoint(pts[i], vert); if ( tcoordFlag ) { for ( j = 0; j < 3; j++ ) { // loaded bounds take both cell data and point date cases into // account if(this->CellFlag) // texcoords between 0 and 1. More complex // depends on the loaded texture { tcoord[j] = spacingSign[j]*(vert[j] - loadedBounds[j*2]) / (loadedBounds[j*2+1] - loadedBounds[j*2]); } else // texcoords between 1/2N and 1-1/2N. { double tmp; // between 0 and 1 tmp = spacingSign[j]*(vert[j] - loadedBounds[j*2]) / (loadedBounds[j*2+1] - loadedBounds[j*2]); double delta=static_cast( loadedExtent[j*2+1]-loadedExtent[j*2]+1); tcoord[j]=(tmp*(delta-1)+0.5)/delta; } } vtkgl::MultiTexCoord3dv(vtkgl::TEXTURE0, tcoord); } glVertex3dv(vert); } glEnd(); } if(tcoordFlag) { // otherwise, we are rendering back face to initialize the zbuffer. if (!this->GeneratingCanonicalView && this->ReportProgress) { glFinish(); // Only invoke an event at most one every second. double currentTime=vtkTimerLog::GetUniversalTime(); if(currentTime - this->LastProgressEventTime > 1.0) { double progress=(static_cast(currentBlock)+polyId/polyCount)/ static_cast(numberOfBlocks); this->InvokeEvent(vtkCommand::VolumeMapperRenderProgressEvent, &progress); renWin->MakeCurrent(); this->LastProgressEventTime = currentTime; } } abort=renWin->CheckAbortStatus(); } ++polyId; } return abort; } void vtkMitkOpenGLGPUVolumeRayCastMapper::CopyFBOToTexture() { // in OpenGL copy texture to texture does not exist but // framebuffer to texture exists (and our FB is an FBO). // we have to copy and not just to switch color textures because the // colorbuffer has to accumulate color or values step after step. // Switching would not work because two different steps can draw different // polygons that don't overlap vtkgl::ActiveTexture(vtkgl::TEXTURE4); glBindTexture( GL_TEXTURE_2D, this->TextureObjects[ vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+1]); glReadBuffer(vtkgl::COLOR_ATTACHMENT0_EXT); glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,this->ReducedSize[0], this->ReducedSize[1]); if(this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { vtkgl::ActiveTexture(vtkgl::TEXTURE5); glBindTexture(GL_TEXTURE_2D,this->MaxValueFrameBuffer2); glReadBuffer(vtkgl::COLOR_ATTACHMENT0_EXT+1); glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,this->ReducedSize[0], this->ReducedSize[1]); } vtkgl::ActiveTexture(vtkgl::TEXTURE0); } //----------------------------------------------------------------------------- // Restore OpenGL state after rendering of the dataset. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::CleanupRender() { glPopMatrix(); glDisable(GL_CULL_FACE); } //----------------------------------------------------------------------------- // Build the fragment shader program that scale and bias a texture // for window/level purpose. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::BuildScaleBiasProgram() { if(this->ScaleBiasProgramShader==0) { GLuint programShader; GLuint fragmentShader; programShader=vtkgl::CreateProgram(); fragmentShader=vtkgl::CreateShader(vtkgl::FRAGMENT_SHADER); vtkgl::AttachShader(programShader,fragmentShader); vtkgl::DeleteShader(fragmentShader); // reference counting vtkgl::ShaderSource( fragmentShader,1, const_cast(&vtkMitkGPUVolumeRayCastMapper_ScaleBiasFS),0); vtkgl::CompileShader(fragmentShader); this->CheckCompilation(static_cast(fragmentShader)); vtkgl::LinkProgram(programShader); this->CheckLinkage(static_cast(programShader)); this->ScaleBiasProgramShader=static_cast(programShader); this->UFrameBufferTexture= static_cast(vtkgl::GetUniformLocation(programShader, "frameBufferTexture")); this->UScale=static_cast(vtkgl::GetUniformLocation(programShader, "scale")); this->UBias=static_cast(vtkgl::GetUniformLocation(programShader, "bias")); } } //----------------------------------------------------------------------------- // Render the offscreen buffer to the screen. // \pre ren_exists: ren!=0 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::RenderTextureToScreen(vtkRenderer *ren) { assert("pre: ren_exists" && ren!=0); if ( this->GeneratingCanonicalView ) { // We just need to copy of the data, not render it glBindTexture(GL_TEXTURE_2D, this->TextureObjects[ vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glPixelStorei( GL_PACK_ALIGNMENT, 1 ); unsigned char *outPtr = static_cast(this->CanonicalViewImageData->GetScalarPointer()); glGetTexImage( GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, outPtr ); return; } int lowerLeft[2]; int usize, vsize; ren->GetTiledSizeAndOrigin(&usize,&vsize,lowerLeft,lowerLeft+1); glViewport(lowerLeft[0],lowerLeft[1], usize, vsize); glEnable( GL_SCISSOR_TEST ); glScissor(lowerLeft[0],lowerLeft[1], usize, vsize); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, usize, 0.0, vsize, -1.0, 1.0 ); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glEnable(GL_BLEND); glBlendFunc( GL_ONE,GL_ONE_MINUS_SRC_ALPHA); // As we use replace mode, we don't need to set the color value. glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); glDisable(GL_DEPTH_TEST); double xOffset = 1.0 / usize; double yOffset = 1.0 / vsize; glDepthMask(GL_FALSE); double scale=1.0/this->FinalColorWindow; double bias=0.5-this->FinalColorLevel/this->FinalColorWindow; if(scale!=1.0 || bias!=0.0) { this->BuildScaleBiasProgram(); vtkgl::UseProgram(this->ScaleBiasProgramShader); if(this->UFrameBufferTexture!=-1) { vtkgl::Uniform1i(this->UFrameBufferTexture,0); } else { vtkErrorMacro(<<"uFrameBufferTexture is not a uniform variable."); } if(this->UScale!=-1) { vtkgl::Uniform1f(this->UScale,static_cast(scale)); } else { vtkErrorMacro(<<"uScale is not a uniform variable."); } if(this->UBias!=-1) { vtkgl::Uniform1f(this->UBias,static_cast(bias)); } else { vtkErrorMacro(<<"uBias is not a uniform variable."); } } else { glEnable(GL_TEXTURE_2D); // fixed pipeline } glBegin(GL_QUADS); glTexCoord2f(static_cast(xOffset),static_cast(yOffset)); glVertex2f(0.0,0.0); glTexCoord2f(static_cast(this->ReductionFactor-xOffset), static_cast(yOffset)); glVertex2f(static_cast(usize),0.0); glTexCoord2f(static_cast(this->ReductionFactor-xOffset), static_cast(this->ReductionFactor-yOffset)); glVertex2f(static_cast(usize),static_cast(vsize)); glTexCoord2f(static_cast(xOffset), static_cast(this->ReductionFactor-yOffset)); glVertex2f(0.0,static_cast(vsize)); glEnd(); // Restore the default mode. Used in overlay. glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); if(scale!=1.0 || bias!=0.0) { vtkgl::UseProgram(0); } else { glDisable(GL_TEXTURE_2D); } glDepthMask(GL_TRUE); glDisable(GL_BLEND); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } //----------------------------------------------------------------------------- // Update the reduction factor of the render viewport (this->ReductionFactor) // according to the time spent in seconds to render the previous frame // (this->TimeToDraw) and a time in seconds allocated to render the next // frame (allocatedTime). // \pre valid_current_reduction_range: this->ReductionFactor>0.0 && this->ReductionFactor<=1.0 // \pre positive_TimeToDraw: this->TimeToDraw>=0.0 // \pre positive_time: allocatedTime>0.0 // \post valid_new_reduction_range: this->ReductionFactor>0.0 && this->ReductionFactor<=1.0 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::ComputeReductionFactor( double allocatedTime) { assert("pre: valid_current_reduction_range" && this->ReductionFactor>0.0 && this->ReductionFactor<=1.0); assert("pre: positive_TimeToDraw" && this->TimeToDraw>=0.0); assert("pre: positive_time" && allocatedTime>0.0); if ( this->GeneratingCanonicalView ) { this->ReductionFactor = 1.0; return; } if ( !this->AutoAdjustSampleDistances ) { this->ReductionFactor = 1.0 / this->ImageSampleDistance; return; } if ( this->TimeToDraw ) { double oldFactor = this->ReductionFactor; double timeToDraw; if (allocatedTime < 1.0) { timeToDraw = this->SmallTimeToDraw; if ( timeToDraw == 0.0 ) { timeToDraw = this->BigTimeToDraw/3.0; } } else { timeToDraw = this->BigTimeToDraw; } if ( timeToDraw == 0.0 ) { timeToDraw = 10.0; } double fullTime = timeToDraw / this->ReductionFactor; double newFactor = allocatedTime / fullTime; if ( oldFactor == 1.0 || newFactor / oldFactor > 1.3 || newFactor / oldFactor < .95 ) { this->ReductionFactor = (newFactor+oldFactor)/2.0; this->ReductionFactor = (this->ReductionFactor > 5.0)?(1.00):(this->ReductionFactor); this->ReductionFactor = (this->ReductionFactor > 1.0)?(0.99):(this->ReductionFactor); this->ReductionFactor = (this->ReductionFactor < 0.1)?(0.10):(this->ReductionFactor); if ( 1.0/this->ReductionFactor > this->MaximumImageSampleDistance ) { this->ReductionFactor = 1.0 / this->MaximumImageSampleDistance; } if ( 1.0/this->ReductionFactor < this->MinimumImageSampleDistance ) { this->ReductionFactor = 1.0 / this->MinimumImageSampleDistance; } } } else { this->ReductionFactor = 1.0; } assert("post: valid_new_reduction_range" && this->ReductionFactor>0.0 && this->ReductionFactor<=1.0); } //----------------------------------------------------------------------------- // Rendering initialization including making the context current, loading // necessary extensions, allocating frame buffers, updating transfer function, // computing clipping regions, and building the fragment shader. // // Pre-conditions: // - ren != NULL // - vol != NULL // - ren->GetRenderWindow() != NULL // - 1 <= numberOfScalarComponents <= 4 // - numberOfLevels >= 1 //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::PreRender(vtkRenderer *ren, vtkVolume *vol, double datasetBounds[6], double scalarRange[2], int numberOfScalarComponents, unsigned int numberOfLevels) { // make sure our window is the current OpenGL context. ren->GetRenderWindow()->MakeCurrent(); // If we haven't already succeeded in loading the extensions, // try to load them if(!this->LoadExtensionsSucceeded) { this->LoadExtensions(ren->GetRenderWindow()); } // If we can't load the necessary extensions, provide // feedback on why it failed. if(!this->LoadExtensionsSucceeded) { vtkErrorMacro( "Rendering failed because the following OpenGL extensions " "are required but not supported: " << (this->UnsupportedRequiredExtensions->Stream.str()).c_str()); return; } // Create the OpenGL object that we need this->CreateOpenGLObjects(); // Compute the reduction factor that may be necessary to get // the interactive rendering rate that we want this->ComputeReductionFactor(vol->GetAllocatedRenderTime()); // Allocate the frame buffers if(!this->AllocateFrameBuffers(ren)) { vtkErrorMacro("Not enough GPU memory to create a framebuffer."); return; } // Save the scalar range - this is what we will use for the range // of the transfer functions this->TableRange[0]=scalarRange[0]; this->TableRange[1]=scalarRange[1]; if(this->RGBTable==0) { this->RGBTable=new vtkRGBTable; } if(this->MaskInput!=0) { if(this->Mask1RGBTable==0) { this->Mask1RGBTable=new vtkRGBTable; } if(this->Mask2RGBTable==0) { this->Mask2RGBTable=new vtkRGBTable; } } // Update the color transfer function this->UpdateColorTransferFunction(vol,numberOfScalarComponents); // Update the noise texture that will be used to jitter rays to // reduce alliasing artifacts this->UpdateNoiseTexture(); // We are going to change the blending mode and blending function - // so lets push here so we can pop later glPushAttrib(GL_COLOR_BUFFER_BIT); // If this is the canonical view - we don't want to intermix so we'll just // start by clearing the z buffer. if ( this->GeneratingCanonicalView ) { glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } // See if the volume transform is orientation-preserving vtkMatrix4x4 *m=vol->GetMatrix(); double det=vtkMath::Determinant3x3( m->GetElement(0,0),m->GetElement(0,1),m->GetElement(0,2), m->GetElement(1,0),m->GetElement(1,1),m->GetElement(1,2), m->GetElement(2,0),m->GetElement(2,1),m->GetElement(2,2)); this->PreserveOrientation=det>0; // If we have clipping planes, render the back faces of the clipped // bounding box of the whole dataset to set the zbuffer. if(this->ClippingPlanes && this->ClippingPlanes->GetNumberOfItems()!=0) { // push the model view matrix onto the stack, make sure we // adjust the mode first glMatrixMode(GL_MODELVIEW); glPushMatrix(); this->TempMatrix[0]->DeepCopy(vol->GetMatrix()); this->TempMatrix[0]->Transpose(); glMultMatrixd(this->TempMatrix[0]->Element[0]); this->ClipBoundingBox(ren,datasetBounds,vol); glEnable (GL_CULL_FACE); glCullFace (GL_FRONT); glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE); this->RenderClippedBoundingBox(0,0,1,ren->GetRenderWindow()); glDisable (GL_CULL_FACE); glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); //glMatrixMode(GL_MODELVIEW); glPopMatrix(); } // Check if everything is OK this->CheckFrameBufferStatus(); // Intermixed geometry: Grab the depth buffer into a texture int size[2]; int lowerLeft[2]; ren->GetTiledSizeAndOrigin(size,size+1,lowerLeft,lowerLeft+1); vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D, static_cast( this->TextureObjects[ vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectDepthMap])); glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,lowerLeft[0],lowerLeft[1],size[0], size[1]); vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); int parallelProjection=ren->GetActiveCamera()->GetParallelProjection(); // initialize variables to prevent compiler warnings. int rayCastMethod=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP; int shadeMethod=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNotUsed; int componentMethod=vtkMitkOpenGLGPUVolumeRayCastMapperComponentNotUsed; switch(this->BlendMode) { case vtkVolumeMapper::COMPOSITE_BLEND: switch(numberOfScalarComponents) { case 1: componentMethod=vtkMitkOpenGLGPUVolumeRayCastMapperComponentOne; break; case 4: componentMethod=vtkMitkOpenGLGPUVolumeRayCastMapperComponentFour; break; default: assert("check: impossible case" && false); break; } if(this->MaskInput!=0) { rayCastMethod= vtkMitkOpenGLGPUVolumeRayCastMapperMethodCompositeMask; } else { //cout<<"program is composite+shade"<GetProperty()->GetShade() ) { shadeMethod=vtkMitkOpenGLGPUVolumeRayCastMapperShadeYes; assert("check: only_1_component_todo" && numberOfScalarComponents==1); } else { shadeMethod=vtkMitkOpenGLGPUVolumeRayCastMapperShadeNo; //cout<<"program is composite"<ComputeNumberOfCroppingRegions(); // TODO AMR vs single block if(this->AMRMode) { NumberOfCroppingRegions=2; // >1, means use do compositing between blocks } this->BuildProgram(parallelProjection,rayCastMethod,shadeMethod, componentMethod); this->CheckLinkage(this->ProgramShader); vtkgl::UseProgram(this->ProgramShader); // for active texture 0, dataset if(numberOfScalarComponents==1) { // colortable vtkgl::ActiveTexture(vtkgl::TEXTURE1); this->RGBTable->Bind(); if(this->MaskInput!=0) { vtkgl::ActiveTexture(vtkgl::TEXTURE8); this->Mask1RGBTable->Bind(); vtkgl::ActiveTexture(vtkgl::TEXTURE9); this->Mask2RGBTable->Bind(); } } GLint uDataSetTexture; uDataSetTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"dataSetTexture"); if(uDataSetTexture!=-1) { vtkgl::Uniform1i(uDataSetTexture,0); } else { vtkErrorMacro(<<"dataSetTexture is not a uniform variable."); } if ( this->MaskInput) { // Make the mask texture available on texture unit 7 GLint uMaskTexture; uMaskTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"maskTexture"); if(uMaskTexture!=-1) { vtkgl::Uniform1i(uMaskTexture,7); } else { vtkErrorMacro(<<"maskTexture is not a uniform variable."); } } if(numberOfScalarComponents==1) { GLint uColorTexture; uColorTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"colorTexture"); if(uColorTexture!=-1) { vtkgl::Uniform1i(uColorTexture,1); } else { vtkErrorMacro(<<"colorTexture is not a uniform variable."); } if(this->MaskInput!=0) { GLint uMask1ColorTexture; uMask1ColorTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"mask1ColorTexture"); if(uMask1ColorTexture!=-1) { vtkgl::Uniform1i(uMask1ColorTexture,8); } else { vtkErrorMacro(<<"mask1ColorTexture is not a uniform variable."); } GLint uMask2ColorTexture; uMask2ColorTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"mask2ColorTexture"); if(uMask2ColorTexture!=-1) { vtkgl::Uniform1i(uMask2ColorTexture,9); } else { vtkErrorMacro(<<"mask2ColorTexture is not a uniform variable."); } GLint uMaskBlendFactor; uMaskBlendFactor=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"maskBlendFactor"); if(uMaskBlendFactor!=-1) { vtkgl::Uniform1f(uMaskBlendFactor,this->MaskBlendFactor); } else { vtkErrorMacro(<<"maskBlendFactor is not a uniform variable."); } } } GLint uOpacityTexture; uOpacityTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"opacityTexture"); if(uOpacityTexture!=-1) { vtkgl::Uniform1i(uOpacityTexture,2); } else { vtkErrorMacro(<<"opacityTexture is not a uniform variable."); } // depthtexture vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D,static_cast(this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectDepthMap])); GLint uDepthTexture; uDepthTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"depthTexture"); if(uDepthTexture!=-1) { vtkgl::Uniform1i(uDepthTexture,3); } else { vtkErrorMacro(<<"depthTexture is not a uniform variable."); } // noise texture vtkgl::ActiveTexture( vtkgl::TEXTURE6 ); glBindTexture(GL_TEXTURE_2D,static_cast(this->NoiseTextureId)); GLint uNoiseTexture; uNoiseTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"noiseTexture"); if(uNoiseTexture!=-1) { vtkgl::Uniform1i(uNoiseTexture,6); } else { vtkErrorMacro(<<"noiseTexture is not a uniform variable."); } this->CheckFrameBufferStatus(); if(this->NumberOfCroppingRegions>1) { // framebuffer texture if(rayCastMethod!=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP && rayCastMethod!=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIP) { vtkgl::ActiveTexture( vtkgl::TEXTURE4 ); glBindTexture(GL_TEXTURE_2D,static_cast(this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront])); GLint uFrameBufferTexture; uFrameBufferTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"frameBufferTexture"); this->PrintError("framebuffertexture 1"); if(uFrameBufferTexture!=-1) { vtkgl::Uniform1i(uFrameBufferTexture,4); } else { vtkErrorMacro(<<"frameBufferTexture is not a uniform variable."); } this->PrintError("framebuffertexture 2"); } this->CheckFrameBufferStatus(); // max scalar value framebuffer texture if(this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { vtkgl::ActiveTexture( vtkgl::TEXTURE5 ); glBindTexture(GL_TEXTURE_2D,static_cast(this->MaxValueFrameBuffer2)); GLint uScalarBufferTexture; uScalarBufferTexture=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"scalarBufferTexture"); this->PrintError("scalarbuffertexture 1"); if(uScalarBufferTexture!=-1) { vtkgl::Uniform1i(uScalarBufferTexture,5); } else { vtkErrorMacro(<<"scalarBufferTexture is not a uniform variable."); } this->PrintError("scalarbuffertexture 2"); } } this->CheckFrameBufferStatus(); GLint uWindowLowerLeftCorner; uWindowLowerLeftCorner=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"windowLowerLeftCorner"); if(uWindowLowerLeftCorner!=-1) { vtkgl::Uniform2f(uWindowLowerLeftCorner,static_cast(lowerLeft[0]), static_cast(lowerLeft[1])); } else { vtkErrorMacro(<<"windowLowerLeftCorner is not a uniform variable."); } GLint uInvOriginalWindowSize; uInvOriginalWindowSize=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"invOriginalWindowSize"); if(uInvOriginalWindowSize!=-1) { vtkgl::Uniform2f(uInvOriginalWindowSize, static_cast(1.0/size[0]), static_cast(1.0/size[1])); } else { // yes it is not error. It is only actually used when there is some // complex cropping (this->NumberOfCroppingRegions>1). Some GLSL compilers // may remove the uniform variable for optimization when it is not used. vtkDebugMacro( <<"invOriginalWindowSize is not an active uniform variable."); } size[0] = static_cast(size[0]*this->ReductionFactor); size[1] = static_cast(size[1]*this->ReductionFactor); GLint uInvWindowSize; uInvWindowSize=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"invWindowSize"); if(uInvWindowSize!=-1) { vtkgl::Uniform2f(uInvWindowSize,static_cast(1.0/size[0]), static_cast(1.0/size[1])); } else { vtkErrorMacro(<<"invWindowSize is not a uniform variable."); } this->PrintError("after uniforms for textures"); this->CheckFrameBufferStatus(); GLint savedFrameBuffer; glGetIntegerv(vtkgl::FRAMEBUFFER_BINDING_EXT,&savedFrameBuffer); this->SavedFrameBuffer=static_cast(savedFrameBuffer); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(this->FrameBufferObject)); this->BindFramebuffer(); GLenum buffer[4]; buffer[0] = vtkgl::COLOR_ATTACHMENT0_EXT; if(this->NumberOfCroppingRegions>1 && this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND) { // max scalar frame buffer buffer[1] = vtkgl::COLOR_ATTACHMENT1_EXT; } else { buffer[1] = GL_NONE; } vtkgl::DrawBuffers(2,buffer); this->CheckFrameBufferStatus(); // Use by the composite+shade program double shininess=vol->GetProperty()->GetSpecularPower(); if(shininess>128.0) { shininess=128.0; // upper limit for the OpenGL shininess. } glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,static_cast(shininess)); glDisable(GL_COLOR_MATERIAL); // other mapper may have enable that. GLfloat values[4]; values[3]=1.0; values[0]=0.0; values[1]=values[0]; values[2]=values[0]; glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,values); values[0]=static_cast(vol->GetProperty()->GetAmbient()); values[1]=values[0]; values[2]=values[0]; glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,values); values[0]=static_cast(vol->GetProperty()->GetDiffuse()); values[1]=values[0]; values[2]=values[0]; glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,values); values[0]=static_cast(vol->GetProperty()->GetSpecular()); values[1]=values[0]; values[2]=values[0]; glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,values); // cout << "pingpong=" << this->PingPongFlag << endl; // To initialize the second color buffer vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront], 0); vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+1], 0); buffer[0] = vtkgl::COLOR_ATTACHMENT0_EXT; buffer[1] = vtkgl::COLOR_ATTACHMENT1_EXT; vtkgl::DrawBuffers(2,buffer); // cout << "check before setup" << endl; this->CheckFrameBufferStatus(); this->SetupRender(ren,vol); // restore in case of composite with no cropping or streaming. buffer[0] = vtkgl::COLOR_ATTACHMENT0_EXT; buffer[1] = GL_NONE; vtkgl::DrawBuffers(2,buffer); vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D,0,0); // cout << "check after color init" << endl; this->CheckFrameBufferStatus(); if(this->NumberOfCroppingRegions>1 && (this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MAXIMUM_INTENSITY_BLEND)) { // cout << "this->MaxValueFrameBuffer="<< this->MaxValueFrameBuffer <MaxValueFrameBuffer2="<< this->MaxValueFrameBuffer2 <MaxValueFrameBuffer,0); vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D, this->MaxValueFrameBuffer2,0); buffer[0] = vtkgl::COLOR_ATTACHMENT0_EXT; buffer[1] = vtkgl::COLOR_ATTACHMENT1_EXT; vtkgl::DrawBuffers(2,buffer); if(this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { glClearColor(1.0, 0.0, 0.0, 0.0); } else { glClearColor(0.0, 0.0, 0.0, 0.0); // for MAXIMUM_INTENSITY_BLEND } // cout << "check before clear on max" << endl; this->CheckFrameBufferStatus(); glClear(GL_COLOR_BUFFER_BIT); } if(this->NumberOfCroppingRegions>1) { // color buffer target in the color attachement 0 vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront], 0); // color buffer input is on texture unit 4. vtkgl::ActiveTexture(vtkgl::TEXTURE4); glBindTexture(GL_TEXTURE_2D,this->TextureObjects[vtkMitkOpenGLGPUVolumeRayCastMapperTextureObjectFrameBufferLeftFront+1]); if(this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { // max buffer target in the color attachment 1 vtkgl::FramebufferTexture2DEXT(vtkgl::FRAMEBUFFER_EXT, vtkgl::COLOR_ATTACHMENT0_EXT+1, GL_TEXTURE_2D, this->MaxValueFrameBuffer,0); // max buffer input is on texture unit 5. vtkgl::ActiveTexture(vtkgl::TEXTURE5); glBindTexture(GL_TEXTURE_2D,this->MaxValueFrameBuffer2); } vtkgl::ActiveTexture(vtkgl::TEXTURE0); } this->CheckFrameBufferStatus(); if(this->OpacityTables!=0 && this->OpacityTables->Vector.size()!=numberOfLevels) { delete this->OpacityTables; this->OpacityTables=0; } if(this->OpacityTables==0) { this->OpacityTables=new vtkOpacityTables(numberOfLevels); } // debug code // DO NOT REMOVE the following commented line // this->ValidateProgram(); glCullFace (GL_BACK); // otherwise, we are rendering back face to initialize the zbuffer. if(!this->GeneratingCanonicalView && this->ReportProgress) { // initialize the time to avoid a progress event at the beginning. this->LastProgressEventTime=vtkTimerLog::GetUniversalTime(); } this->PrintError("PreRender end"); } //----------------------------------------------------------------------------- // Compute how each axis of a cell is projected on the viewport in pixel. // This requires to have information about the camera and about the volume. // It set the value of IgnoreSampleDistancePerPixel to true in case of // degenerated case (axes aligned with the view). //----------------------------------------------------------------------------- double vtkMitkOpenGLGPUVolumeRayCastMapper::ComputeMinimalSampleDistancePerPixel( vtkRenderer *renderer, vtkVolume *volume) { // For each of the 3 directions of a cell, compute the step in z // (world coordinate, not eye/camera coordinate) // to go to the next pixel in x. // Same for the next pixel in y. // Keep the minimum of both zstep // Then keep the minimum for the 3 directions. // in case of either the numerator or the denominator of each ratio is 0. this->IgnoreSampleDistancePerPixel=true; double result=0.0; vtkMatrix4x4 *worldToDataset=volume->GetMatrix(); vtkCamera *camera=renderer->GetActiveCamera(); vtkMatrix4x4 *eyeToWorld=camera->GetViewTransformMatrix(); vtkMatrix4x4 *eyeToDataset=vtkMatrix4x4::New(); vtkMatrix4x4::Multiply4x4(eyeToWorld,worldToDataset,eyeToDataset); int usize; int vsize; renderer->GetTiledSize(&usize,&vsize); vtkMatrix4x4 *viewportToEye=camera->GetProjectionTransformMatrix( usize/static_cast(vsize),0.0,1.0); double volBounds[6]; this->GetInput()->GetBounds(volBounds); int dims[3]; this->GetInput()->GetDimensions(dims); double v0[4]; v0[0]=volBounds[0]; v0[1]=volBounds[2]; v0[2]=volBounds[4]; v0[3]=1.0; double w0[4]; eyeToDataset->MultiplyPoint(v0,w0); double z0; if(w0[3]!=0.0) { z0=w0[2]/w0[3]; } else { z0=0.0; vtkGenericWarningMacro( "eyeToWorld transformation has some projective component." ); } double p0[4]; viewportToEye->MultiplyPoint(w0,p0); p0[0]/=p0[3]; p0[1]/=p0[3]; p0[2]/=p0[3]; bool inFrustum=p0[0]>=-1.0 && p0[0]<=1.0 && p0[1]>=-1.0 && p0[1]<=1.0 && p0[2]>=-1.0 && p0[2]<=1.0; if(inFrustum) { int dim=0; while(dim<3) { double v1[4]; int coord=0; while(coord<3) { if(coord==dim) { v1[coord]=volBounds[2*coord+1]; } else { v1[coord]=volBounds[2*coord]; // same as v0[coord]; } ++coord; } v1[3]=1.0; double w1[4]; eyeToDataset->MultiplyPoint(v1,w1); double z1; if(w1[3]!=0.0) { z1=w1[2]/w1[3]; } else { z1=0.0; vtkGenericWarningMacro( "eyeToWorld transformation has some projective component." ); } double p1[4]; viewportToEye->MultiplyPoint(w1,p1); p1[0]/=p1[3]; p1[1]/=p1[3]; p1[2]/=p1[3]; inFrustum=p1[0]>=-1.0 && p1[0]<=1.0 && p1[1]>=-1.0 && p1[1]<=1.0 && p1[2]>=-1.0 && p1[2]<=1.0; if(inFrustum) { double dx=fabs(p1[0]-p0[0]); double dy=fabs(p1[1]-p0[1]); double dz=fabs(z1-z0); dz=dz/(dims[dim]-1); dx=dx/(dims[dim]-1)*usize; dy=dy/(dims[dim]-1)*vsize; if(dz!=0.0) { if(dx!=0.0) { double d=dz/dx; if(!this->IgnoreSampleDistancePerPixel) { if(result>d) { result=d; } } else { this->IgnoreSampleDistancePerPixel=false; result=d; } } if(dy!=0.0) { double d=dz/dy; if(!this->IgnoreSampleDistancePerPixel) { if(result>d) { result=d; } } else { this->IgnoreSampleDistancePerPixel=false; result=d; } } } } ++dim; } } eyeToDataset->Delete(); if(this->IgnoreSampleDistancePerPixel) { // cout<<"ignore SampleDistancePerPixel"<ValidateProgram(); this->PrintError("before render"); if(!this->Cropping) { this->RenderWholeVolume(ren,vol); } else { this->ClipCroppingRegionPlanes(); this->RenderRegions(ren,vol); } this->PrintError("after render"); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::PostRender( vtkRenderer *ren, int numberOfScalarComponents) { this->PrintError("PostRender1"); if(this->NumberOfCroppingRegions>1) { if(this->BlendMode==vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND || this->BlendMode==vtkMitkGPUVolumeRayCastMapper::MINIMUM_INTENSITY_BLEND) { vtkgl::ActiveTexture( vtkgl::TEXTURE5 ); glBindTexture(GL_TEXTURE_2D,0); } if(this->LastRayCastMethod!=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP && this->LastRayCastMethod!=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIP) { vtkgl::ActiveTexture( vtkgl::TEXTURE4 ); glBindTexture(GL_TEXTURE_2D,0); } } // noisetexture vtkgl::ActiveTexture(vtkgl::TEXTURE6); glBindTexture(GL_TEXTURE_2D,0); // depthtexture vtkgl::ActiveTexture(vtkgl::TEXTURE3); glBindTexture(GL_TEXTURE_2D,0); // opacity vtkgl::ActiveTexture(vtkgl::TEXTURE2); glBindTexture(GL_TEXTURE_1D,0); if(numberOfScalarComponents==1) { vtkgl::ActiveTexture(vtkgl::TEXTURE1); glBindTexture(GL_TEXTURE_1D,0); } // mask, if any if(this->MaskInput!=0) { vtkgl::ActiveTexture(vtkgl::TEXTURE7); glBindTexture(vtkgl::TEXTURE_3D_EXT,0); } // back to active texture 0. vtkgl::ActiveTexture(vtkgl::TEXTURE0); glBindTexture(vtkgl::TEXTURE_3D_EXT,0); vtkgl::UseProgram(0); this->PrintError("after UseProgram(0)"); this->CleanupRender(); this->PrintError("after CleanupRender"); vtkgl::BindFramebufferEXT(vtkgl::FRAMEBUFFER_EXT, static_cast(this->SavedFrameBuffer)); this->SavedFrameBuffer=0; // Undo the viewport change we made to reduce resolution int size[2]; int lowerLeft[2]; ren->GetTiledSizeAndOrigin(size,size+1,lowerLeft,lowerLeft+1); glViewport(lowerLeft[0],lowerLeft[1], size[0], size[1]); glEnable( GL_SCISSOR_TEST ); glScissor(lowerLeft[0],lowerLeft[1], size[0], size[1]); // Render the texture to the screen - this copies the offscreen buffer // onto the screen as a texture mapped polygon this->RenderTextureToScreen(ren); this->PrintError("after RenderTextureToScreen"); glEnable(GL_DEPTH_TEST); glPopAttrib(); // restore the blending mode and function glFinish(); this->PrintError("PostRender2"); } //----------------------------------------------------------------------------- // The main render method called from the superclass //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::GPURender(vtkRenderer *ren, vtkVolume *vol) { // We've already checked that we have input vtkImageData *input = this->GetTransformedInput(); // Get the bounds of this data double bounds[6]; this->GetBounds(bounds); // Get the scalar range. First we have to get the scalars. double range[2]; vtkDataArray *scalars=this->GetScalars(input,this->ScalarMode, this->ArrayAccessMode, this->ArrayId,this->ArrayName, this->CellFlag); // How many components are there? int numberOfScalarComponents=scalars->GetNumberOfComponents(); // If it is just one, then get the range from the scalars if(numberOfScalarComponents==1) { // Warning: here, we ignore the blank cells. scalars->GetRange(range); } // If it is 3, then use the 4th component's range since that is // the component that will be passed through the scalar opacity // transfer function to look up opacity else { // Note that we've already checked data type and we know this is // unsigned char scalars->GetRange(range,3); } // The rendering work has been broken into 3 stages to support AMR // volume rendering in blocks. Here we are simply rendering the // whole volume as one block. Note that if the volume is too big // to fix into texture memory, it will be streamed through in the // RenderBlock method. this->PreRender(ren,vol,bounds,range,numberOfScalarComponents,1); if(!this->OpacityTables) this->PreRender(ren,vol,bounds,range,numberOfScalarComponents,1); if(this->LoadExtensionsSucceeded) { this->RenderBlock(ren,vol,0); this->PostRender(ren,numberOfScalarComponents); } // Let's just make sure no OpenGL errors occurred during this render this->PrintError("End GPU Render"); // If this isn't a canonical view render, then update the progress to // 1 because we are done. if (!this->GeneratingCanonicalView ) { double progress=1.0; this->InvokeEvent(vtkCommand::VolumeMapperRenderProgressEvent,&progress); ren->GetRenderWindow()->MakeCurrent(); } } //----------------------------------------------------------------------------- // Render a the whole volume. // \pre this->ProgramShader!=0 and is linked. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::RenderWholeVolume(vtkRenderer *ren, vtkVolume *vol) { double volBounds[6]; this->GetTransformedInput()->GetBounds(volBounds); this->RenderSubVolume(ren,volBounds,vol); } //----------------------------------------------------------------------------- // Sort regions from front to back. //----------------------------------------------------------------------------- class vtkRegionDistance2 { public: size_t Id; // 0<=Id<27 // square distance between camera center to region center: >=0 double Distance2; }; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- extern "C" int vtkRegionComparisonFunction(const void *x, const void *y) { double dx=static_cast(x)->Distance2; double dy=static_cast(y)->Distance2; int result; if(dxdy) { result=1; } else { result=0; } } return result; } //----------------------------------------------------------------------------- // Render a subvolume. // \pre this->ProgramShader!=0 and is linked. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::RenderRegions(vtkRenderer *ren, vtkVolume *vol) { double bounds[27][6]; double distance2[27]; double camPos[4]; ren->GetActiveCamera()->GetPosition(camPos); double volBounds[6]; this->GetInput()->GetBounds(volBounds); // Pass camera through inverse volume matrix // so that we are in the same coordinate system vol->GetMatrix( this->InvVolumeMatrix ); camPos[3] = 1.0; this->InvVolumeMatrix->Invert(); this->InvVolumeMatrix->MultiplyPoint( camPos, camPos ); if ( camPos[3] ) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } // These are the region limits for x (first four), y (next four) and // z (last four). The first region limit is the lower bound for // that axis, the next two are the region planes along that axis, and // the final one in the upper bound for that axis. double limit[12]; size_t i; for ( i = 0; i < 3; i++ ) { limit[i*4 ] = volBounds[i*2]; limit[i*4+1] = this->ClippedCroppingRegionPlanes[i*2]; limit[i*4+2] = this->ClippedCroppingRegionPlanes[i*2+1]; limit[i*4+3] = volBounds[i*2+1]; } // For each of the 27 possible regions, find out if it is enabled, // and if so, compute the bounds and the distance from the camera // to the center of the region. size_t numRegions = 0; size_t region; for ( region = 0; region < 27; region++ ) { int regionFlag = 1<CroppingRegionFlags & regionFlag ) { // what is the coordinate in the 3x3x3 grid size_t loc[3]; loc[0] = region%3; loc[1] = (region/3)%3; loc[2] = (region/9)%3; // make sure the cropping region is not empty NEW // otherwise, we skip the region. if((limit[loc[0]]!=limit[loc[0]+1]) && (limit[loc[1]+4]!=limit[loc[1]+5]) && (limit[loc[2]+8]!=limit[loc[2]+9])) { // compute the bounds and center double center[3]; for ( i = 0; i < 3; i++ ) { bounds[numRegions][i*2 ] = limit[4*i+loc[i]]; bounds[numRegions][i*2+1] = limit[4*i+loc[i]+1]; center[i]=(bounds[numRegions][i*2]+bounds[numRegions][i*2+1])*0.5; } // compute the distance squared to the center distance2[numRegions] = (camPos[0]-center[0])*(camPos[0]-center[0]) + (camPos[1]-center[1])*(camPos[1]-center[1]) + (camPos[2]-center[2])*(camPos[2]-center[2]); // we've added one region numRegions++; } } } vtkRegionDistance2 regions[27]; i=0; while(iNumberOfCroppingRegions>=0); } //----------------------------------------------------------------------------- // slabsDataSet are position of the slabs in dataset coordinates. // slabsPoints are position of the slabs in points coordinates. // For instance, slabsDataSet[0] is the position of the plane bounding the slab // on the left of x axis of the dataset. slabsPoints[0]=0.3 means that // this plane lies between point 0 and point 1 along the x-axis. // There is no clamping/clipping according to the dataset bounds so, // slabsPoints can be negative or excess the number of points along the // corresponding axis. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::SlabsFromDatasetToIndex( double slabsDataSet[6], double slabsPoints[6]) { double *spacing=this->GetInput()->GetSpacing(); double origin[3]; // take spacing sign into account double *bds = this->GetInput()->GetBounds(); origin[0] = bds[0]; origin[1] = bds[2]; origin[2] = bds[4]; int i=0; while(i<6) { slabsPoints[i]=(slabsDataSet[i] - origin[i/2]) / spacing[i/2]; ++i; } } //----------------------------------------------------------------------------- // slabsDataSet are position of the slabs in dataset coordinates. // slabsPoints are position of the slabs in points coordinates. // For instance, slabsDataSet[0] is the position of the plane bounding the slab // on the left of x axis of the dataset. slabsPoints[0]=0.3 means that // this plane lies between point 0 and point 1 along the x-axis. // There is no clamping/clipping according to the dataset bounds so, // slabsPoints can be negative or excess the number of points along the // corresponding axis. //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::SlabsFromIndexToDataset( double slabsPoints[6], double slabsDataSet[6]) { double *spacing=this->GetInput()->GetSpacing(); double origin[3]; // take spacing sign into account double *bds = this->GetInput()->GetBounds(); origin[0] = bds[0]; origin[1] = bds[2]; origin[2] = bds[4]; int i=0; while(i<6) { slabsDataSet[i]=slabsPoints[i]*spacing[i/2]+origin[i/2]; ++i; } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- class vtkStreamBlock { public: double Bounds[6]; double Extent[6]; }; //----------------------------------------------------------------------------- // Render a subvolume. bounds are in world coordinates. // \pre this->ProgramShader!=0 and is linked. //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::RenderSubVolume(vtkRenderer *ren, double bounds[6], vtkVolume *volume) { // Time to load scalar field size_t i; int wholeTextureExtent[6]; this->GetTransformedInput()->GetExtent(wholeTextureExtent); if(this->CellFlag) { i=1; while(i<6) { wholeTextureExtent[i]--; i+=2; } } // 1. Found out the extent of the subvolume double realExtent[6]; int subvolumeTextureExtent[6]; this->SlabsFromDatasetToIndex(bounds,realExtent); if(this->CellFlag) // 3D texture are celldata { // texture extents are expressed in cells in this case i=0; while(i<6) { subvolumeTextureExtent[i]=vtkMath::Floor(realExtent[i]-0.5); ++i; subvolumeTextureExtent[i]=vtkMath::Floor(realExtent[i]-0.5)+1; ++i; } } else { // texture extents are expressed in points in this case i=0; while(i<6) { subvolumeTextureExtent[i]=vtkMath::Floor(realExtent[i]); ++i; subvolumeTextureExtent[i]=vtkMath::Floor(realExtent[i])+1; // used to not have +1 ++i; } } i=0; while(i<6) { assert("check: wholeTextureExtent" && wholeTextureExtent[i]==0); if(subvolumeTextureExtent[i]wholeTextureExtent[i]) { subvolumeTextureExtent[i]=wholeTextureExtent[i]; } ++i; } assert("check: subvolume_inside_wholevolume" && subvolumeTextureExtent[0]>=wholeTextureExtent[0] && subvolumeTextureExtent[1]<=wholeTextureExtent[1] && subvolumeTextureExtent[2]>=wholeTextureExtent[2] && subvolumeTextureExtent[3]<=wholeTextureExtent[3] && subvolumeTextureExtent[4]>=wholeTextureExtent[4] && subvolumeTextureExtent[5]<=wholeTextureExtent[5]); // 2. Is this subvolume already on the GPU? // ie are the extent of the subvolume inside the loaded extent? // Find the texture (and mask). vtkstd::map::iterator it= this->ScalarsTextures->Map.find(this->GetTransformedInput()); vtkKWScalarField *texture; if(it==this->ScalarsTextures->Map.end()) { texture=0; } else { texture=(*it).second; } vtkKWMask *mask=0; if(this->MaskInput!=0) { vtkstd::map::iterator it2= this->MaskTextures->Map.find(this->MaskInput); if(it2==this->MaskTextures->Map.end()) { mask=0; } else { mask=(*it2).second; } } int loaded = texture!=0 && texture->IsLoaded() && this->GetTransformedInput()->GetMTime()<=texture->GetBuildTime() && (this->GetMaskInput() ? this->GetMaskInput()->GetMTime() <= texture->GetBuildTime() : true) && texture->GetLoadedCellFlag()==this->CellFlag; vtkIdType *loadedExtent; if(loaded) { loadedExtent=texture->GetLoadedExtent(); i=0; while(loaded && i<6) { loaded=loaded && loadedExtent[i]<=subvolumeTextureExtent[i]; ++i; loaded=loaded && loadedExtent[i]>=subvolumeTextureExtent[i]; ++i; } } if(loaded) { this->CurrentScalar=texture; vtkgl::ActiveTexture(vtkgl::TEXTURE0); this->CurrentScalar->Bind(); vtkgl::ActiveTexture(vtkgl::TEXTURE7); this->CurrentMask=mask; if(this->CurrentMask!=0) { this->CurrentMask->Bind(); } } if(!loaded) { // 3. Not loaded: try to load the whole dataset if(!this->LoadScalarField(this->GetTransformedInput(),this->MaskInput,wholeTextureExtent,volume)) { // 4. loading the whole dataset failed: try to load the subvolume if(!this->LoadScalarField(this->GetTransformedInput(),this->MaskInput, subvolumeTextureExtent, volume)) { // 5. loading the subvolume failed: stream the subvolume // 5.1 do zslabs first, if too large then cut with x or y with the // largest dimension. order of zlabs depends on sign of spacing[2] int streamTextureExtent[6]; i=0; while(i<6) { streamTextureExtent[i]=subvolumeTextureExtent[i]; ++i; } unsigned int internalFormat; unsigned int format; unsigned int type; int componentSize; this->GetTextureFormat(this->GetInput(),&internalFormat,&format,&type, &componentSize); // Enough memory? int originalTextureSize[3]; int textureSize[3]; i=0; while(i<3) { textureSize[i]=subvolumeTextureExtent[2*i+1]-subvolumeTextureExtent[2*i]+1; originalTextureSize[i]=textureSize[i]; ++i; } // Make sure loading did not fail because of theorical limits GLint width; glGetIntegerv(vtkgl::MAX_3D_TEXTURE_SIZE,&width); int clippedXY=0; int clippedZ=0; if(textureSize[0]>width) { textureSize[0]=width; clippedXY=1; } if(textureSize[1]>width) { textureSize[1]=width; clippedXY=1; } if(textureSize[2]>width) { textureSize[2]=width; clippedZ=1; } int minSize; if(this->CellFlag) { minSize=1; } else { minSize=2; } if(clippedXY) { // We cannot expect to first divide as z-slabs because it is already // clipped in another dimension. From now, just divide in the largest // dimension. bool foundSize=false; while(!foundSize && textureSize[0]>=minSize && textureSize[1]>=minSize) { foundSize=this->TestLoadingScalar(internalFormat,format,type, textureSize,componentSize); if(!foundSize) { int maxDim=0; if(textureSize[1]>textureSize[0]) { maxDim=1; } if(textureSize[2]>textureSize[maxDim]) { maxDim=2; } textureSize[maxDim]>>=1; // /=2 } } } else { // we are in cropping mode, it will be slow anyway. the case we want // to optimize is stream the all scalar field. With that in mine, // it is better to first try to send z-slabs. If even a minimal // z-slab is too big, we have to divide by x or y dimensions. In // this case, it will be slow and we can choose to keep blocks as // square as possible by dividing by the largest dimension at each // iteration. if(!clippedZ) { // we start by subdividing only if we did not already clipped // the z dimension according to the theorical limits. textureSize[2]>>=1; // /=2 } bool foundSize=false; while(!foundSize && textureSize[2]>=minSize) { foundSize=this->TestLoadingScalar(internalFormat,format,type, textureSize,componentSize); if(!foundSize) { textureSize[2]>>=1; // /=2 } } if(!foundSize) { textureSize[2]=minSize; if(textureSize[0]>textureSize[1]) { textureSize[0]>>=1; // /=2 } else { textureSize[1]>>=1; // /=2 } while(!foundSize && textureSize[0]>=minSize && textureSize[1]>=minSize) { foundSize=this->TestLoadingScalar(internalFormat,format,type, textureSize,componentSize); if(!foundSize) { if(textureSize[0]>textureSize[1]) { textureSize[0]>>=1; // /=2 } else { textureSize[1]>>=1; // /=2 } } } } if(!foundSize) { vtkErrorMacro( <<"No memory left on the GPU even for a minimal block."); return 1; // abort } } // except for the last bound. // front to back ordering // Pass camera through inverse volume matrix // so that we are in the same coordinate system double camPos[4]; vtkCamera *cam = ren->GetActiveCamera(); cam->GetPosition(camPos); volume->GetMatrix( this->InvVolumeMatrix ); camPos[3] = 1.0; this->InvVolumeMatrix->Invert(); this->InvVolumeMatrix->MultiplyPoint( camPos, camPos ); if ( camPos[3] ) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } // 5.2 iterate of each stream of the subvolume and render it: // point scalar: on the first block, the first point is not shared // blockExtent is always expressed in point, not in texture // extent. size_t remainder[3]; i=0; while(i<3) { remainder[i]=static_cast( (originalTextureSize[i]-textureSize[i])%(textureSize[i]-1)); if(remainder[i]>0) { remainder[i]=1; } ++i; } size_t counts[3]; counts[0]=static_cast((originalTextureSize[0]-textureSize[0]) /(textureSize[0]-1)); counts[0]+=remainder[0]+1; counts[1]=static_cast((originalTextureSize[1]-textureSize[1]) /(textureSize[1]-1)); counts[1]+=remainder[1]+1; counts[2]=static_cast((originalTextureSize[2]-textureSize[2]) /(textureSize[2]-1)); counts[2]+=remainder[2]+1; size_t count=counts[0]*counts[1]*counts[2]; double blockExtent[6]; vtkStreamBlock *blocks=new vtkStreamBlock[count]; vtkRegionDistance2 *sortedBlocks=new vtkRegionDistance2[count]; // iterate over z,y,x size_t blockId=0; size_t zIndex=0; blockExtent[4]=realExtent[4]; blockExtent[5]=vtkMath::Floor(blockExtent[4])+textureSize[2]; if(!this->CellFlag) { blockExtent[5]--; } if(blockExtent[5]>realExtent[5]) { blockExtent[5]=realExtent[5]; } while(zIndexCellFlag) { blockExtent[3]--; } if(blockExtent[3]>realExtent[3]) { blockExtent[3]=realExtent[3]; } size_t yIndex=0; while(yIndexCellFlag) { blockExtent[1]--; } if(blockExtent[1]>realExtent[1]) { blockExtent[1]=realExtent[1]; } size_t xIndex=0; while(xIndexSlabsFromIndexToDataset(blockExtent,blockBounds); // compute the bounds and center double center[3]; i=0; while(i<3) { center[i]=(blockBounds[i*2]+blockBounds[i*2+1])*0.5; ++i; } // compute the distance squared to the center double distance2=(camPos[0]-center[0])*(camPos[0]-center[0])+ (camPos[1]-center[1])*(camPos[1]-center[1]) + (camPos[2]-center[2])*(camPos[2]-center[2]); i=0; while(i<6) { blocks[blockId].Bounds[i]=blockBounds[i]; blocks[blockId].Extent[i]=blockExtent[i]; ++i; } sortedBlocks[blockId].Id=blockId; sortedBlocks[blockId].Distance2=distance2; ++blockId; blockExtent[0]=blockExtent[1]; blockExtent[1]=blockExtent[0]+textureSize[0]; if(!this->CellFlag) { blockExtent[1]--; } if(blockExtent[1]>realExtent[1]) { blockExtent[1]=realExtent[1]; } ++xIndex; } // while x blockExtent[2]=blockExtent[3]; blockExtent[3]=blockExtent[2]+textureSize[1]; if(!this->CellFlag) { blockExtent[3]--; } if(blockExtent[3]>realExtent[3]) { blockExtent[3]=realExtent[3]; } ++yIndex; } // while y blockExtent[4]=blockExtent[5]; blockExtent[5]=blockExtent[4]+textureSize[2]; if(!this->CellFlag) { blockExtent[5]--; } if(blockExtent[5]>realExtent[5]) { blockExtent[5]=realExtent[5]; } ++zIndex; } // while z assert("check: valid_number_of_blocks" && blockId==count); qsort(sortedBlocks,static_cast(count), sizeof(vtkRegionDistance2), vtkRegionComparisonFunction); // loop over all blocks we need to render i=0; int abort=0; while(!abort && i < count) // 1) //count) { size_t k=sortedBlocks[i].Id; int blockTextureExtent[6]; int j; if(this->CellFlag) // 3D texture are celldata { // texture extents are expressed in cells in this case j=0; while(j<6) { blockTextureExtent[j]=vtkMath::Floor(blocks[k].Extent[j]); ++j; } } else { // texture extents are expressed in points in this case j=0; while(j<6) { blockTextureExtent[j]=vtkMath::Floor(blocks[k].Extent[j]); ++j; blockTextureExtent[j]=vtkMath::Floor(blocks[k].Extent[j]); if(blockTextureExtent[j]LoadScalarField(this->GetInput(),this->MaskInput, blockTextureExtent, volume)) { cout<<"Loading the streamed block FAILED!!!!!"<CurrentScalar->GetLoadedExtent(); float lowBounds[3]; float highBounds[3]; if(!this->CurrentScalar->GetLoadedCellFlag()) // points { j=0; while(j<3) { double delta= static_cast(loadedExtent[j*2+1]-loadedExtent[j*2]); lowBounds[j]=static_cast((blocks[k].Extent[j*2]-static_cast(loadedExtent[j*2]))/delta); highBounds[j]=static_cast((blocks[k].Extent[j*2+1]-static_cast(loadedExtent[j*2]))/delta); ++j; } } else // cells { j=0; while(j<3) { double delta= static_cast(loadedExtent[j*2+1]-loadedExtent[j*2]); lowBounds[j]=static_cast((blocks[k].Extent[j*2]-0.5-static_cast(loadedExtent[j*2]))/delta); highBounds[j]=static_cast((blocks[k].Extent[j*2+1]-0.5-static_cast(loadedExtent[j*2]))/delta); ++j; } } // bounds have to be normalized. There are used in the shader // as bounds to a value used to sample a texture. assert("check: positive_low_bounds0" && lowBounds[0]>=0.0); assert("check: positive_low_bounds1" && lowBounds[1]>=0.0); assert("check: positive_low_bounds2" && lowBounds[2]>=0.0); assert("check: increasing_bounds0" && lowBounds[0]<=highBounds[0]); assert("check: increasing_bounds1" && lowBounds[1]<=highBounds[1]); assert("check: increasing_bounds2" && lowBounds[2]<=highBounds[2]); assert("check: high_bounds0_less_than1" && highBounds[0]<=1.0); assert("check: high_bounds1_less_than1" && highBounds[1]<=1.0); assert("check: high_bounds2_less_than1" && highBounds[2]<=1.0); GLint lb; lb=vtkgl::GetUniformLocation(static_cast(this->ProgramShader), "lowBounds"); this->PrintError("get uniform low bounds"); if(lb!=-1) { vtkgl::Uniform3f(lb, lowBounds[0],lowBounds[1],lowBounds[2]); this->PrintError("set uniform low bounds"); } else { vtkErrorMacro(<<" lowBounds is not a uniform variable."); } GLint hb; hb=vtkgl::GetUniformLocation(static_cast(this->ProgramShader), "highBounds"); this->PrintError("get uniform high bounds"); if(hb!=-1) { vtkgl::Uniform3f(hb, highBounds[0],highBounds[1],highBounds[2]); this->PrintError("set uniform high bounds"); } else { vtkErrorMacro(<<" highBounds is not a uniform variable."); } this->PrintError("uniform low/high bounds block"); // other sub-volume rendering code this->LoadProjectionParameters(ren,volume); this->ClipBoundingBox(ren,blocks[k].Bounds,volume); abort=this->RenderClippedBoundingBox(1,i,count,ren->GetRenderWindow()); if (!abort) { this->CopyFBOToTexture(); } this->PrintError("render clipped block 1"); ++i; } delete[] blocks; delete[] sortedBlocks; return abort; } } } loadedExtent=this->CurrentScalar->GetLoadedExtent(); // low bounds and high bounds are in texture coordinates. float lowBounds[3]; float highBounds[3]; if(!this->CurrentScalar->GetLoadedCellFlag()) // points { i=0; while(i<3) { double delta= static_cast(loadedExtent[i*2+1]-loadedExtent[i*2]+1); lowBounds[i]=static_cast((realExtent[i*2]+0.5-static_cast(loadedExtent[i*2]))/delta); highBounds[i]=static_cast((realExtent[i*2+1]+0.5-static_cast(loadedExtent[i*2]))/delta); ++i; } } else // cells { i=0; while(i<3) { double delta= static_cast(loadedExtent[i*2+1]-loadedExtent[i*2]+1); // this->LoadedExtent[i*2]==0, texcoord starts at 0, if realExtent==0 // otherwise, texcoord start at 1/2N // this->LoadedExtent[i*2]==wholeTextureExtent[i*2+1], texcoord stops at 1, if realExtent==wholeTextureExtent[i*2+1]+1 // otherwise it stop at 1-1/2N // N is the number of texels in the loadedtexture not the number of // texels in the whole texture. lowBounds[i]=static_cast((realExtent[i*2]-static_cast(loadedExtent[i*2]))/delta); highBounds[i]=static_cast((realExtent[i*2+1]-static_cast(loadedExtent[i*2]))/delta); ++i; } } assert("check: positive_low_bounds0" && lowBounds[0]>=0.0); assert("check: positive_low_bounds1" && lowBounds[1]>=0.0); assert("check: positive_low_bounds2" && lowBounds[2]>=0.0); assert("check: increasing_bounds0" && lowBounds[0]<=highBounds[0]); assert("check: increasing_bounds1" && lowBounds[1]<=highBounds[1]); assert("check: increasing_bounds2" && lowBounds[2]<=highBounds[2]); assert("check: high_bounds0_less_than1" && highBounds[0]<=1.0); assert("check: high_bounds1_less_than1" && highBounds[1]<=1.0); assert("check: high_bounds2_less_than1" && highBounds[2]<=1.0); GLint lb; lb=vtkgl::GetUniformLocation(static_cast(this->ProgramShader), "lowBounds"); this->PrintError("get uniform low bounds"); if(lb!=-1) { vtkgl::Uniform3f(lb, lowBounds[0],lowBounds[1],lowBounds[2]); this->PrintError("set uniform low bounds"); } else { vtkErrorMacro(<<" lowBounds is not a uniform variable."); } GLint hb; hb=vtkgl::GetUniformLocation(static_cast(this->ProgramShader), "highBounds"); this->PrintError("get uniform high bounds"); if(hb!=-1) { vtkgl::Uniform3f(hb, highBounds[0],highBounds[1],highBounds[2]); this->PrintError("set uniform high bounds"); } else { vtkErrorMacro(<<" highBounds is not a uniform variable."); } this->PrintError("uniform low/high bounds"); // other sub-volume rendering code this->LoadProjectionParameters(ren,volume); this->ClipBoundingBox(ren,bounds,volume); int abort=this->RenderClippedBoundingBox(1,0,1,ren->GetRenderWindow()); if (!abort) { this->CopyFBOToTexture(); } this->PrintError("render clipped 1"); return abort; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::LoadProjectionParameters( vtkRenderer *ren, vtkVolume *vol) { vtkMatrix4x4 *worldToDataset=vol->GetMatrix(); vtkMatrix4x4 *datasetToWorld=this->TempMatrix[0]; vtkMatrix4x4::Invert(worldToDataset,datasetToWorld); double *bounds=this->CurrentScalar->GetLoadedBounds(); double dx=bounds[1]-bounds[0]; double dy=bounds[3]-bounds[2]; double dz=bounds[5]-bounds[4]; // worldToTexture matrix is needed // Compute change-of-coordinate matrix from world space to texture space. vtkMatrix4x4 *worldToTexture=this->TempMatrix[2]; vtkMatrix4x4 *datasetToTexture=this->TempMatrix[1]; // Set the matrix datasetToTexture->Zero(); datasetToTexture->SetElement(0,0,dx); datasetToTexture->SetElement(1,1,dy); datasetToTexture->SetElement(2,2,dz); datasetToTexture->SetElement(3,3,1.0); datasetToTexture->SetElement(0,3,bounds[0]); datasetToTexture->SetElement(1,3,bounds[2]); datasetToTexture->SetElement(2,3,bounds[4]); // worldToTexture=worldToDataSet*dataSetToTexture vtkMatrix4x4::Multiply4x4(worldToDataset,datasetToTexture,worldToTexture); // NEW int parallelProjection=ren->GetActiveCamera()->GetParallelProjection(); // cout << "actualSampleDistance=" << this->ActualSampleDistance << endl; if(parallelProjection) { // Unit vector of the direction of projection in world space. double dirWorld[4]; double dir[4]; ren->GetActiveCamera()->GetDirectionOfProjection(dirWorld); dirWorld[3]=0.0; // direction in dataset space. datasetToWorld->MultiplyPoint(dirWorld,dir); // incremental vector: // direction in texture space times sample distance in world space. dir[0]=dir[0]*this->ActualSampleDistance/dx; dir[1]=dir[1]*this->ActualSampleDistance/dy; dir[2]=dir[2]*this->ActualSampleDistance/dz; GLint rayDir; rayDir=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"parallelRayDirection"); if(rayDir!=-1) { vtkgl::Uniform3f(rayDir,static_cast(dir[0]), static_cast(dir[1]), static_cast(dir[2])); } else { vtkErrorMacro(<<"parallelRayDirection is not a uniform variable."); } //cout<<"rayDir="<GetActiveCamera()->GetPosition(cameraPosWorld); cameraPosWorld[3]=1.0; // we use homogeneous coordinates. datasetToWorld->MultiplyPoint(cameraPosWorld,cameraPosDataset); // From homogeneous to cartesian coordinates. if(cameraPosDataset[3]!=1.0) { double ratio=1/cameraPosDataset[3]; cameraPosDataset[0]*=ratio; cameraPosDataset[1]*=ratio; cameraPosDataset[2]*=ratio; } cameraPosTexture[0] = (cameraPosDataset[0]-bounds[0])/dx; cameraPosTexture[1] = (cameraPosDataset[1]-bounds[2])/dy; cameraPosTexture[2] = (cameraPosDataset[2]-bounds[4])/dz; // Only make sense for the vectorial part of the homogeneous matrix. // coefMatrix=transposeWorldToTexture*worldToTexture // we re-cycle the datasetToWorld pointer with a different name vtkMatrix4x4 *transposeWorldToTexture=this->TempMatrix[1]; // transposeWorldToTexture={^t}worldToTexture vtkMatrix4x4::Transpose(worldToTexture,transposeWorldToTexture); vtkMatrix4x4 *coefMatrix=this->TempMatrix[1]; vtkMatrix4x4::Multiply4x4(transposeWorldToTexture,worldToTexture, coefMatrix); GLint uCameraPosition; uCameraPosition=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"cameraPosition"); if(uCameraPosition!=-1) { vtkgl::Uniform3f(uCameraPosition, static_cast(cameraPosTexture[0]), static_cast(cameraPosTexture[1]), static_cast(cameraPosTexture[2])); } else { vtkErrorMacro(<<"cameraPosition is not a uniform variable."); } GLint uSampleDistance; uSampleDistance=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"sampleDistance"); if(uSampleDistance!=-1) { vtkgl::Uniform1f(uSampleDistance,this->ActualSampleDistance); } else { vtkErrorMacro(<<"sampleDistance is not a uniform variable."); } GLint uMatrix1; uMatrix1=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"matrix1"); if(uMatrix1!=-1) { vtkgl::Uniform3f(uMatrix1, static_cast(coefMatrix->GetElement(0,0)), static_cast(coefMatrix->GetElement(1,1)), static_cast(coefMatrix->GetElement(2,2))); } else { vtkErrorMacro(<<"matrix1 is not a uniform variable."); } GLint uMatrix2; uMatrix2=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"matrix2"); if(uMatrix2!=-1) { vtkgl::Uniform3f(uMatrix2, static_cast(2*coefMatrix->GetElement(0,1)), static_cast(2*coefMatrix->GetElement(1,2)), static_cast(2*coefMatrix->GetElement(0,2))); } else { vtkErrorMacro(<<"matrix2 is not a uniform variable."); } } this->PrintError("after uniforms for projection"); // Change-of-coordinate matrix from Eye space to texture space. vtkMatrix4x4 *eyeToTexture=this->TempMatrix[1]; vtkMatrix4x4 *eyeToWorld=ren->GetActiveCamera()->GetViewTransformMatrix(); vtkMatrix4x4::Multiply4x4(eyeToWorld,worldToTexture,eyeToTexture); GLfloat matrix[16];// used sometimes as 3x3, sometimes as 4x4. double *raw=eyeToTexture->Element[0]; int index; int column; int row; int shadeMethod=this->LastShade; if(shadeMethod==vtkMitkOpenGLGPUVolumeRayCastMapperShadeYes) { index=0; column=0; while(column<3) { row=0; while(row<3) { // cout << "index=" << index << " row*4+column=" << row*4+column << endl; matrix[index]=static_cast(raw[row*4+column]); ++index; ++row; } ++column; } GLint uEyeToTexture3; uEyeToTexture3=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"eyeToTexture3"); this->PrintError("after getUniform eyeToTexture3"); if(uEyeToTexture3!=-1) { vtkgl::UniformMatrix3fv(uEyeToTexture3,1,GL_FALSE,matrix); } else { vtkErrorMacro(<<"eyeToTexture3 is not a uniform variable."); } this->PrintError("after Uniform eyeToTexture3"); index=0; column=0; while(column<4) { row=0; while(row<4) { // cout << "index=" << index << " row*4+column=" << row*4+column << endl; matrix[index]=static_cast(raw[row*4+column]); ++index; ++row; } ++column; } GLint uEyeToTexture4; uEyeToTexture4=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"eyeToTexture4"); if(uEyeToTexture4!=-1) { vtkgl::UniformMatrix4fv(uEyeToTexture4,1,GL_FALSE,matrix); } else { vtkErrorMacro(<<"eyeToTexture4 is not a uniform variable."); } } eyeToTexture->Invert(); index=0; column=0; while(column<4) { row=0; while(row<4) { // cout << "index=" << index << " row*4+column=" << row*4+column << endl; matrix[index]=static_cast(raw[row*4+column]); ++index; ++row; } ++column; } this->PrintError("before GetUniformLocation TextureToEye"); GLint uTextureToEye; uTextureToEye=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"textureToEye"); this->PrintError("after GetUniformLocation TextureToEye"); if(uTextureToEye!=-1) { vtkgl::UniformMatrix4fv(uTextureToEye,1,GL_FALSE,matrix); } else { vtkErrorMacro(<<"textureToEye is not a uniform variable."); } this->PrintError("after UniformMatrxix TextureToEye"); if(shadeMethod==vtkMitkOpenGLGPUVolumeRayCastMapperShadeYes) { eyeToTexture->Transpose(); index=0; column=0; while(column<3) { row=0; while(row<3) { // cout << "index=" << index << " row*4+column=" << row*4+column << endl; matrix[index]=static_cast(raw[row*4+column]); ++index; ++row; } ++column; } GLint uTranposeTextureToEye; uTranposeTextureToEye=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"transposeTextureToEye"); if(uTranposeTextureToEye!=-1) { vtkgl::UniformMatrix3fv(uTranposeTextureToEye,1,GL_FALSE,matrix); } else { vtkErrorMacro(<<"transposeTextureToEye is not a uniform variable."); } float cellScale[3]; // 1/(2*Step) float cellStep[3]; // Step vtkIdType *loadedExtent=this->CurrentScalar->GetLoadedExtent(); cellScale[0]=static_cast(static_cast( loadedExtent[1]-loadedExtent[0])*0.5); cellScale[1]=static_cast(static_cast( loadedExtent[3]-loadedExtent[2])*0.5); cellScale[2]=static_cast(static_cast( loadedExtent[5]-loadedExtent[4])*0.5); cellStep[0]=static_cast(1.0/static_cast( loadedExtent[1]-loadedExtent[0])); cellStep[1]=static_cast(1.0/static_cast( loadedExtent[3]-loadedExtent[2])); cellStep[2]=static_cast(1.0/static_cast( loadedExtent[5]-loadedExtent[4])); GLint uCellScale; uCellScale=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"cellScale"); if(uCellScale!=-1) { vtkgl::Uniform3f(uCellScale,cellScale[0],cellScale[1],cellScale[2]); } else { vtkErrorMacro(<<"error: cellScale is not a uniform variable."); } GLint uCellStep; uCellStep=vtkgl::GetUniformLocation( static_cast(this->ProgramShader),"cellStep"); if(uCellStep!=-1) { vtkgl::Uniform3f(uCellStep,cellStep[0],cellStep[1],cellStep[2]); } else { vtkErrorMacro(<<"error: cellStep is not a uniform variable."); } } } //----------------------------------------------------------------------------- // Concatenate the header string, projection type code and method to the // final fragment code in this->FragmentCode. // \pre valid_raycastMethod: raycastMethod>= // vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP && // raycastMethod<=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIPFourDependent //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::BuildProgram(int parallelProjection, int raycastMethod, int shadeMethod, int componentMethod) { assert("pre: valid_raycastMethod" && raycastMethod>= vtkMitkOpenGLGPUVolumeRayCastMapperMethodMIP && raycastMethod<=vtkMitkOpenGLGPUVolumeRayCastMapperMethodCompositeMask); GLuint fs; // cout<<"projection="<ProgramShader, vtkgl::INFO_LOG_LENGTH,¶ms); if(params>0) { char *buffer=new char[params]; vtkgl::GetProgramInfoLog(this->ProgramShader,params,0,buffer); cout<<"validation log: "<GetEnabledString(glIsEnabled(GL_LIGHTING))<GetEnabledString(glIsEnabled(GL_LIGHTING))<(value); cout<<"active texture is "<<(activeTexture-vtkgl::TEXTURE0)<(value); cout<<"light\t| status\t| ambient\t| diffuse\t| specular\t| position\t| spot direction\t| spot exponent\t| spot cutoff\t| k0\t| k1\t| k2"<=0 // \post valid_result: result>=x //----------------------------------------------------------------------------- int vtkMitkOpenGLGPUVolumeRayCastMapper::PowerOfTwoGreaterOrEqual(int x) { assert("pre: positive_x" && x>=0); int result=1; while(result=x); return result; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::UpdateNoiseTexture() { if(this->NoiseTextureId==0) { GLuint noiseTextureObject; glGenTextures(1,&noiseTextureObject); this->NoiseTextureId=static_cast(noiseTextureObject); vtkgl::ActiveTexture(vtkgl::TEXTURE6); glBindTexture(GL_TEXTURE_2D,noiseTextureObject); GLsizei size=128; // 1024; // Power of two value GLint maxSize; const float factor=0.1f; // const float factor=1.0f; const float amplitude=0.5f*factor; // something positive. // amplitude=0.5. noise between -0.5 +0.5. add some +0.5 shift. glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize); if(size>maxSize) { size=maxSize; } if(this->NoiseTexture!=0 && this->NoiseTextureSize!=size) { delete[] this->NoiseTexture; this->NoiseTexture=0; } if(this->NoiseTexture==0) { this->NoiseTexture=new float[size*size]; this->NoiseTextureSize=size; vtkPerlinNoise *noiseGenerator=vtkPerlinNoise::New(); noiseGenerator->SetFrequency(size,1.0,1.0); noiseGenerator->SetPhase(0.0,0.0,0.0); noiseGenerator->SetAmplitude(amplitude); int j=0; while(jNoiseTexture[j*size+i]=0.0; //amplitude+static_cast(noiseGenerator->EvaluateFunction(i,j,0.0)); ++i; } ++j; } noiseGenerator->Delete(); } glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,size,size,0,GL_RED,GL_FLOAT, this->NoiseTexture); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); GLfloat borderColor[4]={0.0,0.0,0.0,0.0}; glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR,borderColor); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); vtkgl::ActiveTexture(vtkgl::TEXTURE0); } } // ---------------------------------------------------------------------------- // Description: // Return how much the dataset has to be reduced in each dimension to // fit on the GPU. If the value is 1.0, there is no need to reduce the // dataset. // \pre the calling thread has a current OpenGL context. // \pre mapper_supported: IsRenderSupported(renderer->GetRenderWindow(),0) // The computation is based on hardware limits (3D texture indexable size) // and MaxMemoryInBytes. // \post valid_i_ratio: ratio[0]>0 && ratio[0]<=1.0 // \post valid_j_ratio: ratio[1]>0 && ratio[1]<=1.0 // \post valid_k_ratio: ratio[2]>0 && ratio[2]<=1.0 void vtkMitkOpenGLGPUVolumeRayCastMapper::GetReductionRatio(double ratio[3]) { // Compute texture size int i; int wholeTextureExtent[6]; this->GetInput()->GetExtent(wholeTextureExtent); if(this->CellFlag) // if we deal with cell data { i=1; while(i<6) { wholeTextureExtent[i]--; i+=2; } } // Indexable hardware limits GLint maxSize; glGetIntegerv(vtkgl::MAX_3D_TEXTURE_SIZE,&maxSize); vtkIdType rTextureSize[3]; double dMaxSize=static_cast(maxSize); i=0; while(i<3) { double textureSize=wholeTextureExtent[2*i+1]-wholeTextureExtent[2*i]+1; if(textureSize>maxSize) { ratio[i]=dMaxSize/textureSize; } else { ratio[i]=1.0; // no reduction } rTextureSize[i]=static_cast(floor(textureSize*ratio[i])); ++i; } // Data memory limits. vtkDataArray *scalars=this->GetScalars(this->GetInput(),this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag); int scalarType=scalars->GetDataType(); vtkIdType size=rTextureSize[0]*rTextureSize[1]*rTextureSize[2] *vtkAbstractArray::GetDataTypeSize(scalarType) *scalars->GetNumberOfComponents(); if(size>static_cast(this->MaxMemoryInBytes) *static_cast(this->MaxMemoryFraction)) { double r=static_cast(this->MaxMemoryInBytes) *static_cast(this->MaxMemoryFraction)/static_cast(size); double r3=pow(r,1.0/3.0); // try the keep reduction ratio uniform to avoid artifacts. bool reduced[3]; i=0; int count=0; while(i<3) { vtkIdType newSize=static_cast( floor(static_cast(rTextureSize[i])*r3)); reduced[i]=newSize>=1; if(reduced[i]) { ++count; } ++i; } if(count<3) // some axis cannot be reduced { double r2=sqrt(r); count=0; i=0; while(i<3) { if(reduced[i]) { vtkIdType newSize=static_cast( floor(static_cast(rTextureSize[i])*r2)); reduced[i]=newSize>=1; if(reduced[i]) { ++count; } } ++i; } if(count<2) // we can only reduce one axis { i=0; while(i<3) { if(reduced[i]) { ratio[i]*=r; } ++i; } } else // we can reduce two axes { i=0; while(i<3) { if(reduced[i]) { ratio[i]*=r2; } ++i; } } } else // we can reduce all three axes { i=0; while(i<3) { ratio[i]*=r3; ++i; } } } assert("post: valid_i_ratio" && ratio[0]>0 && ratio[0]<=1.0); assert("post: valid_j_ratio" && ratio[1]>0 && ratio[1]<=1.0); assert("post: valid_k_ratio" && ratio[2]>0 && ratio[2]<=1.0); } //----------------------------------------------------------------------------- // Standard print method //----------------------------------------------------------------------------- void vtkMitkOpenGLGPUVolumeRayCastMapper::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); } #endif diff --git a/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.h b/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.h index 9e597b1256..262ed748e3 100644 --- a/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.h +++ b/Modules/MitkExt/Rendering/vtkMitkOpenGLGPUVolumeRayCastMapper.h @@ -1,518 +1,526 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkMitkOpenGLGPUVolumeRayCastMapper.h,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ // .NAME vtkMitkOpenGLGPUVolumeRayCastMapper - OpenGL subclass that draws the // image to the screen // .SECTION Description // This is the concrete implementation of a ray cast image display helper - // a helper class responsible for drawing the image to the screen. // .SECTION see also // vtkGPUVolumeRayCastMapper #ifndef __vtkMitkOpenGLGPUVolumeRayCastMapper_h #define __vtkMitkOpenGLGPUVolumeRayCastMapper_h #include "vtkMitkGPUVolumeRayCastMapper.h" #include "mitkCommon.h" #include "MitkExtExports.h" // Only with VTK 5.6 or above #if ((VTK_MAJOR_VERSION > 5) || ((VTK_MAJOR_VERSION==5) && (VTK_MINOR_VERSION>=6) )) class vtkVolume; class vtkRenderer; class vtkOpenGLExtensionManager; class vtkMatrix4x4; class vtkUnsupportedRequiredExtensionsStringStream; // Pimpl class vtkMapDataArrayTextureId; // Pimpl class vtkMapMaskTextureId; // Pimpl class vtkPolyData; class vtkClipConvexPolyData; class vtkClipPolyData; class vtkTessellatedBoxSource; class vtkOpacityTable; // internal class. class vtkRGBTable; // internal class. class vtkKWScalarField; // internal class. class vtkKWMask; // internal class. class vtkOpacityTables; // Pimpl class vtkDensifyPolyData; class vtkStdString; class MitkExt_EXPORT vtkMitkOpenGLGPUVolumeRayCastMapper : public vtkMitkGPUVolumeRayCastMapper { public: static vtkMitkOpenGLGPUVolumeRayCastMapper *New(); vtkTypeRevisionMacro(vtkMitkOpenGLGPUVolumeRayCastMapper,vtkMitkGPUVolumeRayCastMapper); virtual void PrintSelf(ostream& os, vtkIndent indent); // Description: // Based on hardware and properties, we may or may not be able to // render using 3D texture mapping. This indicates if 3D texture // mapping is supported by the hardware, and if the other extensions // necessary to support the specific properties are available. virtual int IsRenderSupported(vtkRenderWindow *window, vtkVolumeProperty *property); // Description: // Return a string matching the OpenGL errorCode. // \post result_exists: result!=0 static const char *OpenGLErrorMessage(unsigned int errorCode); // Description: // Display headerMessage on the standard output and the last OpenGL error // message if any. // \pre headerMessage_exists: headerMessage!=0 static void PrintError(const char *headerMessage); protected: vtkMitkOpenGLGPUVolumeRayCastMapper(); ~vtkMitkOpenGLGPUVolumeRayCastMapper(); // The render method called by the superclass virtual void GPURender(vtkRenderer *ren, vtkVolume *vol); // Methods called by the AMR Volume Mapper. virtual void PreRender(vtkRenderer *ren, vtkVolume *vol, double datasetBounds[6], double scalarRange[2], int numberOfScalarComponents, unsigned int numberOfLevels); // \pre input is up-to-date virtual void RenderBlock(vtkRenderer *ren, vtkVolume *vol, unsigned int level); virtual void PostRender(vtkRenderer *ren, int numberOfScalarComponents); // Description: // Return if the required OpenGL extension `extensionName' is supported. // If not, its name is added to the string of unsupported but required // extensions. // \pre extensions_exist: extensions!=0 // \pre extensionName_exists: extensionName!=0 int TestRequiredExtension(vtkOpenGLExtensionManager *extensions, const char *extensionName); // Description: // Attempt to load required and optional OpenGL extensions for the current // context window. Variable LoadExtensionsSucceeded is set if all required // extensions has been loaded. In addition, variable // Supports_GL_ARB_texture_float is set if this extension has been loaded. // \pre: window_exists: window!=0 void LoadExtensions(vtkRenderWindow *window); // Description: // Create GLSL OpenGL objects such fragment program Ids. void CreateGLSLObjects(); // Description: // Allows late binding textures to framebuffers, because ATI openGL requires // the texture to initialized before void BindFramebuffer(); // Description: // Create OpenGL objects such as textures, buffers and fragment program Ids. // It only registers Ids, there is no actual initialization of textures or // fragment program. // \pre extensions_loaded: this->LoadExtensionsSucceeded // \post done: this->OpenGLObjectsCreated==1 void CreateOpenGLObjects(); // Description: // Delete OpenGL objects. // \post done: this->OpenGLObjectsCreated==0 - void ReleaseGraphicsResources(vtkWindow *window); + // \deprecatedSince{2013_12} Use ReleaseGraphicsResources(mitk::BaseRenderer* renderer) instead + // + DEPRECATED(void ReleaseGraphicsResources(vtkWindow *window)); + + // Description: + // Delete OpenGL objects. + // \post done: this->OpenGLObjectsCreated==0 + // + void ReleaseGraphicsResources(mitk::BaseRenderer* renderer); // Description: // Allocate memory on the GPU for the framebuffers according to the size of // the window or reallocate if the size has changed. Return true if // allocation succeeded. // \pre ren_exists: ren!=0 // \pre opengl_objects_created: this->OpenGLObjectsCreated // \post right_size: LastSize[]=window size. int AllocateFrameBuffers(vtkRenderer *ren); // Description // Load the scalar field (one or four component scalar field), cell or point // based for a given subextent of the whole extent (can be the whole extent) // as a 3D texture on the GPU. // Extents are expressed in point if the cell flag is false or in cells of // the cell flag is true. // It returns true if it succeeded, false if there is not enough memory on // the GPU. // If succeeded, it updates the LoadedExtent, LoadedBounds, LoadedCellFlag // and LoadedTime. It also succeed if the scalar field is already loaded // (ie since last load, input has not changed and cell flag has not changed // and requested texture extents are enclosed in the loaded extent). // \pre input_exists: input!=0 // \pre valid_point_extent: (this->CellFlag || // (textureExtent[0]CellFlag || // (textureExtent[0]<=textureExtent[1] && // textureExtent[2]<=textureExtent[3] && // textureExtent[4]<=textureExtent[5]))) int LoadScalarField(vtkImageData *input, vtkImageData *maskInput, int textureExtent[6], vtkVolume *volume); // Description: // Allocate memory and load color table on the GPU or // reload it if the transfer function changed. // \pre vol_exists: vol!=0 // \pre valid_numberOfScalarComponents: numberOfScalarComponents==1 || numberOfScalarComponents==4 int UpdateColorTransferFunction(vtkVolume *vol, int numberOfScalarComponents); // Description: // Allocate memory and load opacity table on the GPU or // reload it if the transfer functions changed. // \pre vol_exists: vol!=0 // \pre valid_numberOfScalarComponents: numberOfScalarComponents==1 || numberOfScalarComponents==4 int UpdateOpacityTransferFunction(vtkVolume *vol, int numberOfScalarComponents, unsigned int level); // Description: // Prepare rendering in the offscreen framebuffer. // \pre ren_exists: ren!=0 // \pre vol_exists: vol!=0 void SetupRender(vtkRenderer *ren, vtkVolume *vol); // Description: // Clip the bounding box with all clipping planes // and the near and far plane void ClipBoundingBox(vtkRenderer *ren, double worldBounds[6], vtkVolume *vol); // Description: // Render the bounding box. The flag indicates whether // or not tcoords are rendered too. Return abort status (true==abort). // \pre valid_currentBlock: currentBlock>=0 && currentBlock=0 // \post valid_result: result>=x int PowerOfTwoGreaterOrEqual(int x); // Description: // Display the status of the current framebuffer on the standard output. void CheckFrameBufferStatus(); // Description: // Create a string from a buffer id. The result has to be free by the caller. vtkStdString BufferToString(int buffer); // Description: // Display the buffers assigned for drawing and reading operations. void DisplayReadAndDrawBuffers(); // Description: // Display all the attachments of the current framebuffer object. void DisplayFrameBufferAttachments(); // Description: // Display a given attachment for the current framebuffer object. void DisplayFrameBufferAttachment(unsigned int uattachment); // Description: // Concatenate the header string, projection type code and method to the // final fragment code in this->FragmentCode. // \pre valid_raycastMethod: raycastMethod>= vtkMitkOpenGLGPUVolumeRayCastMapperMethodMaximumIntensityProjection && raycastMethod<=vtkMitkOpenGLGPUVolumeRayCastMapperMethodMinIPFourDependent void BuildProgram(int parallelProjection, int raycastMethod, int shadeMethod, int componentMethod); // Description: // Return the current OpenGL state about lighting. void GetLightingStatus(); // Description: // Check the compilation status of some fragment shader source. void CheckCompilation(unsigned int fragmentShader); // Description: // Check the linkage status of the fragment program. int CheckLinkage(unsigned int programShader); // Description: // Print all active uniform variables void PrintUniformVariables(unsigned int programShader); // Description: // Is the program shader valid in the current OpenGL state? // Debugging purpose only. void ValidateProgram(); // Description: // Update the reduction factor of the render viewport (this->ReductionFactor) // according to the time spent in seconds to render the previous frame // (this->TimeToDraw) and a time in seconds allocated to render the next // frame (allocatedTime). // \pre valid_current_reduction_range: this->ReductionFactor>0.0 && this->ReductionFactor<=1.0 // \pre positive_TimeToDraw: this->TimeToDraw>=0.0 // \pre positive_time: allocatedTime>0 // \post valid_new_reduction_range: this->ReductionFactor>0.0 && this->ReductionFactor<=1.0 void ComputeReductionFactor(double allocatedTime); // Description: // Render a subvolume. // \pre this->ProgramShader!=0 and is linked. void RenderWholeVolume(vtkRenderer *ren, vtkVolume *vol); // Description: // Render a subvolume. // \pre this->ProgramShader!=0 and is linked. void RenderRegions(vtkRenderer *ren, vtkVolume *vol); // Return abort status (true==abort) int RenderSubVolume(vtkRenderer *ren, double bounds[6], vtkVolume *vol); void LoadProjectionParameters(vtkRenderer *ren, vtkVolume *vol); // Description: // Compute and return the number of cropping regions void ComputeNumberOfCroppingRegions(); void GetTextureFormat(vtkImageData *input, unsigned int *internalFormat, unsigned int *format, unsigned int *type, int *componentSize); bool TestLoadingScalar(unsigned int internalFormat, unsigned int format, unsigned int type, int textureSize[3], int componentSize); void SlabsFromDatasetToIndex(double slabsDataSet[6], double slabsPoints[6]); void SlabsFromIndexToDataset(double slabsPoints[6], double slabsDataSet[6]); const char *GetEnabledString(unsigned char value); void GetOpenGLState(); void DebugDisplayBox(vtkPolyData *box); void UpdateNoiseTexture(); // Description: // Compute how each axis of a cell is projected on the viewport in pixel. // This requires to have information about the camera and about the volume. // It set the value of IgnoreSampleDistancePerPixel to true in case of // degenerated case (axes aligned with the view). double ComputeMinimalSampleDistancePerPixel(vtkRenderer *renderer, vtkVolume *volume); // Description: // Return how much the dataset has to be reduced in each dimension to // fit on the GPU. If the value is 1.0, there is no need to reduce the // dataset. // \pre the calling thread has a current OpenGL context. // \pre mapper_supported: IsRenderSupported(renderer->GetRenderWindow(),0) // The computation is based on hardware limits (3D texture indexable size) // and MaxMemoryInBytes. // \post valid_i_ratio: ratio[0]>0 && ratio[0]<=1.0 // \post valid_j_ratio: ratio[1]>0 && ratio[1]<=1.0 // \post valid_k_ratio: ratio[2]>0 && ratio[2]<=1.0 virtual void GetReductionRatio(double ratio[3]); bool m_BindMax; int NumberOfCroppingRegions; // World coordinates of each corner of the dataset. double BoundingBox[8][3]; // Used during the clipping process. vtkPolyData *PolyDataBoundingBox; vtkPlaneCollection *Planes; vtkPlane *NearPlane; vtkClipConvexPolyData *Clip; vtkMatrix4x4 *InvVolumeMatrix; vtkDensifyPolyData *Densify; int OpenGLObjectsCreated; int NumberOfFrameBuffers; unsigned int FrameBufferObject; unsigned int DepthRenderBufferObject; // 3D scalar texture +1D color+1D opacity+2D grabbed depth buffer // +1 2D colorbuffer. unsigned int TextureObjects[5]; unsigned int FragmentMainShader; unsigned int FragmentProjectionShader; unsigned int FragmentTraceShader; unsigned int FragmentCroppingShader; unsigned int FragmentComponentShader; unsigned int FragmentShadeShader; unsigned int ProgramShader; // used in MIP Mode (2 needed for ping-pong technique) unsigned int MaxValueFrameBuffer; unsigned int MaxValueFrameBuffer2; int ReducedSize[2]; vtkPolyData *ClippedBoundingBox; int LastSize[2]; double ReductionFactor; // Supported extensions // List of unsupported required extensions. Pimpl. vtkUnsupportedRequiredExtensionsStringStream *UnsupportedRequiredExtensions; int LoadExtensionsSucceeded; int Supports_GL_ARB_texture_float; int SupportsPixelBufferObjects; vtkTimeStamp DataBufferTime; // Matrices used in internal computation. As a member variable, // only one memory allocation is performed. vtkMatrix4x4 *TempMatrix[3]; double TableRange[2]; // Final string to send to the GPU as the fragment program source code. // char *FragmentCode; // int FragmentCodeCapacity; int ErrorLine; int ErrorColumn; char *ErrorString; // Store the last projection an raycast method in order to not rebuild // the fragment code at every call. int LastParallelProjection; int LastRayCastMethod; int LastCroppingMode; int LastComponent; int LastShade; vtkImageData *SmallInput; vtkTimeStamp SmallInputBuildTime; // Description: // Build the fragment shader program that scale and bias a texture // for window/level purpose. void BuildScaleBiasProgram(); unsigned int ScaleBiasProgramShader; // GLuint int UFrameBufferTexture; // GLint int UScale; // GLint int UBias; // GLint #if 0 vtkIdType LoadedExtent[6]; double LoadedBounds[6]; vtkTimeStamp LoadedScalarTime; int LoadedCellFlag; // point data or cell data (or field data, not handled) ? #endif unsigned int SavedFrameBuffer; // some offscreen mode use a framebuffer too. vtkTessellatedBoxSource *BoxSource; float *NoiseTexture; int NoiseTextureSize; // size of one dimension. unsigned int NoiseTextureId; // GLuint bool IgnoreSampleDistancePerPixel; vtkMapDataArrayTextureId *ScalarsTextures; // need a list for AMR mode. vtkMapMaskTextureId *MaskTextures; // need a list for AMR mode. vtkRGBTable *RGBTable; vtkRGBTable *Mask1RGBTable; vtkRGBTable *Mask2RGBTable; vtkOpacityTables *OpacityTables; vtkKWScalarField *CurrentScalar; vtkKWMask *CurrentMask; float ActualSampleDistance; double LastProgressEventTime; // initial value is 0.0. Expressed in seconds. bool PreserveOrientation; private: vtkMitkOpenGLGPUVolumeRayCastMapper(const vtkMitkOpenGLGPUVolumeRayCastMapper&); // Not implemented. void operator=(const vtkMitkOpenGLGPUVolumeRayCastMapper&); // Not implemented. }; #endif #endif diff --git a/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp b/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp index 80611ac2f1..b73dc50dd0 100644 --- a/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp +++ b/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.cpp @@ -1,2425 +1,2458 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifdef _OPENMP #include #endif #include "vtkWindows.h" #include "vtkMitkOpenGLVolumeTextureMapper3D.h" #include "mitkCommon.h" #define GPU_INFO MITK_INFO("mapper.vr") #define GPU_WARN MITK_WARN("mapper.vr") #include "vtkImageData.h" #include "vtkMatrix4x4.h" #include "vtkDataArray.h" #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkPlaneCollection.h" #include "vtkPointData.h" #include "vtkRenderWindow.h" #include "vtkRenderer.h" #include "vtkTimerLog.h" #include "vtkVolumeProperty.h" #include "vtkTransform.h" #include "vtkLightCollection.h" #include "vtkLight.h" #include "vtkCamera.h" #include "vtkMath.h" #include "vtkOpenGLExtensionManager.h" #include "vtkgl.h" #include "vtkOpenGLRenderWindow.h" #define myGL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 #define myGL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 #define myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 const char *vtkMitkVolumeTextureMapper3D_FourDependentShadeFP = "!!ARBfp1.0\n" //# We need some temporary variables "TEMP index, normal, finalColor;\n" "TEMP temp,temp1, temp2, temp3,temp4; \n" "TEMP sampleColor;\n" "TEMP ndotl, ndoth, ndotv; \n" "TEMP lightInfo, lightResult;\n" //# We are going to use the first //# texture coordinate "ATTRIB tex0 = fragment.texcoord[0];\n" //# This is the lighting information "PARAM lightDirection = program.local[0];\n" "PARAM halfwayVector = program.local[1];\n" "PARAM coefficient = program.local[2];\n" "PARAM lightDiffColor = program.local[3]; \n" "PARAM lightSpecColor = program.local[4]; \n" "PARAM viewVector = program.local[5];\n" "PARAM constants = program.local[6];\n" //# This is our output color "OUTPUT out = result.color;\n" //# Look up the gradient direction //# in the third volume "TEX temp2, tex0, texture[0], 3D;\n" //# This normal is stored 0 to 1, change to -1 to 1 //# by multiplying by 2.0 then adding -1.0. "MAD normal, temp2, constants.x, constants.y;\n" "DP3 temp4, normal, normal;\n" "RSQ temp, temp4.x;\n" "MUL normal, normal, temp;\n" //"RCP temp4,temp.x;\n" //"MUL temp2.w,temp2.w,temp4.x;\n" //"MUL_SAT temp2.w,temp2.w,6.0;\n" "TEX sampleColor, tex0, texture[1], 3D;\n" //# Take the dot product of the light //# direction and the normal "DP3 ndotl, normal, lightDirection;\n" //# Take the dot product of the halfway //# vector and the normal "DP3 ndoth, normal, halfwayVector;\n" "DP3 ndotv, normal, viewVector;\n" //# flip if necessary for two sided lighting "MUL temp3, ndotl, constants.y; \n" "CMP ndotl, ndotv, ndotl, temp3;\n" "MUL temp3, ndoth, constants.y; \n" "CMP ndoth, ndotv, ndoth, temp3;\n" //# put the pieces together for a LIT operation "MOV lightInfo.x, ndotl.x; \n" "MOV lightInfo.y, ndoth.x; \n" "MOV lightInfo.w, coefficient.w; \n" //# compute the lighting "LIT lightResult, lightInfo;\n" //# COLOR FIX "MUL lightResult, lightResult, 4.0;\n" //# This is the ambient contribution "MUL finalColor, coefficient.x, sampleColor;\n" //# This is the diffuse contribution "MUL temp3, lightDiffColor, sampleColor;\n" "MUL temp3, temp3, lightResult.y;\n" "ADD finalColor, finalColor, temp3;\n" //# This is th specular contribution "MUL temp3, lightSpecColor, lightResult.z; \n" //# Add specular into result so far, and replace //# with the original alpha. "ADD out, finalColor, temp3;\n" "MOV out.w, temp2.w;\n" "END\n"; const char *vtkMitkVolumeTextureMapper3D_OneComponentShadeFP = "!!ARBfp1.0\n" //# This is the fragment program for one //# component data with shading //# We need some temporary variables "TEMP index, normal, finalColor;\n" "TEMP temp,temp1, temp2, temp3,temp4; \n" "TEMP sampleColor;\n" "TEMP ndotl, ndoth, ndotv; \n" "TEMP lightInfo, lightResult;\n" //# We are going to use the first //# texture coordinate "ATTRIB tex0 = fragment.texcoord[0];\n" //# This is the lighting information "PARAM lightDirection = program.local[0];\n" "PARAM halfwayVector = program.local[1];\n" "PARAM coefficient = program.local[2];\n" "PARAM lightDiffColor = program.local[3]; \n" "PARAM lightSpecColor = program.local[4]; \n" "PARAM viewVector = program.local[5];\n" "PARAM constants = program.local[6];\n" //# This is our output color "OUTPUT out = result.color;\n" //# Look up the gradient direction //# in the third volume "TEX temp2, tex0, texture[0], 3D;\n" // Gradient Compution //# Look up the scalar value / gradient //# magnitude in the first volume //"TEX temp1, tex0, texture[0], 3D;\n" /* "ADD temp3,tex0,{-0.005,0,0};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{ 0.005,0,0};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.x,temp2.y,temp1.y;\n" "ADD temp3,tex0,{0,-0.005,0};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{0, 0.005,0};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.y,temp2.y,temp1.y;\n" "ADD temp3,tex0,{0,0,-0.005};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{0,0, 0.005};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.z,temp2.y,temp1.y;\n" */ //"MOV normal,{1,1,1};\n" "MOV index.x,temp2.a;\n" //# This normal is stored 0 to 1, change to -1 to 1 //# by multiplying by 2.0 then adding -1.0. "MAD normal, temp2, constants.x, constants.y;\n" //# Swizzle this to use (a,r) as texture //# coordinates //"SWZ index, temp1, a, r, 1, 1;\n" //# Use this coordinate to look up a //# final color in the third texture //# (this is a 2D texture) "DP3 temp4, normal, normal;\n" "RSQ temp, temp4.x;\n" "RCP temp4,temp.x;\n" "MUL normal, normal, temp;\n" "MOV index.y, temp4.x;\n" "TEX sampleColor, index, texture[1], 2D;\n" //"MUL sampleColor.w,sampleColor.w,temp4.x;\n" //# Take the dot product of the light //# direction and the normal "DP3 ndotl, normal, lightDirection;\n" //# Take the dot product of the halfway //# vector and the normal "DP3 ndoth, normal, halfwayVector;\n" "DP3 ndotv, normal, viewVector;\n" //# flip if necessary for two sided lighting "MUL temp3, ndotl, constants.y; \n" "CMP ndotl, ndotv, ndotl, temp3;\n" "MUL temp3, ndoth, constants.y; \n" "CMP ndoth, ndotv, ndoth, temp3;\n" //# put the pieces together for a LIT operation "MOV lightInfo.x, ndotl.x; \n" "MOV lightInfo.y, ndoth.x; \n" "MOV lightInfo.w, coefficient.w; \n" //# compute the lighting "LIT lightResult, lightInfo;\n" //# COLOR FIX "MUL lightResult, lightResult, 4.0;\n" //# This is the ambient contribution "MUL finalColor, coefficient.x, sampleColor;\n" //# This is the diffuse contribution "MUL temp3, lightDiffColor, sampleColor;\n" "MUL temp3, temp3, lightResult.y;\n" "ADD finalColor, finalColor, temp3;\n" //# This is th specular contribution "MUL temp3, lightSpecColor, lightResult.z; \n" //# Add specular into result so far, and replace //# with the original alpha. "ADD out, finalColor, temp3;\n" "MOV out.w, sampleColor.w;\n" "END\n"; //#ifndef VTK_IMPLEMENT_MESA_CXX vtkCxxRevisionMacro(vtkMitkOpenGLVolumeTextureMapper3D, "$Revision: 1.21 $"); vtkStandardNewMacro(vtkMitkOpenGLVolumeTextureMapper3D); //#endif vtkMitkOpenGLVolumeTextureMapper3D::vtkMitkOpenGLVolumeTextureMapper3D() { //GPU_INFO << "vtkMitkOpenGLVolumeTextureMapper3D"; this->Initialized = 0; this->Volume1Index = 0; this->Volume2Index = 0; this->Volume3Index = 0; this->ColorLookupIndex = 0; this->AlphaLookupIndex = 0; this->RenderWindow = NULL; this->SupportsCompressedTexture = false; prgOneComponentShade = 0; prgRGBAShade = 0; } vtkMitkOpenGLVolumeTextureMapper3D::~vtkMitkOpenGLVolumeTextureMapper3D() { //GPU_INFO << "~vtkMitkOpenGLVolumeTextureMapper3D"; if(prgOneComponentShade) vtkgl::DeleteProgramsARB( 1, &prgOneComponentShade ); if(prgRGBAShade) vtkgl::DeleteProgramsARB( 1, &prgRGBAShade ); } // Release the graphics resources used by this texture. void vtkMitkOpenGLVolumeTextureMapper3D::ReleaseGraphicsResources(vtkWindow *renWin) { //GPU_INFO << "ReleaseGraphicsResources"; if (( this->Volume1Index || this->Volume2Index || this->Volume3Index || this->ColorLookupIndex) && renWin) { static_cast(renWin)->MakeCurrent(); #ifdef GL_VERSION_1_1 // free any textures this->DeleteTextureIndex( &this->Volume1Index ); this->DeleteTextureIndex( &this->Volume2Index ); this->DeleteTextureIndex( &this->Volume3Index ); this->DeleteTextureIndex( &this->ColorLookupIndex ); this->DeleteTextureIndex( &this->AlphaLookupIndex ); #endif } this->Volume1Index = 0; this->Volume2Index = 0; this->Volume3Index = 0; this->ColorLookupIndex = 0; this->RenderWindow = NULL; this->SupportsCompressedTexture=false; this->SupportsNonPowerOfTwoTextures=false; this->Modified(); } + +// Release the graphics resources used by this texture. +void vtkMitkOpenGLVolumeTextureMapper3D::ReleaseGraphicsResources(mitk::BaseRenderer* renderer) +{ + //GPU_INFO << "ReleaseGraphicsResources"; + + vtkWindow * renWin = renderer->GetVtkRenderer()->GetRenderWindow(); + + if (( this->Volume1Index || this->Volume2Index || + this->Volume3Index || this->ColorLookupIndex) && renWin) + { + static_cast(renWin)->MakeCurrent(); +#ifdef GL_VERSION_1_1 + // free any textures + this->DeleteTextureIndex( &this->Volume1Index ); + this->DeleteTextureIndex( &this->Volume2Index ); + this->DeleteTextureIndex( &this->Volume3Index ); + this->DeleteTextureIndex( &this->ColorLookupIndex ); + this->DeleteTextureIndex( &this->AlphaLookupIndex ); +#endif + } + this->Volume1Index = 0; + this->Volume2Index = 0; + this->Volume3Index = 0; + this->ColorLookupIndex = 0; + this->RenderWindow = NULL; + this->SupportsCompressedTexture=false; + this->SupportsNonPowerOfTwoTextures=false; + + this->Modified(); +} + + void vtkMitkOpenGLVolumeTextureMapper3D::Render(vtkRenderer *ren, vtkVolume *vol) { //GPU_INFO << "Render"; ren->GetRenderWindow()->MakeCurrent(); if ( !this->Initialized ) { //this->Initialize(); this->Initialize(ren); } if ( !this->RenderPossible ) { vtkErrorMacro( "required extensions not supported" ); return; } vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); vtkPlaneCollection *clipPlanes; vtkPlane *plane; int numClipPlanes = 0; double planeEquation[4]; // build transformation vol->GetMatrix(matrix); matrix->Transpose(); glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_TEXTURE_BIT); int i; // Use the OpenGL clip planes clipPlanes = this->ClippingPlanes; if ( clipPlanes ) { numClipPlanes = clipPlanes->GetNumberOfItems(); if (numClipPlanes > 6) { vtkErrorMacro(<< "OpenGL guarantees only 6 additional clipping planes"); } for (i = 0; i < numClipPlanes; i++) { glEnable(static_cast(GL_CLIP_PLANE0+i)); plane = static_cast(clipPlanes->GetItemAsObject(i)); planeEquation[0] = plane->GetNormal()[0]; planeEquation[1] = plane->GetNormal()[1]; planeEquation[2] = plane->GetNormal()[2]; planeEquation[3] = -(planeEquation[0]*plane->GetOrigin()[0]+ planeEquation[1]*plane->GetOrigin()[1]+ planeEquation[2]*plane->GetOrigin()[2]); glClipPlane(static_cast(GL_CLIP_PLANE0+i),planeEquation); } } // insert model transformation glMatrixMode( GL_MODELVIEW ); glPushMatrix(); glMultMatrixd(matrix->Element[0]); glColor4f( 1.0, 1.0, 1.0, 1.0 ); // Turn lighting off - the polygon textures already have illumination glDisable( GL_LIGHTING ); vtkGraphicErrorMacro(ren->GetRenderWindow(),"Before actual render method"); this->RenderFP(ren,vol); // pop transformation matrix glMatrixMode( GL_MODELVIEW ); glPopMatrix(); matrix->Delete(); glPopAttrib(); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderFP(vtkRenderer *ren, vtkVolume *vol) { //GPU_INFO << "RenderFP"; /* glAlphaFunc (GL_GREATER, static_cast(1.0/255.0)); glEnable (GL_ALPHA_TEST); */ glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); int components = this->GetInput()->GetNumberOfScalarComponents(); switch ( components ) { case 1: this->RenderOneIndependentShadeFP(ren,vol); break; case 4: this->RenderRGBAShadeFP(ren,vol); break; } vtkgl::ActiveTexture( vtkgl::TEXTURE2); glDisable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE1); glDisable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE0); glDisable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); glDisable( GL_BLEND ); } void vtkMitkOpenGLVolumeTextureMapper3D::DeleteTextureIndex( GLuint *index ) { //GPU_INFO << "DeleteTextureIndex"; if (glIsTexture(*index)) { GLuint tempIndex; tempIndex = *index; glDeleteTextures(1, &tempIndex); *index = 0; } } void vtkMitkOpenGLVolumeTextureMapper3D::CreateTextureIndex( GLuint *index ) { //GPU_INFO << "CreateTextureIndex"; GLuint tempIndex=0; glGenTextures(1, &tempIndex); *index = static_cast(tempIndex); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderPolygons( vtkRenderer *ren, vtkVolume *vol, int stages[4] ) { //GPU_INFO << "RenderPolygons"; vtkRenderWindow *renWin = ren->GetRenderWindow(); if ( renWin->CheckAbortStatus() ) { return; } double bounds[27][6]; float distance2[27]; int numIterations; int i, j, k; // No cropping case - render the whole thing if ( !this->Cropping ) { // Use the input data bounds - we'll take care of the volume's // matrix during rendering this->GetInput()->GetBounds(bounds[0]); numIterations = 1; } // Simple cropping case - render the subvolume else if ( this->CroppingRegionFlags == 0x2000 ) { this->GetCroppingRegionPlanes(bounds[0]); numIterations = 1; } // Complex cropping case - render each region in back-to-front order else { // Get the camera position double camPos[4]; ren->GetActiveCamera()->GetPosition(camPos); double volBounds[6]; this->GetInput()->GetBounds(volBounds); // Pass camera through inverse volume matrix // so that we are in the same coordinate system vtkMatrix4x4 *volMatrix = vtkMatrix4x4::New(); vol->GetMatrix( volMatrix ); camPos[3] = 1.0; volMatrix->Invert(); volMatrix->MultiplyPoint( camPos, camPos ); volMatrix->Delete(); if ( camPos[3] ) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } // These are the region limits for x (first four), y (next four) and // z (last four). The first region limit is the lower bound for // that axis, the next two are the region planes along that axis, and // the final one in the upper bound for that axis. float limit[12]; for ( i = 0; i < 3; i++ ) { limit[i*4 ] = volBounds[i*2]; limit[i*4+1] = this->CroppingRegionPlanes[i*2]; limit[i*4+2] = this->CroppingRegionPlanes[i*2+1]; limit[i*4+3] = volBounds[i*2+1]; } // For each of the 27 possible regions, find out if it is enabled, // and if so, compute the bounds and the distance from the camera // to the center of the region. int numRegions = 0; int region; for ( region = 0; region < 27; region++ ) { int regionFlag = 1<CroppingRegionFlags & regionFlag ) { // what is the coordinate in the 3x3x3 grid int loc[3]; loc[0] = region%3; loc[1] = (region/3)%3; loc[2] = (region/9)%3; // compute the bounds and center float center[3]; for ( i = 0; i < 3; i++ ) { bounds[numRegions][i*2 ] = limit[4*i+loc[i]]; bounds[numRegions][i*2+1] = limit[4*i+loc[i]+1]; center[i] = (bounds[numRegions][i*2 ] + bounds[numRegions][i*2+1])/2.0; } // compute the distance squared to the center distance2[numRegions] = (camPos[0]-center[0])*(camPos[0]-center[0]) + (camPos[1]-center[1])*(camPos[1]-center[1]) + (camPos[2]-center[2])*(camPos[2]-center[2]); // we've added one region numRegions++; } } // Do a quick bubble sort on distance for ( i = 1; i < numRegions; i++ ) { for ( j = i; j > 0 && distance2[j] > distance2[j-1]; j-- ) { float tmpBounds[6]; float tmpDistance2; for ( k = 0; k < 6; k++ ) { tmpBounds[k] = bounds[j][k]; } tmpDistance2 = distance2[j]; for ( k = 0; k < 6; k++ ) { bounds[j][k] = bounds[j-1][k]; } distance2[j] = distance2[j-1]; for ( k = 0; k < 6; k++ ) { bounds[j-1][k] = tmpBounds[k]; } distance2[j-1] = tmpDistance2; } } numIterations = numRegions; } // loop over all regions we need to render for ( int loop = 0; loop < numIterations; loop++ ) { // Compute the set of polygons for this region // according to the bounds this->ComputePolygons( ren, vol, bounds[loop] ); // Loop over the polygons for ( i = 0; i < this->NumberOfPolygons; i++ ) { if ( renWin->CheckAbortStatus() ) { return; } float *ptr = this->PolygonBuffer + 36*i; glBegin( GL_TRIANGLE_FAN ); for ( j = 0; j < 6; j++ ) { if ( ptr[0] < 0.0 ) { break; } for ( k = 0; k < 4; k++ ) { if ( stages[k] ) { vtkgl::MultiTexCoord3fv( vtkgl::TEXTURE0 + k, ptr ); } } glVertex3fv( ptr+3 ); ptr += 6; } glEnd(); } } } // This method moves the scalars from the input volume into volume1 (and // possibly volume2) which are the 3D texture maps used for rendering. // // In the case where our volume is a power of two, the copy is done // directly. If we need to resample, then trilinear interpolation is used. // // A shift/scale is applied to the input scalar value to produce an 8 bit // value for the texture volume. // // When the input data is one component, the scalar value is placed in the // second component of the two component volume1. The first component is // filled in later with the gradient magnitude. // // When the input data is two component non-independent, the first component // of the input data is placed in the first component of volume1, and the // second component of the input data is placed in the third component of // volume1. Volume1 has three components - the second is filled in later with // the gradient magnitude. // // When the input data is four component non-independent, the first three // components of the input data are placed in volume1 (which has three // components), and the fourth component is placed in the second component // of volume2. The first component of volume2 is later filled in with the // gradient magnitude. template class ScalarGradientCompute { T *dataPtr; unsigned char *tmpPtr; unsigned char *tmpPtr2; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; int fullX; int fullY; int fullZ; int fullXY; int currentChunkStart; int currentChunkEnd; int offZ; float offset; float scale; public: ScalarGradientCompute( T *_dataPtr,unsigned char *_tmpPtr,unsigned char *_tmpPtr2,int _sizeX,int _sizeY,int _sizeZ,int _fullX,int _fullY,int _fullZ,float _offset,float _scale) { dataPtr=_dataPtr; tmpPtr=_tmpPtr; tmpPtr2=_tmpPtr2; sizeX=_sizeX; sizeY=_sizeY; sizeZ=_sizeZ; fullX=_fullX; fullY=_fullY; fullZ=_fullZ; offset=_offset; scale=_scale; sizeXY=sizeX*sizeY; sizeXm1=sizeX-1; sizeYm1=sizeY-1; sizeZm1=sizeZ-1; fullXY=fullX*fullY; } inline float sample(int x,int y,int z) { return float(dataPtr[ x + y * sizeX + z * sizeXY ]); } inline void fill(int x,int y,int z) { int doff = x + y * fullX + (z-offZ) * fullXY; tmpPtr[doff*4+0]= 0; tmpPtr[doff*4+1]= 0; tmpPtr[doff*4+2]= 0; tmpPtr[doff*4+3]= 0; /* tmpPtr2[doff*3+0]= 0; tmpPtr2[doff*3+1]= 0; tmpPtr2[doff*3+2]= 0; */ } inline int clamp(int x) { if(x<0) x=0; else if(x>255) x=255; return x; } inline void write(int x,int y,int z,float grayValue,float gx,float gy,float gz) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ // Compute the gradient magnitude int iGrayValue = static_cast( (grayValue + offset) * scale + 0.5f ); gx *= scale; gy *= scale; gz *= scale; float t = sqrtf( gx*gx + gy*gy + gz*gz ); if ( t > 0.01f ) { if( t < 2.0f ) { float fac = 2.0f/t; gx *= fac; gy *= fac; gz *= fac; } else if( t > 255.0f) { float fac = 255.0f/t; gx *= fac; gy *= fac; gz *= fac; } } else { gx=gy=gz=0.0f; } int nx = static_cast(0.5f*gx+127.5f); int ny = static_cast(0.5f*gy+127.5f); int nz = static_cast(0.5f*gz+127.5f); int doff = x + y * fullX + (z-offZ) * fullXY; //tmpPtr[doff*2+0]= 0; tmpPtr[doff*4+0]= clamp(nx); tmpPtr[doff*4+1]= clamp(ny); tmpPtr[doff*4+2]= clamp(nz); tmpPtr[doff*4+3]= clamp(iGrayValue); /* if( z == fullZ/2 ) if( y == fullY/2 ) MITK_INFO << x << " " << y << " " << z << " : " << iGrayValue << " : " << iGradient; */ } inline void compute(int x,int y,int z) { float grayValue = sample(x,y,z); float gx,gy,gz; gx = sample(x+1,y,z) - sample(x-1,y,z); gy = sample(x,y+1,z) - sample(x,y-1,z); gz = sample(x,y,z+1) - sample(x,y,z-1); write( x, y, z, grayValue, gx, gy, gz ); } inline void computeClamp(int x,int y,int z) { float grayValue = sample(x,y,z); float gx,gy,gz; if(x==0) gx = 2.0f * ( sample(x+1,y,z) - grayValue ); else if(x==sizeXm1) gx = 2.0f * ( grayValue - sample(x-1,y,z) ); else gx = sample(x+1,y,z) - sample(x-1,y,z); if(y==0) gy = 2.0f * ( sample(x,y+1,z) - grayValue ); else if(y==sizeYm1) gy = 2.0f * ( grayValue - sample(x,y-1,z) ); else gy = sample(x,y+1,z) - sample(x,y-1,z); if(z==0) gz = 2.0f * ( sample(x,y,z+1) - grayValue ); else if(z==sizeZm1) gz = 2.0f * ( grayValue - sample(x,y,z-1) ); else gz = sample(x,y,z+1) - sample(x,y,z-1); write( x, y, z, grayValue, gx, gy, gz ); } inline void compute1D(int y,int z) { int x; x=0; computeClamp(x,y,z); x++; while(x=sizeZ) fill2D(z); else compute2D(z); } } }; template void vtkVolumeTextureMapper3DComputeScalars( T *dataPtr, vtkMitkVolumeTextureMapper3D *me, float offset, float scale, GLuint volume1, GLuint /*volume2*/) { T *inPtr; // unsigned char *outPtr, *outPtr2; // int i, j, k; // int idx; int inputDimensions[3]; double inputSpacing[3]; vtkImageData *input = me->GetInput(); input->GetDimensions( inputDimensions ); input->GetSpacing( inputSpacing ); int outputDimensions[3]; float outputSpacing[3]; me->GetVolumeDimensions( outputDimensions ); me->GetVolumeSpacing( outputSpacing ); // int components = input->GetNumberOfScalarComponents(); // double wx, wy, wz; // double fx, fy, fz; // int x, y, z; double sampleRate[3]; sampleRate[0] = outputSpacing[0] / static_cast(inputSpacing[0]); sampleRate[1] = outputSpacing[1] / static_cast(inputSpacing[1]); sampleRate[2] = outputSpacing[2] / static_cast(inputSpacing[2]); int fullX = outputDimensions[0]; int fullY = outputDimensions[1]; int fullZ = outputDimensions[2]; int sizeX = inputDimensions[0]; int sizeY = inputDimensions[1]; int sizeZ = inputDimensions[2]; int chunkSize = 64; if(fullZ < chunkSize) chunkSize=fullZ; int numChunks = ( fullZ + (chunkSize-1) ) / chunkSize; inPtr = dataPtr; unsigned char *tmpPtr = new unsigned char[fullX*fullY*chunkSize*4]; unsigned char *tmpPtr2 = 0;//new unsigned char[fullX*fullY*chunkSize*3]; // For each Chunk { ScalarGradientCompute sgc(dataPtr,tmpPtr,tmpPtr2,sizeX,sizeY,sizeZ,fullX,fullY,fullZ,offset,scale); int currentChunk = 0; while(currentChunk < numChunks) { // MITK_INFO << "processing chunk " << currentChunk; int currentChunkStart = currentChunk * chunkSize; int currentChunkEnd = currentChunkStart + chunkSize - 1 ; if( currentChunkEnd > (fullZ-1) ) currentChunkEnd = (fullZ-1); int currentChunkSize = currentChunkEnd - currentChunkStart + 1; sgc.fillSlices( currentChunkStart , currentChunkEnd ); glBindTexture(vtkgl::TEXTURE_3D, volume1); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D,0,0,0,currentChunkStart,fullX,fullY,currentChunkSize,GL_RGBA,GL_UNSIGNED_BYTE,tmpPtr); /* glBindTexture(vtkgl::TEXTURE_3D, volume2); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D,0,0,0,currentChunkStart,fullX,fullY,currentChunkSize,GL_RGB,GL_UNSIGNED_BYTE,tmpPtr2); */ currentChunk ++; } } delete tmpPtr; // delete tmpPtr2; } class RGBACompute { unsigned char *dataPtr; unsigned char *tmpPtr; unsigned char *tmpPtr2; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; int fullX; int fullY; int fullZ; int fullXY; int currentChunkStart; int currentChunkEnd; int offZ; public: RGBACompute( unsigned char *_dataPtr,unsigned char *_tmpPtr,unsigned char *_tmpPtr2,int _sizeX,int _sizeY,int _sizeZ,int _fullX,int _fullY,int _fullZ) { dataPtr=_dataPtr; tmpPtr=_tmpPtr; tmpPtr2=_tmpPtr2; sizeX=_sizeX; sizeY=_sizeY; sizeZ=_sizeZ; fullX=_fullX; fullY=_fullY; fullZ=_fullZ; sizeXY=sizeX*sizeY; sizeXm1=sizeX-1; sizeYm1=sizeY-1; sizeZm1=sizeZ-1; fullXY=fullX*fullY; } inline int sample(int x,int y,int z) { return dataPtr[ ( x + y * sizeX + z * sizeXY ) * 4 +3 ]; } inline void fill(int x,int y,int z) { int doff = x + y * fullX + (z-offZ) * fullXY; tmpPtr[doff*4+0]= 0; tmpPtr[doff*4+1]= 0; tmpPtr[doff*4+2]= 0; tmpPtr[doff*4+3]= 0; tmpPtr2[doff*3+0]= 0; tmpPtr2[doff*3+1]= 0; tmpPtr2[doff*3+2]= 0; } inline int clamp(int x) { if(x<0) x=0; else if(x>255) x=255; return x; } inline void write(int x,int y,int z,int iGrayValue,int gx,int gy,int gz) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ int nx = static_cast(0.5f*gx+127.5f); int ny = static_cast(0.5f*gy+127.5f); int nz = static_cast(0.5f*gz+127.5f); int doff = x + y * fullX + (z-offZ) * fullXY; //tmpPtr[doff*2+0]= 0; tmpPtr[doff*4+0]= clamp(nx); tmpPtr[doff*4+1]= clamp(ny); tmpPtr[doff*4+2]= clamp(nz); tmpPtr[doff*4+3]= clamp(iGrayValue); int soff = x + y * sizeX + z * sizeXY; tmpPtr2[doff*3+0]= dataPtr[soff*4+0]; tmpPtr2[doff*3+1]= dataPtr[soff*4+1]; tmpPtr2[doff*3+2]= dataPtr[soff*4+2]; /* if( z == fullZ/2 ) if( y == fullY/2 ) MITK_INFO << x << " " << y << " " << z << " : " << iGrayValue << " : " << iGradient; */ } inline void compute(int x,int y,int z) { int grayValue = sample(x,y,z); int gx,gy,gz; gx = sample(x+1,y,z) - sample(x-1,y,z); gy = sample(x,y+1,z) - sample(x,y-1,z); gz = sample(x,y,z+1) - sample(x,y,z-1); write( x, y, z, grayValue, gx, gy, gz ); } inline void computeClamp(int x,int y,int z) { int grayValue = sample(x,y,z); int gx,gy,gz; if(x==0) gx = 2 * ( sample(x+1,y,z) - grayValue ); else if(x==sizeXm1) gx = 2 * ( grayValue - sample(x-1,y,z) ); else gx = sample(x+1,y,z) - sample(x-1,y,z); if(y==0) gy = 2 * ( sample(x,y+1,z) - grayValue ); else if(y==sizeYm1) gy = 2 * ( grayValue - sample(x,y-1,z) ); else gy = sample(x,y+1,z) - sample(x,y-1,z); if(z==0) gz = 2 * ( sample(x,y,z+1) - grayValue ); else if(z==sizeZm1) gz = 2 * ( grayValue - sample(x,y,z-1) ); else gz = sample(x,y,z+1) - sample(x,y,z-1); write( x, y, z, grayValue, gx, gy, gz ); } inline void compute1D(int y,int z) { int x=0; computeClamp(x,y,z); x++; while(x=sizeZ) fill2D(z); else compute2D(z); } } }; void vtkVolumeTextureMapper3DComputeRGBA( unsigned char *dataPtr, vtkMitkVolumeTextureMapper3D *me, GLuint volume1, GLuint volume2) { unsigned char *inPtr; // unsigned char *outPtr, *outPtr2; // int i, j, k; // int idx; int inputDimensions[3]; double inputSpacing[3]; vtkImageData *input = me->GetInput(); input->GetDimensions( inputDimensions ); input->GetSpacing( inputSpacing ); int outputDimensions[3]; float outputSpacing[3]; me->GetVolumeDimensions( outputDimensions ); me->GetVolumeSpacing( outputSpacing ); int components = input->GetNumberOfScalarComponents(); MITK_INFO << "components are " << components; // double wx, wy, wz; // double fx, fy, fz; // int x, y, z; double sampleRate[3]; sampleRate[0] = outputSpacing[0] / static_cast(inputSpacing[0]); sampleRate[1] = outputSpacing[1] / static_cast(inputSpacing[1]); sampleRate[2] = outputSpacing[2] / static_cast(inputSpacing[2]); int fullX = outputDimensions[0]; int fullY = outputDimensions[1]; int fullZ = outputDimensions[2]; int sizeX = inputDimensions[0]; int sizeY = inputDimensions[1]; int sizeZ = inputDimensions[2]; int chunkSize = 64; if(fullZ < chunkSize) chunkSize=fullZ; int numChunks = ( fullZ + (chunkSize-1) ) / chunkSize; inPtr = dataPtr; unsigned char *tmpPtr = new unsigned char[fullX*fullY*chunkSize*4]; unsigned char *tmpPtr2 = new unsigned char[fullX*fullY*chunkSize*3]; // For each Chunk { RGBACompute sgc(dataPtr,tmpPtr,tmpPtr2,sizeX,sizeY,sizeZ,fullX,fullY,fullZ); int currentChunk = 0; while(currentChunk < numChunks) { // MITK_INFO << "processing chunk " << currentChunk; int currentChunkStart = currentChunk * chunkSize; int currentChunkEnd = currentChunkStart + chunkSize - 1 ; if( currentChunkEnd > (fullZ-1) ) currentChunkEnd = (fullZ-1); int currentChunkSize = currentChunkEnd - currentChunkStart + 1; sgc.fillSlices( currentChunkStart , currentChunkEnd ); glBindTexture(vtkgl::TEXTURE_3D, volume1); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D,0,0,0,currentChunkStart,fullX,fullY,currentChunkSize,GL_RGBA,GL_UNSIGNED_BYTE,tmpPtr); glBindTexture(vtkgl::TEXTURE_3D, volume2); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D,0,0,0,currentChunkStart,fullX,fullY,currentChunkSize,GL_RGB,GL_UNSIGNED_BYTE,tmpPtr2); currentChunk ++; } } delete tmpPtr; delete tmpPtr2; } //----------------------------------------------------------------------------- void vtkMitkOpenGLVolumeTextureMapper3D::ComputeVolumeDimensions() { // Get the image data vtkImageData *input = this->GetInput(); // How big does the Volume need to be? int dim[3]; input->GetDimensions(dim); int powerOfTwoDim[3]; if(this->SupportsNonPowerOfTwoTextures) { for ( int i = 0; i < 3; i++ ) powerOfTwoDim[i]=(dim[i]+1)&~1; // MITK_INFO << "using non-power-two even textures (" << (1.0-double(dim[0]*dim[1]*dim[2])/double(powerOfTwoDim[0]*powerOfTwoDim[1]*powerOfTwoDim[2])) * 100.0 << "% memory wasted)"; } else { for ( int i = 0; i < 3; i++ ) { powerOfTwoDim[i] = 4; while ( powerOfTwoDim[i] < dim[i] ) powerOfTwoDim[i] *= 2; } MITK_WARN << "using power-two textures (" << (1.0-double(dim[0]*dim[1]*dim[2])/double(powerOfTwoDim[0]*powerOfTwoDim[1]*powerOfTwoDim[2])) * 100.0 << "% memory wasted)"; } // Save the volume size this->VolumeDimensions[0] = powerOfTwoDim[0]; this->VolumeDimensions[1] = powerOfTwoDim[1]; this->VolumeDimensions[2] = powerOfTwoDim[2]; // What is the spacing? double spacing[3]; input->GetSpacing(spacing); // Compute the new spacing this->VolumeSpacing[0] = ( dim[0] -1.01)*spacing[0] / static_cast(this->VolumeDimensions[0]-1); this->VolumeSpacing[1] = ( dim[1] -1.01)*spacing[1] / static_cast(this->VolumeDimensions[1]-1); this->VolumeSpacing[2] = ((dim[2])-1.01)*spacing[2] / static_cast(this->VolumeDimensions[2]-1); } //----------------------------------------------------------------------------- bool vtkMitkOpenGLVolumeTextureMapper3D::UpdateVolumes(vtkVolume *vtkNotUsed(vol)) { // Get the image data vtkImageData *input = this->GetInput(); input->Update(); bool needUpdate = false; // Has the volume changed in some way? if ( this->SavedTextureInput != input || this->SavedTextureMTime.GetMTime() < input->GetMTime() ) needUpdate = true; // Do we have any volume on the gpu already? if(!this->Volume1Index) needUpdate = true; if(!needUpdate) return true; ComputeVolumeDimensions(); int components = input->GetNumberOfScalarComponents(); // Find the scalar range double scalarRange[2]; input->GetPointData()->GetScalars()->GetRange(scalarRange, components-1); // Is the difference between max and min less than 4096? If so, and if // the data is not of float or double type, use a simple offset mapping. // If the difference between max and min is 4096 or greater, or the data // is of type float or double, we must use an offset / scaling mapping. // In this case, the array size will be 4096 - we need to figure out the // offset and scale factor. float offset; float scale; int arraySizeNeeded; int scalarType = input->GetScalarType(); if ( scalarType == VTK_FLOAT || scalarType == VTK_DOUBLE || scalarRange[1] - scalarRange[0] > 255 ) { arraySizeNeeded = 256; offset = -scalarRange[0]; scale = 255.0 / (scalarRange[1] - scalarRange[0]); } else { arraySizeNeeded = static_cast(scalarRange[1] - scalarRange[0] + 1); offset = -scalarRange[0]; scale = 1.0; } this->ColorTableSize = arraySizeNeeded; this->ColorTableOffset = offset; this->ColorTableScale = scale; // Allocating volume on gpu { // Deleting old textures this->DeleteTextureIndex(&this->Volume1Index); this->DeleteTextureIndex(&this->Volume2Index); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume1Index); //this->CreateTextureIndex(&this->Volume2Index); int dim[3]; this->GetVolumeDimensions(dim); vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); MITK_INFO << "allocating volume on gpu"; GLint gradientScalarTextureFormat = GL_RGBA8; if(this->UseCompressedTexture && SupportsCompressedTexture) gradientScalarTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,gradientScalarTextureFormat,dim[0],dim[1],dim[2],0,GL_RGBA,GL_UNSIGNED_BYTE,0); this->Setup3DTextureParameters( true ); } // Transfer the input volume to the RGBA volume void *dataPtr = input->GetScalarPointer(); switch ( scalarType ) { vtkTemplateMacro( vtkVolumeTextureMapper3DComputeScalars( static_cast(dataPtr), this, offset, scale, this->Volume1Index, this->Volume2Index)); } this->SavedTextureInput = input; this->SavedTextureMTime.Modified(); return true; } //----------------------------------------------------------------------------- bool vtkMitkOpenGLVolumeTextureMapper3D::UpdateVolumesRGBA(vtkVolume *vtkNotUsed(vol)) { // Get the image data vtkImageData *input = this->GetInput(); input->Update(); bool needUpdate = false; // Has the volume changed in some way? if ( this->SavedTextureInput != input || this->SavedTextureMTime.GetMTime() < input->GetMTime() ) needUpdate = true; // Do we have any volume on the gpu already? if(!this->Volume1Index) needUpdate = true; if(!needUpdate) return true; MITK_INFO << "updating rgba volume"; ComputeVolumeDimensions(); // Allocating volume on gpu { // Deleting old textures this->DeleteTextureIndex(&this->Volume1Index); this->DeleteTextureIndex(&this->Volume2Index); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume1Index); this->CreateTextureIndex(&this->Volume2Index); int dim[3]; this->GetVolumeDimensions(dim); MITK_INFO << "allocating volume on gpu"; GLint gradientScalarTextureFormat = GL_RGBA8; GLint colorTextureFormat = GL_RGB8; if(this->UseCompressedTexture && SupportsCompressedTexture) { gradientScalarTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; colorTextureFormat = myGL_COMPRESSED_RGB_S3TC_DXT1_EXT; } vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,gradientScalarTextureFormat,dim[0],dim[1],dim[2],0,GL_RGBA,GL_UNSIGNED_BYTE,0); this->Setup3DTextureParameters( true ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,colorTextureFormat,dim[0],dim[1],dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,0); this->Setup3DTextureParameters( true ); } // Transfer the input volume to the RGBA volume unsigned char *dataPtr = (unsigned char*)input->GetScalarPointer(); vtkVolumeTextureMapper3DComputeRGBA( dataPtr, this, this->Volume1Index, this->Volume2Index); this->SavedTextureInput = input; this->SavedTextureMTime.Modified(); return true; } void vtkMitkOpenGLVolumeTextureMapper3D::Setup3DTextureParameters( bool linear ) { //GPU_INFO << "Setup3DTextureParameters"; if( linear ) { glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); } else { glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameterf( vtkgl::TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP ); } void vtkMitkOpenGLVolumeTextureMapper3D::SetupOneIndependentTextures( vtkRenderer *vtkNotUsed(ren), vtkVolume *vol ) { // Update the volume containing the 2 byte scalar / gradient magnitude this->UpdateVolumes( vol ); // Update the dependent 2D color table mapping scalar value and // gradient magnitude to RGBA if ( this->UpdateColorLookup( vol ) || !this->ColorLookupIndex ) { this->DeleteTextureIndex( &this->ColorLookupIndex ); this->DeleteTextureIndex( &this->AlphaLookupIndex ); this->CreateTextureIndex( &this->ColorLookupIndex ); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glBindTexture(GL_TEXTURE_2D, this->ColorLookupIndex); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); //MITK_INFO << "uploading transferfunction"; GLint colorLookupTextureFormat = GL_RGBA8; if(this->UseCompressedTexture && SupportsCompressedTexture) colorLookupTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; glTexImage2D( GL_TEXTURE_2D, 0,colorLookupTextureFormat, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->ColorLookup ); } } void vtkMitkOpenGLVolumeTextureMapper3D::SetupRGBATextures( vtkRenderer *vtkNotUsed(ren), vtkVolume *vol ) { MITK_INFO << "SetupFourDependentTextures"; this->UpdateVolumesRGBA(vol); /* vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); // Update the volume containing the 3 byte scalars / gradient magnitude if ( this->UpdateVolumes( vol ) || !this->Volume1Index || !this->Volume2Index || !this->Volume3Index ) { int dim[3]; this->GetVolumeDimensions(dim); vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume1Index); this->CreateTextureIndex(&this->Volume1Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalRGB,dim[0],dim[1], dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,this->Volume1); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume2Index); this->CreateTextureIndex(&this->Volume2Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalLA,dim[0],dim[1], dim[2],0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE, this->Volume2); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume3Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume3Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalRGB,dim[0],dim[1], dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,this->Volume3); } vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glBindTexture(vtkgl::TEXTURE_3D_EXT, this->Volume3Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glEnable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); // Update the dependent 2D table mapping scalar value and // gradient magnitude to opacity if ( this->UpdateColorLookup( vol ) || !this->AlphaLookupIndex ) { this->DeleteTextureIndex(&this->ColorLookupIndex); vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D,0); this->DeleteTextureIndex(&this->AlphaLookupIndex); this->CreateTextureIndex(&this->AlphaLookupIndex); glBindTexture(GL_TEXTURE_2D, this->AlphaLookupIndex); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); //MITK_INFO << "uploading transferfunction"; glTexImage2D(GL_TEXTURE_2D,0,this->InternalAlpha, 256, 256, 0, GL_ALPHA, GL_UNSIGNED_BYTE, this->AlphaLookup ); } vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D, this->AlphaLookupIndex); */ } void vtkMitkOpenGLVolumeTextureMapper3D::RenderOneIndependentShadeFP( vtkRenderer *ren, vtkVolume *vol ) { //GPU_INFO << "RenderOneIndependentShadeFP"; this->SetupOneIndependentTextures( ren, vol ); glEnable( vtkgl::FRAGMENT_PROGRAM_ARB ); vtkgl::BindProgramARB( vtkgl::FRAGMENT_PROGRAM_ARB, prgOneComponentShade ); this->SetupProgramLocalsForShadingFP( ren, vol ); // Bind Textures { vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glEnable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); glBindTexture(GL_TEXTURE_2D, this->ColorLookupIndex); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); } int stages[4] = {1,1,1,0}; this->RenderPolygons( ren, vol, stages ); glDisable( vtkgl::FRAGMENT_PROGRAM_ARB ); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderRGBAShadeFP( vtkRenderer *ren, vtkVolume *vol ) { this->SetupRGBATextures(ren, vol); glEnable( vtkgl::FRAGMENT_PROGRAM_ARB ); vtkgl::BindProgramARB( vtkgl::FRAGMENT_PROGRAM_ARB, prgRGBAShade ); this->SetupProgramLocalsForShadingFP( ren, vol ); // Bind Textures { vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); } int stages[4] = {1,1,1,0}; this->RenderPolygons( ren, vol, stages ); glDisable( vtkgl::FRAGMENT_PROGRAM_ARB ); } void vtkMitkOpenGLVolumeTextureMapper3D::GetLightInformation( vtkRenderer *ren, vtkVolume *vol, GLfloat lightDirection[2][4], GLfloat lightDiffuseColor[2][4], GLfloat lightSpecularColor[2][4], GLfloat halfwayVector[2][4], GLfloat ambientColor[4] ) { //GPU_INFO << "GetLightInformation"; float ambient = vol->GetProperty()->GetAmbient(); float diffuse = vol->GetProperty()->GetDiffuse(); float specular = vol->GetProperty()->GetSpecular(); vtkTransform *volumeTransform = vtkTransform::New(); volumeTransform->SetMatrix( vol->GetMatrix() ); volumeTransform->Inverse(); vtkLightCollection *lights = ren->GetLights(); lights->InitTraversal(); vtkLight *light[2]; light[0] = lights->GetNextItem(); light[1] = lights->GetNextItem(); int lightIndex = 0; double cameraPosition[3]; double cameraFocalPoint[3]; ren->GetActiveCamera()->GetPosition( cameraPosition ); ren->GetActiveCamera()->GetFocalPoint( cameraFocalPoint ); double viewDirection[3]; volumeTransform->TransformPoint( cameraPosition, cameraPosition ); volumeTransform->TransformPoint( cameraFocalPoint, cameraFocalPoint ); viewDirection[0] = cameraFocalPoint[0] - cameraPosition[0]; viewDirection[1] = cameraFocalPoint[1] - cameraPosition[1]; viewDirection[2] = cameraFocalPoint[2] - cameraPosition[2]; vtkMath::Normalize( viewDirection ); ambientColor[0] = 0.0; ambientColor[1] = 0.0; ambientColor[2] = 0.0; ambientColor[3] = 0.0; for ( lightIndex = 0; lightIndex < 2; lightIndex++ ) { float dir[3] = {0,0,0}; float half[3] = {0,0,0}; if ( light[lightIndex] == NULL || light[lightIndex]->GetSwitch() == 0 ) { lightDiffuseColor[lightIndex][0] = 0.0; lightDiffuseColor[lightIndex][1] = 0.0; lightDiffuseColor[lightIndex][2] = 0.0; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0] = 0.0; lightSpecularColor[lightIndex][1] = 0.0; lightSpecularColor[lightIndex][2] = 0.0; lightSpecularColor[lightIndex][3] = 0.0; } else { float lightIntensity = light[lightIndex]->GetIntensity(); double lightColor[3]; light[lightIndex]->GetDiffuseColor( lightColor ); double lightPosition[3]; double lightFocalPoint[3]; light[lightIndex]->GetTransformedPosition( lightPosition ); light[lightIndex]->GetTransformedFocalPoint( lightFocalPoint ); volumeTransform->TransformPoint( lightPosition, lightPosition ); volumeTransform->TransformPoint( lightFocalPoint, lightFocalPoint ); dir[0] = lightPosition[0] - lightFocalPoint[0]; dir[1] = lightPosition[1] - lightFocalPoint[1]; dir[2] = lightPosition[2] - lightFocalPoint[2]; vtkMath::Normalize( dir ); lightDiffuseColor[lightIndex][0] = lightColor[0]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][1] = lightColor[1]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][2] = lightColor[2]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][3] = 1.0; lightSpecularColor[lightIndex][0]= lightColor[0]*specular*lightIntensity; lightSpecularColor[lightIndex][1]= lightColor[1]*specular*lightIntensity; lightSpecularColor[lightIndex][2]= lightColor[2]*specular*lightIntensity; lightSpecularColor[lightIndex][3] = 0.0; half[0] = dir[0] - viewDirection[0]; half[1] = dir[1] - viewDirection[1]; half[2] = dir[2] - viewDirection[2]; vtkMath::Normalize( half ); ambientColor[0] += ambient*lightColor[0]; ambientColor[1] += ambient*lightColor[1]; ambientColor[2] += ambient*lightColor[2]; } lightDirection[lightIndex][0] = (dir[0]+1.0)/2.0; lightDirection[lightIndex][1] = (dir[1]+1.0)/2.0; lightDirection[lightIndex][2] = (dir[2]+1.0)/2.0; lightDirection[lightIndex][3] = 0.0; halfwayVector[lightIndex][0] = (half[0]+1.0)/2.0; halfwayVector[lightIndex][1] = (half[1]+1.0)/2.0; halfwayVector[lightIndex][2] = (half[2]+1.0)/2.0; halfwayVector[lightIndex][3] = 0.0; } volumeTransform->Delete(); } void vtkMitkOpenGLVolumeTextureMapper3D::SetupProgramLocalsForShadingFP( vtkRenderer *ren, vtkVolume *vol ) { //GPU_INFO << "SetupProgramLocalsForShadingFP"; GLfloat lightDirection[2][4]; GLfloat lightDiffuseColor[2][4]; GLfloat lightSpecularColor[2][4]; GLfloat halfwayVector[2][4]; GLfloat ambientColor[4]; float ambient = vol->GetProperty()->GetAmbient(); float diffuse = vol->GetProperty()->GetDiffuse(); float specular = vol->GetProperty()->GetSpecular(); float specularPower = vol->GetProperty()->GetSpecularPower(); vtkTransform *volumeTransform = vtkTransform::New(); volumeTransform->SetMatrix( vol->GetMatrix() ); volumeTransform->Inverse(); vtkLightCollection *lights = ren->GetLights(); lights->InitTraversal(); vtkLight *light[2]; light[0] = lights->GetNextItem(); light[1] = lights->GetNextItem(); int lightIndex = 0; double cameraPosition[3]; double cameraFocalPoint[3]; ren->GetActiveCamera()->GetPosition( cameraPosition ); ren->GetActiveCamera()->GetFocalPoint( cameraFocalPoint ); volumeTransform->TransformPoint( cameraPosition, cameraPosition ); volumeTransform->TransformPoint( cameraFocalPoint, cameraFocalPoint ); double viewDirection[4]; viewDirection[0] = cameraFocalPoint[0] - cameraPosition[0]; viewDirection[1] = cameraFocalPoint[1] - cameraPosition[1]; viewDirection[2] = cameraFocalPoint[2] - cameraPosition[2]; viewDirection[3] = 0.0; vtkMath::Normalize( viewDirection ); ambientColor[0] = 0.0; ambientColor[1] = 0.0; ambientColor[2] = 0.0; ambientColor[3] = 0.0; for ( lightIndex = 0; lightIndex < 2; lightIndex++ ) { float dir[3] = {0,0,0}; float half[3] = {0,0,0}; if ( light[lightIndex] == NULL || light[lightIndex]->GetSwitch() == 0 ) { lightDiffuseColor[lightIndex][0] = 0.0; lightDiffuseColor[lightIndex][1] = 0.0; lightDiffuseColor[lightIndex][2] = 0.0; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0] = 0.0; lightSpecularColor[lightIndex][1] = 0.0; lightSpecularColor[lightIndex][2] = 0.0; lightSpecularColor[lightIndex][3] = 0.0; } else { float lightIntensity = light[lightIndex]->GetIntensity(); double lightColor[3]; light[lightIndex]->GetDiffuseColor( lightColor ); double lightPosition[3]; double lightFocalPoint[3]; light[lightIndex]->GetTransformedPosition( lightPosition ); light[lightIndex]->GetTransformedFocalPoint( lightFocalPoint ); volumeTransform->TransformPoint( lightPosition, lightPosition ); volumeTransform->TransformPoint( lightFocalPoint, lightFocalPoint ); dir[0] = lightPosition[0] - lightFocalPoint[0]; dir[1] = lightPosition[1] - lightFocalPoint[1]; dir[2] = lightPosition[2] - lightFocalPoint[2]; vtkMath::Normalize( dir ); lightDiffuseColor[lightIndex][0] = lightColor[0]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][1] = lightColor[1]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][2] = lightColor[2]*diffuse*lightIntensity; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0]= lightColor[0]*specular*lightIntensity; lightSpecularColor[lightIndex][1]= lightColor[1]*specular*lightIntensity; lightSpecularColor[lightIndex][2]= lightColor[2]*specular*lightIntensity; lightSpecularColor[lightIndex][3] = 0.0; half[0] = dir[0] - viewDirection[0]; half[1] = dir[1] - viewDirection[1]; half[2] = dir[2] - viewDirection[2]; vtkMath::Normalize( half ); ambientColor[0] += ambient*lightColor[0]; ambientColor[1] += ambient*lightColor[1]; ambientColor[2] += ambient*lightColor[2]; } lightDirection[lightIndex][0] = dir[0]; lightDirection[lightIndex][1] = dir[1]; lightDirection[lightIndex][2] = dir[2]; lightDirection[lightIndex][3] = 0.0; halfwayVector[lightIndex][0] = half[0]; halfwayVector[lightIndex][1] = half[1]; halfwayVector[lightIndex][2] = half[2]; halfwayVector[lightIndex][3] = 0.0; } volumeTransform->Delete(); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 0, lightDirection[0][0], lightDirection[0][1], lightDirection[0][2], lightDirection[0][3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 1, halfwayVector[0][0], halfwayVector[0][1], halfwayVector[0][2], halfwayVector[0][3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 2, ambient, diffuse, specular, specularPower ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 3, lightDiffuseColor[0][0], lightDiffuseColor[0][1], lightDiffuseColor[0][2], lightDiffuseColor[0][3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 4, lightSpecularColor[0][0], lightSpecularColor[0][1], lightSpecularColor[0][2], lightSpecularColor[0][3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 5, viewDirection[0], viewDirection[1], viewDirection[2], viewDirection[3] ); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 6, 2.0, -1.0, 0.0, 0.0 ); } int vtkMitkOpenGLVolumeTextureMapper3D::IsRenderSupported( vtkRenderer *renderer, vtkVolumeProperty *property ) { //GPU_INFO << "IsRenderSupported"; if ( !this->Initialized ) { //this->Initialize(); this->Initialize(renderer); } if ( !this->RenderPossible ) { return 0; } if ( !this->GetInput() ) { return 0; } if ( this->GetInput()->GetNumberOfScalarComponents() > 1 && property->GetIndependentComponents() ) { return 0; } return 1; } void vtkMitkOpenGLVolumeTextureMapper3D::Initialize(vtkRenderer *renderer) { //GPU_INFO << "Initialize"; this->Initialized = 1; // vtkOpenGLExtensionManager * extensions = vtkOpenGLExtensionManager::New(); //extensions->SetRenderWindow(NULL); // set render window to the current one. vtkOpenGLExtensionManager *extensions=static_cast(renderer->GetRenderWindow())->GetExtensionManager(); int supports_texture3D=extensions->ExtensionSupported( "GL_VERSION_1_2" ); if(supports_texture3D) { extensions->LoadExtension("GL_VERSION_1_2"); } else { supports_texture3D=extensions->ExtensionSupported( "GL_EXT_texture3D" ); if(supports_texture3D) { extensions->LoadCorePromotedExtension("GL_EXT_texture3D"); } } int supports_multitexture=extensions->ExtensionSupported( "GL_VERSION_1_3" ); if(supports_multitexture) { extensions->LoadExtension("GL_VERSION_1_3"); } else { supports_multitexture= extensions->ExtensionSupported("GL_ARB_multitexture"); if(supports_multitexture) { extensions->LoadCorePromotedExtension("GL_ARB_multitexture"); } } this->SupportsCompressedTexture=extensions->ExtensionSupported("GL_VERSION_1_3")==1; if(!this->SupportsCompressedTexture) { this->SupportsCompressedTexture= extensions->ExtensionSupported("GL_ARB_texture_compression")==1; if(this->SupportsCompressedTexture) { extensions->LoadCorePromotedExtension("GL_ARB_texture_compression"); } } //GPU_INFO(this->SupportsCompressedTexture) << "supporting compressed textures"; this->SupportsNonPowerOfTwoTextures= extensions->ExtensionSupported("GL_VERSION_2_0") || extensions->ExtensionSupported("GL_ARB_texture_non_power_of_two"); //GPU_INFO << "np2: " << (this->SupportsNonPowerOfTwoTextures?1:0); int supports_GL_ARB_fragment_program = extensions->ExtensionSupported( "GL_ARB_fragment_program" ); if(supports_GL_ARB_fragment_program) { extensions->LoadExtension( "GL_ARB_fragment_program" ); } int supports_GL_ARB_vertex_program = extensions->ExtensionSupported( "GL_ARB_vertex_program" ); if(supports_GL_ARB_vertex_program) { extensions->LoadExtension( "GL_ARB_vertex_program" ); } RenderPossible = 0; if ( supports_texture3D && supports_multitexture && supports_GL_ARB_fragment_program && supports_GL_ARB_vertex_program && vtkgl::TexImage3D && vtkgl::ActiveTexture && vtkgl::MultiTexCoord3fv && vtkgl::GenProgramsARB && vtkgl::DeleteProgramsARB && vtkgl::BindProgramARB && vtkgl::ProgramStringARB && vtkgl::ProgramLocalParameter4fARB ) { RenderPossible = 1; } else { std::string errString = "no gpu-acceleration possible cause following extensions/methods are missing or unsupported:"; if(!supports_texture3D) errString += " EXT_TEXTURE3D"; if(!supports_multitexture) errString += " EXT_MULTITEXTURE"; if(!supports_GL_ARB_fragment_program) errString += " ARB_FRAGMENT_PROGRAM"; if(!supports_GL_ARB_vertex_program) errString += " ARB_VERTEX_PROGRAM"; if(!vtkgl::TexImage3D) errString += " glTexImage3D"; if(!vtkgl::ActiveTexture) errString += " glActiveTexture"; if(!vtkgl::MultiTexCoord3fv) errString += " glMultiTexCoord3fv"; if(!vtkgl::GenProgramsARB) errString += " glGenProgramsARB"; if(!vtkgl::DeleteProgramsARB) errString += " glDeleteProgramsARB"; if(!vtkgl::BindProgramARB) errString += " glBindProgramARB"; if(!vtkgl::ProgramStringARB) errString += " glProgramStringARB"; if(!vtkgl::ProgramLocalParameter4fARB) errString += " glProgramLocalParameter4fARB"; GPU_WARN << errString; }; if(RenderPossible) { vtkgl::GenProgramsARB( 1, &prgOneComponentShade ); vtkgl::BindProgramARB( vtkgl::FRAGMENT_PROGRAM_ARB, prgOneComponentShade ); vtkgl::ProgramStringARB( vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkMitkVolumeTextureMapper3D_OneComponentShadeFP)), vtkMitkVolumeTextureMapper3D_OneComponentShadeFP ); vtkgl::GenProgramsARB( 1, &prgRGBAShade ); vtkgl::BindProgramARB( vtkgl::FRAGMENT_PROGRAM_ARB, prgRGBAShade ); vtkgl::ProgramStringARB( vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkMitkVolumeTextureMapper3D_FourDependentShadeFP)), vtkMitkVolumeTextureMapper3D_FourDependentShadeFP ); } } // ---------------------------------------------------------------------------- // Print the vtkMitkOpenGLVolumeTextureMapper3D void vtkMitkOpenGLVolumeTextureMapper3D::PrintSelf(ostream& os, vtkIndent indent) { // vtkOpenGLExtensionManager * extensions = vtkOpenGLExtensionManager::New(); // extensions->SetRenderWindow(NULL); // set render window to current render window os << indent << "Initialized " << this->Initialized << endl; /* if ( this->Initialized ) { os << indent << "Supports GL_VERSION_1_2:" << extensions->ExtensionSupported( "GL_VERSION_1_2" ) << endl; os << indent << "Supports GL_EXT_texture3D:" << extensions->ExtensionSupported( "GL_EXT_texture3D" ) << endl; os << indent << "Supports GL_VERSION_1_3:" << extensions->ExtensionSupported( "GL_VERSION_1_3" ) << endl; os << indent << "Supports GL_ARB_multitexture: " << extensions->ExtensionSupported( "GL_ARB_multitexture" ) << endl; os << indent << "Supports GL_NV_texture_shader2: " << extensions->ExtensionSupported( "GL_NV_texture_shader2" ) << endl; os << indent << "Supports GL_NV_register_combiners2: " << extensions->ExtensionSupported( "GL_NV_register_combiners2" ) << endl; os << indent << "Supports GL_ATI_fragment_shader: " << extensions->ExtensionSupported( "GL_ATI_fragment_shader" ) << endl; os << indent << "Supports GL_ARB_fragment_program: " << extensions->ExtensionSupported( "GL_ARB_fragment_program" ) << endl; os << indent << "Supports GL_ARB_texture_compression: " << extensions->ExtensionSupported( "GL_ARB_texture_compression" ) << endl; os << indent << "Supports GL_VERSION_2_0:" << extensions->ExtensionSupported( "GL_VERSION_2_0" ) << endl; os << indent << "Supports GL_ARB_texture_non_power_of_two:" << extensions->ExtensionSupported( "GL_ARB_texture_non_power_of_two" ) << endl; } extensions->Delete(); */ if(this->RenderWindow!=0) { vtkOpenGLExtensionManager *extensions= static_cast(this->RenderWindow)->GetExtensionManager(); if ( this->Initialized ) { os << indent << "Supports GL_VERSION_1_2:" << extensions->ExtensionSupported( "GL_VERSION_1_2" ) << endl; os << indent << "Supports GL_EXT_texture3D:" << extensions->ExtensionSupported( "GL_EXT_texture3D" ) << endl; os << indent << "Supports GL_VERSION_1_3:" << extensions->ExtensionSupported( "GL_VERSION_1_3" ) << endl; os << indent << "Supports GL_ARB_multitexture: " << extensions->ExtensionSupported( "GL_ARB_multitexture" ) << endl; os << indent << "Supports GL_NV_texture_shader2: " << extensions->ExtensionSupported( "GL_NV_texture_shader2" ) << endl; os << indent << "Supports GL_NV_register_combiners2: " << extensions->ExtensionSupported( "GL_NV_register_combiners2" ) << endl; os << indent << "Supports GL_ATI_fragment_shader: " << extensions->ExtensionSupported( "GL_ATI_fragment_shader" ) << endl; os << indent << "Supports GL_ARB_fragment_program: " << extensions->ExtensionSupported( "GL_ARB_fragment_program" ) << endl; os << indent << "Supports GL_ARB_texture_compression: " << extensions->ExtensionSupported( "GL_ARB_texture_compression" ) << endl; os << indent << "Supports GL_VERSION_2_0:" << extensions->ExtensionSupported( "GL_VERSION_2_0" ) << endl; os << indent << "Supports GL_ARB_texture_non_power_of_two:" << extensions->ExtensionSupported( "GL_ARB_texture_non_power_of_two" ) << endl; } } this->Superclass::PrintSelf(os,indent); } diff --git a/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.h b/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.h index 322ce2eb06..92beb27568 100644 --- a/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.h +++ b/Modules/MitkExt/Rendering/vtkMitkOpenGLVolumeTextureMapper3D.h @@ -1,145 +1,153 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // .NAME vtkMitkOpenGLVolumeTextureMapper3D - concrete implementation of 3D volume texture mapping // .SECTION Description // vtkMitkOpenGLVolumeTextureMapper3D renders a volume using 3D texture mapping. // See vtkMitkVolumeTextureMapper3D for full description. // .SECTION see also // vtkMitkVolumeTextureMapper3D vtkVolumeMapper #ifndef __vtkMitkOpenGLVolumeTextureMapper3D_h #define __vtkMitkOpenGLVolumeTextureMapper3D_h #include "vtkMitkVolumeTextureMapper3D.h" +#include "mitkBaseRenderer.h" #include "MitkExtExports.h" #ifndef VTK_IMPLEMENT_MESA_CXX # include "vtkOpenGL.h" // GLfloat type is used in some method signatures. #endif class vtkRenderWindow; class vtkVolumeProperty; #include "mitkCommon.h" class MitkExt_EXPORT vtkMitkOpenGLVolumeTextureMapper3D : public vtkMitkVolumeTextureMapper3D { public: vtkTypeRevisionMacro(vtkMitkOpenGLVolumeTextureMapper3D,vtkMitkVolumeTextureMapper3D); void PrintSelf(ostream& os, vtkIndent indent); static vtkMitkOpenGLVolumeTextureMapper3D *New(); // Description: // Is hardware rendering supported? No if the input data is // more than one independent component, or if the hardware does // not support the required extensions // int IsRenderSupported(vtkVolumeProperty *); int IsRenderSupported(vtkRenderer *ren,vtkVolumeProperty *); //BTX // Description: // WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE // DO NOT USE THIS METHOD OUTSIDE OF THE RENDERING PROCESS // Render the volume virtual void Render(vtkRenderer *ren, vtkVolume *vol); //ETX // Desciption: // Initialize when we go to render, or go to answer the // IsRenderSupported question. Don't call unless we have // a valid OpenGL context! vtkGetMacro( Initialized, int ); // Description: // Release any graphics resources that are being consumed by this texture. // The parameter window could be used to determine which graphic // resources to release. - void ReleaseGraphicsResources(vtkWindow *); + // deprecatedSince{2013_12} Use ReleaseGraphicsResources(mitk::BaseRenderer* renderer) instead + DEPRECATED(void ReleaseGraphicsResources(vtkWindow *)); + + // Description: + // Release any graphics resources that are being consumed by this texture. + // The parameter renderer could be used to determine which graphic + // resources to release. + void ReleaseGraphicsResources(mitk::BaseRenderer * renderer); protected: vtkMitkOpenGLVolumeTextureMapper3D(); ~vtkMitkOpenGLVolumeTextureMapper3D(); bool RenderPossible; //BTX void GetLightInformation(vtkRenderer *ren, vtkVolume *vol, GLfloat lightDirection[2][4], GLfloat lightDiffuseColor[2][4], GLfloat lightSpecularColor[2][4], GLfloat halfwayVector[2][4], GLfloat *ambient ); //ETX int Initialized; GLuint Volume1Index; GLuint Volume2Index; GLuint Volume3Index; GLuint ColorLookupIndex; GLuint AlphaLookupIndex; GLuint prgOneComponentShade; GLuint prgRGBAShade; vtkRenderWindow *RenderWindow; bool SupportsCompressedTexture; //void Initialize(); void Initialize(vtkRenderer *r); virtual void RenderFP(vtkRenderer *ren, vtkVolume *vol); void SetupOneIndependentTextures( vtkRenderer *ren, vtkVolume *vol ); void RenderOneIndependentShadeFP( vtkRenderer *ren, vtkVolume *vol ); void SetupRGBATextures( vtkRenderer *ren, vtkVolume *vol ); void RenderRGBAShadeFP( vtkRenderer *ren, vtkVolume *vol ); void DeleteTextureIndex( GLuint *index ); void CreateTextureIndex( GLuint *index ); void RenderPolygons( vtkRenderer *ren, vtkVolume *vol, int stages[4] ); void SetupProgramLocalsForShadingFP( vtkRenderer *ren, vtkVolume *vol ); void Setup3DTextureParameters( bool linear ); void ComputeVolumeDimensions(); bool UpdateVolumes( vtkVolume * ); bool UpdateVolumesRGBA( vtkVolume * ); private: vtkMitkOpenGLVolumeTextureMapper3D(const vtkMitkOpenGLVolumeTextureMapper3D&); // Not implemented. void operator=(const vtkMitkOpenGLVolumeTextureMapper3D&); // Not implemented. }; #endif diff --git a/Modules/MitkExt/Rendering/vtkUnstructuredGridMapper.cpp b/Modules/MitkExt/Rendering/vtkUnstructuredGridMapper.cpp index dab5e71a47..514d8805f6 100644 --- a/Modules/MitkExt/Rendering/vtkUnstructuredGridMapper.cpp +++ b/Modules/MitkExt/Rendering/vtkUnstructuredGridMapper.cpp @@ -1,230 +1,239 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "vtkUnstructuredGridMapper.h" #include "vtkGeometryFilter.h" #include "vtkExecutive.h" #include "vtkGarbageCollector.h" #include "vtkInformation.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkUnstructuredGrid.h" vtkCxxRevisionMacro(vtkUnstructuredGridMapper, "$Revision$"); vtkStandardNewMacro(vtkUnstructuredGridMapper); //---------------------------------------------------------------------------- vtkUnstructuredGridMapper::vtkUnstructuredGridMapper() { this->GeometryExtractor = 0; this->PolyDataMapper = 0; } //---------------------------------------------------------------------------- vtkUnstructuredGridMapper::~vtkUnstructuredGridMapper() { // delete internally created objects. if ( this->GeometryExtractor ) { this->GeometryExtractor->Delete(); } if ( this->PolyDataMapper ) { this->PolyDataMapper->Delete(); } } void vtkUnstructuredGridMapper::SetBoundingObject(mitk::BoundingObject* bo) { m_BoundingObject = bo; } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::SetInput(vtkUnstructuredGrid *input) { if(input) { this->SetInputConnection(0, input->GetProducerPort()); } else { // Setting a NULL input removes the connection. this->SetInputConnection(0, 0); } } //---------------------------------------------------------------------------- vtkUnstructuredGrid *vtkUnstructuredGridMapper::GetInput() { //return this->Superclass::GetInputAsDataSet(); return vtkUnstructuredGrid::SafeDownCast( this->GetExecutive()->GetInputData(0, 0)); } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::ReleaseGraphicsResources( vtkWindow *renWin ) { if (this->PolyDataMapper) { this->PolyDataMapper->ReleaseGraphicsResources( renWin ); } } +//---------------------------------------------------------------------------- +void vtkUnstructuredGridMapper::ReleaseGraphicsResources( mitk::BaseRenderer * renderer ) +{ + if (this->PolyDataMapper) + { + this->PolyDataMapper->ReleaseGraphicsResources( renderer->GetVtkRenderer()->GetRenderWindow()); + } +} + //---------------------------------------------------------------------------- // Receives from Actor -> maps data to primitives // void vtkUnstructuredGridMapper::Render(vtkRenderer *ren, vtkActor *act) { // make sure that we've been properly initialized // if ( !this->GetInput() ) { vtkErrorMacro(<< "No input!\n"); return; } // Need a lookup table // if ( this->LookupTable == 0 ) { this->CreateDefaultLookupTable(); } this->LookupTable->Build(); // Now can create appropriate mapper // if ( this->PolyDataMapper == 0 ) { vtkGeometryFilter *gf = vtkGeometryFilter::New(); vtkPolyDataMapper *pm = vtkPolyDataMapper::New(); pm->SetInput(gf->GetOutput()); this->GeometryExtractor = gf; this->PolyDataMapper = pm; } // share clipping planes with the PolyDataMapper // if (this->ClippingPlanes != this->PolyDataMapper->GetClippingPlanes()) { this->PolyDataMapper->SetClippingPlanes(this->ClippingPlanes); } if (this->m_BoundingObject) { mitk::BoundingBox::BoundsArrayType bounds = this->m_BoundingObject->GetGeometry()->CalculateBoundingBoxRelativeToTransform(0)->GetBounds(); this->GeometryExtractor->SetExtent(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); this->GeometryExtractor->ExtentClippingOn(); } else { this->GeometryExtractor->ExtentClippingOff(); } this->GeometryExtractor->SetInput(this->GetInput()); this->PolyDataMapper->SetInput(this->GeometryExtractor->GetOutput()); // update ourselves in case something has changed this->PolyDataMapper->SetLookupTable(this->GetLookupTable()); this->PolyDataMapper->SetScalarVisibility(this->GetScalarVisibility()); this->PolyDataMapper->SetUseLookupTableScalarRange( this->GetUseLookupTableScalarRange()); this->PolyDataMapper->SetScalarRange(this->GetScalarRange()); this->PolyDataMapper->SetImmediateModeRendering( this->GetImmediateModeRendering()); this->PolyDataMapper->SetColorMode(this->GetColorMode()); this->PolyDataMapper->SetInterpolateScalarsBeforeMapping( this->GetInterpolateScalarsBeforeMapping()); this->PolyDataMapper->SetScalarMode(this->GetScalarMode()); if ( this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_FIELD_DATA || this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_FIELD_DATA ) { if ( this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID ) { this->PolyDataMapper->ColorByArrayComponent(this->ArrayId,ArrayComponent); } else { this->PolyDataMapper->ColorByArrayComponent(this->ArrayName,ArrayComponent); } } this->PolyDataMapper->Render(ren,act); this->TimeToDraw = this->PolyDataMapper->GetTimeToDraw(); } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); if ( this->PolyDataMapper ) { os << indent << "Poly Mapper: (" << this->PolyDataMapper << ")\n"; } else { os << indent << "Poly Mapper: (none)\n"; } if ( this->GeometryExtractor ) { os << indent << "Geometry Extractor: (" << this->GeometryExtractor << ")\n"; } else { os << indent << "Geometry Extractor: (none)\n"; } } //---------------------------------------------------------------------------- unsigned long vtkUnstructuredGridMapper::GetMTime() { unsigned long mTime=this->vtkMapper::GetMTime(); unsigned long time; if ( this->LookupTable != NULL ) { time = this->LookupTable->GetMTime(); mTime = ( time > mTime ? time : mTime ); } return mTime; } //---------------------------------------------------------------------------- int vtkUnstructuredGridMapper::FillInputPortInformation( int vtkNotUsed(port), vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid"); return 1; } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::ReportReferences(vtkGarbageCollector* collector) { this->Superclass::ReportReferences(collector); // These filters share our input and are therefore involved in a // reference loop. vtkGarbageCollectorReport(collector, this->GeometryExtractor, "GeometryExtractor"); vtkGarbageCollectorReport(collector, this->PolyDataMapper, "PolyDataMapper"); } diff --git a/Modules/MitkExt/Rendering/vtkUnstructuredGridMapper.h b/Modules/MitkExt/Rendering/vtkUnstructuredGridMapper.h index 876e3f221d..785f689734 100644 --- a/Modules/MitkExt/Rendering/vtkUnstructuredGridMapper.h +++ b/Modules/MitkExt/Rendering/vtkUnstructuredGridMapper.h @@ -1,78 +1,88 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __vtkUnstructuredGridMapper_h #define __vtkUnstructuredGridMapper_h #include "mitkCommon.h" #include "MitkExtExports.h" #include "mitkBoundingObject.h" +#include "mitkBaseRenderer.h" #include "vtkMapper.h" class vtkPolyDataMapper; class vtkGeometryFilter; class vtkUnstructuredGrid; class MitkExt_EXPORT vtkUnstructuredGridMapper : public vtkMapper { public: static vtkUnstructuredGridMapper *New(); vtkTypeRevisionMacro(vtkUnstructuredGridMapper,vtkMapper); void PrintSelf(ostream& os, vtkIndent indent); void Render(vtkRenderer *ren, vtkActor *act); // Description: // Get the internal poly data mapper used to map data set to graphics system. vtkGetObjectMacro(PolyDataMapper, vtkPolyDataMapper); // Description: // Release any graphics resources that are being consumed by this mapper. // The parameter window could be used to determine which graphic // resources to release. - void ReleaseGraphicsResources(vtkWindow *); + // deprecatedSince{2013_12} Use ReleaseGraphicsResources(mitk::BaseRenderer* renderer) instead + DEPRECATED(void ReleaseGraphicsResources(vtkWindow *)); + + // Description: + // Release any graphics resources that are being consumed by this mapper. + // The parameter renderer could be used to determine which graphic + // resources to release. + // deprecatedSince{2013_12} Use ReleaseGraphicsResources(mitk::BaseRenderer* renderer) instead + void ReleaseGraphicsResources(mitk::BaseRenderer * renderer); + // Description: // Get the mtime also considering the lookup table. unsigned long GetMTime(); // Description: // Set the Input of this mapper. void SetInput(vtkUnstructuredGrid *input); vtkUnstructuredGrid *GetInput(); void SetBoundingObject(mitk::BoundingObject* bo); protected: vtkUnstructuredGridMapper(); ~vtkUnstructuredGridMapper(); vtkGeometryFilter *GeometryExtractor; vtkPolyDataMapper *PolyDataMapper; mitk::BoundingObject::Pointer m_BoundingObject; virtual void ReportReferences(vtkGarbageCollector*); // see algorithm for more info virtual int FillInputPortInformation(int port, vtkInformation* info); private: vtkUnstructuredGridMapper(const vtkUnstructuredGridMapper&); // Not implemented. void operator=(const vtkUnstructuredGridMapper&); // Not implemented. }; #endif // __vtkUnstructuredGridMapper_h diff --git a/Modules/Qmitk/CMakeLists.txt b/Modules/Qmitk/CMakeLists.txt index c9ce24c0c0..d7252919af 100644 --- a/Modules/Qmitk/CMakeLists.txt +++ b/Modules/Qmitk/CMakeLists.txt @@ -1,9 +1,9 @@ MITK_CREATE_MODULE( Qmitk - DEPENDS Mitk + DEPENDS Mitk PlanarFigure PACKAGE_DEPENDS QT QVTK SUBPROJECTS MITK-CoreUI EXPORT_DEFINE QMITK_EXPORT QT_MODULE ) add_subdirectory(Testing) diff --git a/Modules/Qmitk/QmitkDataStorageTreeModel.cpp b/Modules/Qmitk/QmitkDataStorageTreeModel.cpp index 1e64846e19..b267a98f49 100644 --- a/Modules/Qmitk/QmitkDataStorageTreeModel.cpp +++ b/Modules/Qmitk/QmitkDataStorageTreeModel.cpp @@ -1,919 +1,925 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include +#include #include #include #include "QmitkDataStorageTreeModel.h" #include "QmitkNodeDescriptorManager.h" #include #include #include #include #include #include QmitkDataStorageTreeModel::QmitkDataStorageTreeModel( mitk::DataStorage* _DataStorage , bool _PlaceNewNodesOnTop , bool _ShowHelperObjects , bool _ShowNodesContainingNoData , QObject* parent ) : QAbstractItemModel(parent) , m_DataStorage(0) , m_PlaceNewNodesOnTop(_PlaceNewNodesOnTop) , m_ShowHelperObjects(_ShowHelperObjects) , m_ShowNodesContainingNoData(_ShowNodesContainingNoData) , m_Root(0) { this->UpdateNodeVisibility(); this->SetDataStorage(_DataStorage); } QmitkDataStorageTreeModel::~QmitkDataStorageTreeModel() { // set data storage to 0 = remove all listeners this->SetDataStorage(0); m_Root->Delete(); m_Root = 0; //Removing all observers for ( NodeTagMapType::iterator dataIter = m_HelperObjectObserverTags.begin(); dataIter != m_HelperObjectObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("helper object")->RemoveObserver( (*dataIter).second ); } m_HelperObjectObserverTags.clear(); } mitk::DataNode::Pointer QmitkDataStorageTreeModel::GetNode( const QModelIndex &index ) const { return this->TreeItemFromIndex(index)->GetDataNode(); } const mitk::DataStorage::Pointer QmitkDataStorageTreeModel::GetDataStorage() const { return m_DataStorage.GetPointer(); } QModelIndex QmitkDataStorageTreeModel::index( int row, int column, const QModelIndex & parent ) const { TreeItem* parentItem; if (!parent.isValid()) parentItem = m_Root; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->GetChild(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } int QmitkDataStorageTreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentTreeItem = this->TreeItemFromIndex(parent); return parentTreeItem->GetChildCount(); } Qt::ItemFlags QmitkDataStorageTreeModel::flags( const QModelIndex& index ) const { mitk::DataNode* dataNode = this->TreeItemFromIndex(index)->GetDataNode(); if (index.isValid()) { if(DicomPropertiesExists(*dataNode)) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; }else{ return Qt::ItemIsDropEnabled; } } int QmitkDataStorageTreeModel::columnCount( const QModelIndex& /* parent = QModelIndex() */ ) const { return 1; } QModelIndex QmitkDataStorageTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = this->TreeItemFromIndex(index); TreeItem *parentItem = childItem->GetParent(); if (parentItem == m_Root) return QModelIndex(); return this->createIndex(parentItem->GetIndex(), 0, parentItem); } QmitkDataStorageTreeModel::TreeItem* QmitkDataStorageTreeModel::TreeItemFromIndex( const QModelIndex &index ) const { if (index.isValid()) return static_cast(index.internalPointer()); else return m_Root; } Qt::DropActions QmitkDataStorageTreeModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions QmitkDataStorageTreeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } bool QmitkDataStorageTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent) { // Early exit, returning true, but not actually doing anything (ignoring data). if (action == Qt::IgnoreAction) { return true; } // Note, we are returning true if we handled it, and false otherwise bool returnValue = false; if(data->hasFormat("application/x-qabstractitemmodeldatalist")) { returnValue = true; // First we extract a Qlist of TreeItem* pointers. QString arg = QString(data->data("application/x-qabstractitemmodeldatalist").data()); QStringList listOfTreeItemAddressPointers = arg.split(","); QStringList::iterator slIter; QList listOfItemsToDrop; for(slIter = listOfTreeItemAddressPointers.begin(); slIter != listOfTreeItemAddressPointers.end(); slIter++) { long val = (*slIter).toLong(); listOfItemsToDrop << static_cast((void*)val); } // Retrieve the TreeItem* where we are dropping stuff, and its parent. TreeItem* dropItem = this->TreeItemFromIndex(parent); TreeItem* parentItem = dropItem->GetParent(); // If item was dropped onto empty space, we select the root node if(dropItem == m_Root) { parentItem = m_Root; } // Dragging and Dropping is only allowed within the same parent, so use the first item in list to validate. // (otherwise, you could have a derived image such as a segmentation, and assign it to another image). // NOTE: We are assuming the input list is valid... i.e. when it was dragged, all the items had the same parent. if(listOfItemsToDrop[0] != dropItem && listOfItemsToDrop[0]->GetParent() == parentItem) { // Retrieve the index of where we are dropping stuff. QModelIndex dropItemModelIndex = this->IndexFromTreeItem(dropItem); QModelIndex parentModelIndex = this->IndexFromTreeItem(parentItem); // Iterate through the list of TreeItem (which may be at non-consecutive indexes). QList::iterator diIter; for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { // Here we assume that as you remove items, one at a time, that GetIndex() will be valid. this->beginRemoveRows(parentModelIndex, (*diIter)->GetIndex(), (*diIter)->GetIndex()); parentItem->RemoveChild(*diIter); this->endRemoveRows(); } // Select the target index position, or put it at the end of the list. int dropIndex = dropItemModelIndex.row(); if (dropIndex == -1) { dropIndex = parentItem->GetChildCount(); } // Now insert items again at the drop item position this->beginInsertRows(parentModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1); for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { parentItem->InsertChild( (*diIter), dropIndex ); dropIndex++; } this->endInsertRows(); // Change Layers to match. this->AdjustLayerProperty(); } } else if(data->hasFormat("application/x-mitk-datanodes")) { returnValue = true; QString arg = QString(data->data("application/x-mitk-datanodes").data()); QStringList listOfDataNodeAddressPointers = arg.split(","); int numberOfNodesDropped = 0; QStringList::iterator slIter; for (slIter = listOfDataNodeAddressPointers.begin(); slIter != listOfDataNodeAddressPointers.end(); slIter++) { long val = (*slIter).toLong(); mitk::DataNode* node = static_cast((void*)val); if(node && m_DataStorage.IsNotNull() && !m_DataStorage->Exists(node)) { m_DataStorage->Add( node ); mitk::BaseData::Pointer basedata = node->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); numberOfNodesDropped++; } } } // Only do a rendering update, if we actually dropped anything. if (numberOfNodesDropped > 0) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return returnValue; } QStringList QmitkDataStorageTreeModel::mimeTypes() const { QStringList types = QAbstractItemModel::mimeTypes(); types << "application/x-qabstractitemmodeldatalist"; types << "application/x-mitk-datanodes"; return types; } QMimeData * QmitkDataStorageTreeModel::mimeData(const QModelIndexList & indexes) const{ QMimeData * ret = new QMimeData; QString treeItemAddresses(""); QString dataNodeAddresses(""); for (int i = 0; i < indexes.size(); i++) { TreeItem* treeItem = static_cast(indexes.at(i).internalPointer()); long treeItemAddress = reinterpret_cast(treeItem); long dataNodeAddress = reinterpret_cast(treeItem->GetDataNode().GetPointer()); QTextStream(&treeItemAddresses) << treeItemAddress; QTextStream(&dataNodeAddresses) << dataNodeAddress; if (i != indexes.size() - 1) { QTextStream(&treeItemAddresses) << ","; QTextStream(&dataNodeAddresses) << ","; } } ret->setData("application/x-qabstractitemmodeldatalist", QByteArray(treeItemAddresses.toAscii())); ret->setData("application/x-mitk-datanodes", QByteArray(dataNodeAddresses.toAscii())); return ret; } QVariant QmitkDataStorageTreeModel::data( const QModelIndex & index, int role ) const { mitk::DataNode* dataNode = this->TreeItemFromIndex(index)->GetDataNode(); // get name of treeItem (may also be edited) QString nodeName; if(DicomPropertiesExists(*dataNode)) { mitk::BaseProperty* seriesDescription = (dataNode->GetProperty("dicom.series.SeriesDescription")); mitk::BaseProperty* studyDescription = (dataNode->GetProperty("dicom.study.StudyDescription")); mitk::BaseProperty* patientsName = (dataNode->GetProperty("dicom.patient.PatientsName")); nodeName.append(patientsName->GetValueAsString().c_str()).append("\n"); nodeName.append(studyDescription->GetValueAsString().c_str()).append("\n"); nodeName.append(seriesDescription->GetValueAsString().c_str()); }else{ nodeName = QString::fromStdString(dataNode->GetName()); } if(nodeName.isEmpty()) { nodeName = "unnamed"; } if (role == Qt::DisplayRole) return nodeName; else if(role == Qt::ToolTipRole) return nodeName; else if(role == Qt::DecorationRole) { QmitkNodeDescriptor* nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(); } else if(role == Qt::CheckStateRole) { return dataNode->IsVisible(0); } else if(role == QmitkDataNodeRole) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if(role == QmitkDataNodeRawPointerRole) { return QVariant::fromValue(dataNode); } return QVariant(); } bool QmitkDataStorageTreeModel::DicomPropertiesExists(const mitk::DataNode& node) const { bool propertiesExists = false; mitk::BaseProperty* seriesDescription = (node.GetProperty("dicom.series.SeriesDescription")); mitk::BaseProperty* studyDescription = (node.GetProperty("dicom.study.StudyDescription")); mitk::BaseProperty* patientsName = (node.GetProperty("dicom.patient.PatientsName")); if(patientsName!=NULL && studyDescription!=NULL && seriesDescription!=NULL) { if((!patientsName->GetValueAsString().empty())&& (!studyDescription->GetValueAsString().empty())&& (!seriesDescription->GetValueAsString().empty())) { propertiesExists = true; } } return propertiesExists; } QVariant QmitkDataStorageTreeModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_Root) return QString::fromStdString(m_Root->GetDataNode()->GetName()); return QVariant(); } void QmitkDataStorageTreeModel::SetDataStorage( mitk::DataStorage* _DataStorage ) { if(m_DataStorage != _DataStorage) // dont take the same again { if(m_DataStorage.IsNotNull()) { // remove Listener for the data storage itself m_DataStorage.ObjectDelete.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetDataStorageDeleted ) ); // remove listeners for the nodes m_DataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::AddNode ) ); m_DataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified ) ); m_DataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode ) ); } // take over the new data storage m_DataStorage = _DataStorage; // delete the old root (if necessary, create new) if(m_Root) m_Root->Delete(); mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New(); rootDataNode->SetName("Data Manager"); m_Root = new TreeItem(rootDataNode, 0); this->reset(); if(m_DataStorage.IsNotNull()) { // add Listener for the data storage itself m_DataStorage.ObjectDelete.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetDataStorageDeleted ) ); // add listeners for the nodes m_DataStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::AddNode ) ); m_DataStorage->ChangedNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified ) ); m_DataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode ) ); mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = m_DataStorage->GetSubset(m_Predicate); // finally add all nodes to the model this->Update(); } } } void QmitkDataStorageTreeModel::SetDataStorageDeleted( const itk::Object* /*_DataStorage*/ ) { this->SetDataStorage(0); } void QmitkDataStorageTreeModel::AddNodeInternal(const mitk::DataNode *node) { if(node == 0 || m_DataStorage.IsNull() || !m_DataStorage->Exists(node) || !m_Predicate->CheckNode(node) || m_Root->Find(node) != 0) return; // find out if we have a root node TreeItem* parentTreeItem = m_Root; QModelIndex index; mitk::DataNode* parentDataNode = this->GetParentNode(node); if(parentDataNode) // no top level data node { parentTreeItem = m_Root->Find(parentDataNode); // find the corresponding tree item if(!parentTreeItem) { this->AddNode(parentDataNode); parentTreeItem = m_Root->Find(parentDataNode); if(!parentTreeItem) return; } // get the index of this parent with the help of the grand parent index = this->createIndex(parentTreeItem->GetIndex(), 0, parentTreeItem); } // add node if(m_PlaceNewNodesOnTop) { // emit beginInsertRows event beginInsertRows(index, 0, 0); parentTreeItem->InsertChild(new TreeItem( const_cast(node)), 0); } else { beginInsertRows(index, parentTreeItem->GetChildCount() , parentTreeItem->GetChildCount()); new TreeItem(const_cast(node), parentTreeItem); } // emit endInsertRows event endInsertRows(); this->AdjustLayerProperty(); } void QmitkDataStorageTreeModel::AddNode( const mitk::DataNode* node ) { if(node == 0 || m_DataStorage.IsNull() || !m_DataStorage->Exists(node) || m_Root->Find(node) != 0) return; bool isHelperObject (false); NodeTagMapType::iterator searchIter = m_HelperObjectObserverTags.find( const_cast(node) ); if (node->GetBoolProperty("helper object", isHelperObject) && searchIter == m_HelperObjectObserverTags.end()) { itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkDataStorageTreeModel::UpdateNodeVisibility); m_HelperObjectObserverTags.insert( std::pair( const_cast(node), node->GetProperty("helper object")->AddObserver( itk::ModifiedEvent(), command ) ) ); } if (m_Predicate->CheckNode(node)) this->AddNodeInternal(node); } void QmitkDataStorageTreeModel::SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop) { m_PlaceNewNodesOnTop = _PlaceNewNodesOnTop; } void QmitkDataStorageTreeModel::RemoveNodeInternal( const mitk::DataNode* node ) { if(!m_Root) return; TreeItem* treeItem = m_Root->Find(node); if(!treeItem) return; // return because there is no treeitem containing this node TreeItem* parentTreeItem = treeItem->GetParent(); QModelIndex parentIndex = this->IndexFromTreeItem(parentTreeItem); // emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model) this->beginRemoveRows(parentIndex, treeItem->GetIndex(), treeItem->GetIndex()); // remove node std::vector children = treeItem->GetChildren(); delete treeItem; // emit endRemoveRows event endRemoveRows(); // move all children of deleted node into its parent for ( std::vector::iterator it = children.begin() ; it != children.end(); it++) { // emit beginInsertRows event beginInsertRows(parentIndex, parentTreeItem->GetChildCount(), parentTreeItem->GetChildCount()); // add nodes again parentTreeItem->AddChild(*it); // emit endInsertRows event endInsertRows(); } this->AdjustLayerProperty(); } void QmitkDataStorageTreeModel::RemoveNode( const mitk::DataNode* node ) { if (node == 0) return; //Removing Observer bool isHelperObject (false); NodeTagMapType::iterator searchIter = m_HelperObjectObserverTags.find( const_cast(node) ); if (node->GetBoolProperty("helper object", isHelperObject) && searchIter != m_HelperObjectObserverTags.end()) { (*searchIter).first->GetProperty("helper object")->RemoveObserver( (*searchIter).second ); m_HelperObjectObserverTags.erase(const_cast(node)); } this->RemoveNodeInternal(node); } void QmitkDataStorageTreeModel::SetNodeModified( const mitk::DataNode* node ) { TreeItem* treeItem = m_Root->Find(node); if(!treeItem) { // check if the node still fits the predicates if( m_Predicate->CheckNode( node ) ) { this->UpdateNodeVisibility(); } } else { TreeItem* parentTreeItem = treeItem->GetParent(); // as the root node should not be removed one should always have a parent item if(!parentTreeItem) return; QModelIndex index = this->createIndex(treeItem->GetIndex(), 0, treeItem); // now emit the dataChanged signal emit dataChanged(index, index); } } mitk::DataNode* QmitkDataStorageTreeModel::GetParentNode( const mitk::DataNode* node ) const { mitk::DataNode* dataNode = 0; mitk::DataStorage::SetOfObjects::ConstPointer _Sources = m_DataStorage->GetSources(node); if(_Sources->Size() > 0) dataNode = _Sources->front(); return dataNode; } bool QmitkDataStorageTreeModel::setData( const QModelIndex &index, const QVariant &value, int role ) { mitk::DataNode* dataNode = this->TreeItemFromIndex(index)->GetDataNode(); if(!dataNode) return false; if(role == Qt::EditRole && !value.toString().isEmpty()) { dataNode->SetStringProperty("name", value.toString().toStdString().c_str()); + + mitk::PlanarFigure* planarFigure = dynamic_cast(dataNode->GetData()); + + if (planarFigure != NULL) + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if(role == Qt::CheckStateRole) { // Please note: value.toInt() returns 2, independentely from the actual checkstate of the index element. // Therefore the checkstate is being estimated again here. QVariant qcheckstate = index.data(Qt::CheckStateRole); int checkstate = qcheckstate.toInt(); bool isVisible = bool(checkstate); dataNode->SetVisibility(!isVisible); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } // inform listeners about changes emit dataChanged(index, index); return true; } bool QmitkDataStorageTreeModel::setHeaderData( int /*section*/, Qt::Orientation /*orientation*/, const QVariant& /* value */, int /*role = Qt::EditRole*/ ) { return false; } void QmitkDataStorageTreeModel::AdjustLayerProperty() { /// transform the tree into an array and set the layer property descending std::vector vec; this->TreeToVector(m_Root, vec); int i = vec.size()-1; for(std::vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { mitk::DataNode::Pointer dataNode = (*it)->GetDataNode(); bool fixedLayer = false; if (!(dataNode->GetBoolProperty("fixedLayer", fixedLayer) && fixedLayer)) dataNode->SetIntProperty("layer", i); --i; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataStorageTreeModel::TreeToVector(TreeItem* parent, std::vector& vec) const { TreeItem* current; for(int i = 0; iGetChildCount(); ++i) { current = parent->GetChild(i); this->TreeToVector(current, vec); vec.push_back(current); } } QModelIndex QmitkDataStorageTreeModel::IndexFromTreeItem( TreeItem* item ) const { if(item == m_Root) return QModelIndex(); else return this->createIndex(item->GetIndex(), 0, item); } QList QmitkDataStorageTreeModel::GetNodeSet() const { QList res; if(m_Root) this->TreeToNodeSet(m_Root, res); return res; } void QmitkDataStorageTreeModel::TreeToNodeSet( TreeItem* parent, QList& vec ) const { TreeItem* current; for(int i = 0; iGetChildCount(); ++i) { current = parent->GetChild(i); vec.push_back(current->GetDataNode()); this->TreeToNodeSet(current, vec); } } QModelIndex QmitkDataStorageTreeModel::GetIndex( const mitk::DataNode* node ) const { if(m_Root) { TreeItem* item = m_Root->Find(node); if(item) return this->IndexFromTreeItem(item); } return QModelIndex(); } QmitkDataStorageTreeModel::TreeItem::TreeItem( mitk::DataNode* _DataNode, TreeItem* _Parent ) : m_Parent(_Parent) , m_DataNode(_DataNode) { if(m_Parent) m_Parent->AddChild(this); } QmitkDataStorageTreeModel::TreeItem::~TreeItem() { if(m_Parent) m_Parent->RemoveChild(this); } void QmitkDataStorageTreeModel::TreeItem::Delete() { while(m_Children.size() > 0) delete m_Children.back(); delete this; } QmitkDataStorageTreeModel::TreeItem* QmitkDataStorageTreeModel::TreeItem::Find( const mitk::DataNode* _DataNode ) const { QmitkDataStorageTreeModel::TreeItem* item = 0; if(_DataNode) { if(m_DataNode == _DataNode) item = const_cast(this); else { for(std::vector::const_iterator it = m_Children.begin(); it != m_Children.end(); ++it) { if(item) break; item = (*it)->Find(_DataNode); } } } return item; } int QmitkDataStorageTreeModel::TreeItem::IndexOfChild( const TreeItem* item ) const { std::vector::const_iterator it = std::find(m_Children.begin(), m_Children.end(), item); return it != m_Children.end() ? std::distance(m_Children.begin(), it): -1; } QmitkDataStorageTreeModel::TreeItem* QmitkDataStorageTreeModel::TreeItem::GetChild( int index ) const { return (m_Children.size() > 0 && index >= 0 && index < (int)m_Children.size())? m_Children.at(index): 0; } void QmitkDataStorageTreeModel::TreeItem::AddChild( TreeItem* item ) { this->InsertChild(item); } void QmitkDataStorageTreeModel::TreeItem::RemoveChild( TreeItem* item ) { std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), item); if(it != m_Children.end()) { m_Children.erase(it); item->SetParent(0); } } int QmitkDataStorageTreeModel::TreeItem::GetChildCount() const { return m_Children.size(); } int QmitkDataStorageTreeModel::TreeItem::GetIndex() const { if (m_Parent) return m_Parent->IndexOfChild(this); return 0; } QmitkDataStorageTreeModel::TreeItem* QmitkDataStorageTreeModel::TreeItem::GetParent() const { return m_Parent; } mitk::DataNode::Pointer QmitkDataStorageTreeModel::TreeItem::GetDataNode() const { return m_DataNode; } void QmitkDataStorageTreeModel::TreeItem::InsertChild( TreeItem* item, int index ) { std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), item); if(it == m_Children.end()) { if(m_Children.size() > 0 && index >= 0 && index < (int)m_Children.size()) { it = m_Children.begin(); std::advance(it, index); m_Children.insert(it, item); } else m_Children.push_back(item); // add parent if necessary if(item->GetParent() != this) item->SetParent(this); } } std::vector QmitkDataStorageTreeModel::TreeItem::GetChildren() const { return m_Children; } void QmitkDataStorageTreeModel::TreeItem::SetParent( TreeItem* _Parent ) { m_Parent = _Parent; if(m_Parent) m_Parent->AddChild(this); } void QmitkDataStorageTreeModel::SetShowHelperObjects(bool _ShowHelperObjects) { m_ShowHelperObjects = _ShowHelperObjects; this->UpdateNodeVisibility(); } void QmitkDataStorageTreeModel::SetShowNodesContainingNoData(bool _ShowNodesContainingNoData) { m_ShowNodesContainingNoData = _ShowNodesContainingNoData; this->UpdateNodeVisibility(); } void QmitkDataStorageTreeModel::UpdateNodeVisibility() { mitk::NodePredicateData::Pointer dataIsNull = mitk::NodePredicateData::New(0); mitk::NodePredicateNot::Pointer dataIsNotNull = mitk::NodePredicateNot::New(dataIsNull);// Show only nodes that really contain dat if (m_ShowHelperObjects) { if (m_ShowNodesContainingNoData) { // Show every node m_Predicate = mitk::NodePredicateOr::New(dataIsNull, dataIsNotNull); } else { // Show helper objects but not nodes containing no data m_Predicate = dataIsNotNull; } } else { mitk::NodePredicateProperty::Pointer isHelperObject = mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotHelperObject = mitk::NodePredicateNot::New(isHelperObject);// Show only nodes that are not helper objects if (m_ShowNodesContainingNoData) { // Don't show helper objects but nodes containing no data m_Predicate = isNotHelperObject; } else { // Don't show helper objects and nodes containing no data m_Predicate = mitk::NodePredicateAnd::New(isNotHelperObject, dataIsNotNull); } } this->Update(); } void QmitkDataStorageTreeModel::Update() { if (m_DataStorage.IsNotNull()) { this->reset(); mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = m_DataStorage->GetSubset(m_Predicate); for(mitk::DataStorage::SetOfObjects::const_iterator it=_NodeSet->begin(); it!=_NodeSet->end(); it++) { // save node this->AddNodeInternal(*it); } mitk::DataStorage::SetOfObjects::ConstPointer _NotNodeSet = m_DataStorage->GetSubset(mitk::NodePredicateNot::New(m_Predicate)); for(mitk::DataStorage::SetOfObjects::const_iterator it=_NotNodeSet->begin(); it!=_NotNodeSet->end(); it++) { // remove node this->RemoveNodeInternal(*it); } } } diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp index 3fab7a32bc..fe26d6c6c4 100644 --- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp @@ -1,493 +1,502 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPaintbrushTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "ipSegmentation.h" #include "mitkLevelWindowProperty.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) int mitk::PaintbrushTool::m_Size = 1; mitk::PaintbrushTool::PaintbrushTool(int paintingPixelValue) :FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"), m_PaintingPixelValue(paintingPixelValue), m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28) { // great magic numbers CONNECT_ACTION( 80, OnMousePressed ); CONNECT_ACTION( 90, OnMouseMoved ); CONNECT_ACTION( 42, OnMouseReleased ); CONNECT_ACTION( 49014, OnInvertLogic ); m_MasterContour = ContourModel::New(); m_MasterContour->Initialize(); m_CurrentPlane = NULL; m_WorkingNode = DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); } mitk::PaintbrushTool::~PaintbrushTool() { } void mitk::PaintbrushTool::Activated() { Superclass::Activated(); FeedbackContourTool::SetFeedbackContourVisible(true); SizeChanged.Send(m_Size); + m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified ); } void mitk::PaintbrushTool::Deactivated() { FeedbackContourTool::SetFeedbackContourVisible(false); if (m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); Superclass::Deactivated(); m_WorkingSlice = NULL; m_CurrentPlane = NULL; + m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate( this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified ); } void mitk::PaintbrushTool::SetSize(int value) { m_Size = value; } mitk::Point2D mitk::PaintbrushTool::upperLeft(mitk::Point2D p) { p[0] -= 0.5; p[1] += 0.5; return p; } void mitk::PaintbrushTool::UpdateContour(const StateEvent* stateEvent) { //MITK_INFO<<"Update..."; // examine stateEvent and create a contour that matches the pixel mask that we are going to draw const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; // Get Spacing of current Slice //mitk::Vector3D vSpacing = m_WorkingSlice->GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(); // // Draw a contour in Square according to selected brush size // int radius = (m_Size)/2; float fradius = static_cast(m_Size) / 2.0f; ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); // estimate center point of the brush ( relative to the pixel the mouse points on ) // -- left upper corner for even sizes, // -- midpoint for uneven sizes mitk::Point2D centerCorrection; centerCorrection.Fill(0); // even --> correction of [+0.5, +0.5] bool evenSize = ((m_Size % 2) == 0); if( evenSize ) { centerCorrection[0] += 0.5; centerCorrection[1] += 0.5; } // we will compute the control points for the upper left quarter part of a circle contour std::vector< mitk::Point2D > quarterCycleUpperRight; std::vector< mitk::Point2D > quarterCycleLowerRight; std::vector< mitk::Point2D > quarterCycleLowerLeft; std::vector< mitk::Point2D > quarterCycleUpperLeft; mitk::Point2D curPoint; bool curPointIsInside = true; curPoint[0] = 0; curPoint[1] = radius; quarterCycleUpperRight.push_back( upperLeft(curPoint) ); // to estimate if a pixel is inside the circle, we need to compare against the 'outer radius' // i.e. the distance from the midpoint [0,0] to the border of the pixel [0,radius] //const float outer_radius = static_cast(radius) + 0.5; while (curPoint[1] > 0) { // Move right until pixel is outside circle float curPointX_squared = 0.0f; float curPointY_squared = (curPoint[1] - centerCorrection[1] ) * (curPoint[1] - centerCorrection[1] ); while( curPointIsInside ) { // increment posX and chec curPoint[0]++; curPointX_squared = (curPoint[0] - centerCorrection[0] ) * (curPoint[0] - centerCorrection[0] ); const float len = sqrt( curPointX_squared + curPointY_squared); if ( len > fradius ) { // found first Pixel in this horizontal line, that is outside the circle curPointIsInside = false; } } quarterCycleUpperRight.push_back( upperLeft(curPoint) ); // Move down until pixel is inside circle while( !curPointIsInside ) { // increment posX and chec curPoint[1]--; curPointY_squared = (curPoint[1] - centerCorrection[1] ) * (curPoint[1] - centerCorrection[1] ); const float len = sqrt( curPointX_squared + curPointY_squared); if ( len <= fradius ) { // found first Pixel in this horizontal line, that is outside the circle curPointIsInside = true; quarterCycleUpperRight.push_back( upperLeft(curPoint) ); } // Quarter cycle is full, when curPoint y position is 0 if (curPoint[1] <= 0) break; } } // QuarterCycle is full! Now copy quarter cycle to other quarters. if( !evenSize ) { std::vector< mitk::Point2D >::const_iterator it = quarterCycleUpperRight.begin(); while( it != quarterCycleUpperRight.end() ) { mitk::Point2D p; p = *it; // the contour points in the lower right corner have same position but with negative y values p[1] *= -1; quarterCycleLowerRight.push_back(p); // the contour points in the lower left corner have same position // but with both x,y negative p[0] *= -1; quarterCycleLowerLeft.push_back(p); // the contour points in the upper left corner have same position // but with x negative p[1] *= -1; quarterCycleUpperLeft.push_back(p); it++; } } else { std::vector< mitk::Point2D >::const_iterator it = quarterCycleUpperRight.begin(); while( it != quarterCycleUpperRight.end() ) { mitk::Point2D p,q; p = *it; q = p; // the contour points in the lower right corner have same position but with negative y values q[1] *= -1; // correct for moved offset if size even = the midpoint is not the midpoint of the current pixel // but its upper rigt corner q[1] += 1; quarterCycleLowerRight.push_back(q); q = p; // the contour points in the lower left corner have same position // but with both x,y negative q[1] = -1.0f * q[1] + 1; q[0] = -1.0f * q[0] + 1; quarterCycleLowerLeft.push_back(q); // the contour points in the upper left corner have same position // but with x negative q = p; q[0] *= -1; q[0] += 1; quarterCycleUpperLeft.push_back(q); it++; } } // fill contour with poins in right ordering, starting with the upperRight block mitk::Point3D tempPoint; for (unsigned int i=0; iAddVertex( tempPoint ); } // the lower right has to be parsed in reverse order for (int i=quarterCycleLowerRight.size()-1; i>=0; i--) { tempPoint[0] = quarterCycleLowerRight[i][0]; tempPoint[1] = quarterCycleLowerRight[i][1]; tempPoint[2] = 0; contourInImageIndexCoordinates->AddVertex( tempPoint ); } for (unsigned int i=0; iAddVertex( tempPoint ); } // the upper left also has to be parsed in reverse order for (int i=quarterCycleUpperLeft.size()-1; i>=0; i--) { tempPoint[0] = quarterCycleUpperLeft[i][0]; tempPoint[1] = quarterCycleUpperLeft[i][1]; tempPoint[2] = 0; contourInImageIndexCoordinates->AddVertex( tempPoint ); } m_MasterContour = contourInImageIndexCoordinates; } /** Just show the contour, get one point as the central point and add surrounding points to the contour. */ bool mitk::PaintbrushTool::OnMousePressed (Action* action, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_MasterContour->SetIsClosed(true); return this->OnMouseMoved(action, stateEvent); } /** Insert the point to the feedback contour,finish to build the contour and at the same time the painting function */ bool mitk::PaintbrushTool::OnMouseMoved (Action* itkNotUsed(action), const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); CheckIfCurrentSliceHasChanged(positionEvent); if ( m_LastContourSize != m_Size ) { UpdateContour( stateEvent ); m_LastContourSize = m_Size; } bool leftMouseButtonPressed( stateEvent->GetId() == 530 || stateEvent->GetId() == 534 || stateEvent->GetId() == 1 || stateEvent->GetId() == 5 ); Point3D worldCoordinates = positionEvent->GetWorldPosition(); Point3D indexCoordinates; m_WorkingSlice->GetGeometry()->WorldToIndex( worldCoordinates, indexCoordinates ); MITK_DEBUG << "Mouse at W " << worldCoordinates << std::endl; MITK_DEBUG << "Mouse at I " << indexCoordinates << std::endl; // round to nearest voxel center (abort if this hasn't changed) if ( m_Size % 2 == 0 ) // even { indexCoordinates[0] = ROUND( indexCoordinates[0]);// /*+ 0.5*/) + 0.5; indexCoordinates[1] = ROUND( indexCoordinates[1]);// /*+ 0.5*/ ) + 0.5; } else // odd { indexCoordinates[0] = ROUND( indexCoordinates[0] ) ; indexCoordinates[1] = ROUND( indexCoordinates[1] ) ; } static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me if ( fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps || fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed ) { lastPos = indexCoordinates; } else { MITK_DEBUG << "." << std::flush; return false; } MITK_DEBUG << "Mouse at C " << indexCoordinates; int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel::Pointer contour = ContourModel::New(); contour->Expand(timestep + 1); contour->SetIsClosed(true, timestep); ContourModel::VertexIterator it = m_MasterContour->Begin(); ContourModel::VertexIterator end = m_MasterContour->End(); while(it != end) { Point3D point = (*it)->Coordinates; point[0] += indexCoordinates[ 0 ]; point[1] += indexCoordinates[ 1 ]; contour->AddVertex( point, timestep ); it++; } if (leftMouseButtonPressed) { FeedbackContourTool::FillContourInSlice( contour, timestep, m_WorkingSlice, m_PaintingPixelValue ); m_WorkingNode->SetData(m_WorkingSlice); m_WorkingNode->Modified(); } // visualize contour ContourModel::Pointer displayContour = ContourModel::New(); displayContour->Initialize(); //for (unsigned int index = 0; index < contour->GetNumberOfPoints(); ++index) //{ // Point3D point = contour->GetPoints()->ElementAt(index); // if ( m_Size % 2 == 0 ) // even // { // point[0] += 0.5; // point[1] += 0.5; // } // displayContour->AddVertex( point ); //} displayContour = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), /*displayContour*/contour ); SetFeedbackContour( *displayContour ); assert( positionEvent->GetSender()->GetRenderWindow() ); RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::PaintbrushTool::OnMouseReleased(Action* /*action*/, const StateEvent* stateEvent) { //When mouse is released write segmentationresult back into image const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice->Clone()); return true; } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ bool mitk::PaintbrushTool::OnInvertLogic(Action* itkNotUsed(action), const StateEvent* /*stateEvent*/) { // Inversion only for 0 and 1 as painting values if (m_PaintingPixelValue == 1) { m_PaintingPixelValue = 0; FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } else if (m_PaintingPixelValue == 0) { m_PaintingPixelValue = 1; FeedbackContourTool::SetFeedbackContourColorDefault(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } void mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const PositionEvent *event) { const PlaneGeometry* planeGeometry( dynamic_cast (event->GetSender()->GetCurrentWorldGeometry2D() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image::Pointer image = dynamic_cast(workingNode->GetData()); if ( !image || !planeGeometry ) return; - if(m_CurrentPlane.IsNull()) + if(m_CurrentPlane.IsNull() || m_WorkingSlice.IsNull()) { m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode->ReplaceProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetData(m_WorkingSlice); } else { bool isSameSlice (false); isSameSlice = mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix()); isSameSlice = mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()); if (!isSameSlice) { m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_CurrentPlane = NULL; m_WorkingSlice = NULL; m_WorkingNode = NULL; m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode = mitk::DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetData(m_WorkingSlice); //So that the paintbrush contour vanished in the previous render window RenderingManager::GetInstance()->RequestUpdateAll(); } } if(!m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) { m_WorkingNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetProperty( "name", mitk::StringProperty::New("Paintbrush_Node") ); m_WorkingNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_WorkingNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_WorkingNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_ToolManager->GetDataStorage()->Add(m_WorkingNode); } } + +void mitk::PaintbrushTool::OnToolManagerWorkingDataModified() +{ + //Here we simply set the current working slice to null. The next time the mouse is moved + //within a renderwindow a new slice will be extracted from the new working data + m_WorkingSlice = 0; +} diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.h b/Modules/Segmentation/Interactions/mitkPaintbrushTool.h index 2ec21aeb31..35e7976d3d 100644 --- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.h +++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.h @@ -1,101 +1,103 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPaintbrushTool_h_Included #define mitkPaintbrushTool_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkFeedbackContourTool.h" #include "mitkPointSet.h" #include "mitkPointOperation.h" #include "mitkLegacyAdaptors.h" namespace mitk { /** \brief Paintbrush tool for InteractiveSegmentation \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Simple paintbrush drawing tool. Right now there are only circular pens of varying size. \warning Only to be instantiated by mitk::ToolManager. $Author: maleike $ */ class Segmentation_EXPORT PaintbrushTool : public FeedbackContourTool { public: // sent when the pen size is changed or should be updated in a GUI. Message1 SizeChanged; mitkClassMacro(PaintbrushTool, FeedbackContourTool); void SetSize(int value); protected: PaintbrushTool(int paintingPixelValue = 1); // purposely hidden virtual ~PaintbrushTool(); virtual void Activated(); virtual void Deactivated(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); /** * \todo This is a possible place where to introduce * different types of pens */ void UpdateContour(const StateEvent* stateEvent); /** * Little helper function. Returns the upper left corner of the given pixel. */ mitk::Point2D upperLeft(mitk::Point2D p); /** * Checks if the current slice has changed */ void CheckIfCurrentSliceHasChanged(const PositionEvent* event); + void OnToolManagerWorkingDataModified(); + int m_PaintingPixelValue; static int m_Size; ContourModel::Pointer m_MasterContour; int m_LastContourSize; Image::Pointer m_WorkingSlice; PlaneGeometry::Pointer m_CurrentPlane; DataNode::Pointer m_WorkingNode; }; } // namespace #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp index 6de4e86bb8..043c091f28 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp @@ -1,1078 +1,1082 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkSlicesInterpolator.h" #include "QmitkStdMultiWidget.h" #include "QmitkSelectableGLWidget.h" #include "mitkToolManager.h" #include "mitkDataNodeFactory.h" #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkProgressBar.h" #include "mitkGlobalInteraction.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkInteractionConst.h" #include "mitkApplyDiffImageOperation.h" #include "mitkDiffImageApplier.h" #include "mitkSegTool2D.h" #include "mitkCoreObjectFactory.h" #include "mitkSurfaceToImageFilter.h" #include "mitkSliceNavigationController.h" #include #include #include #include #include #include #include #include #include #include //#define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) const std::map QmitkSlicesInterpolator::createActionToSliceDimension() { std::map actionToSliceDimension; foreach(mitk::SliceNavigationController* slicer, m_ControllerToDeleteObserverTag.keys()) { actionToSliceDimension[new QAction(QString::fromStdString(slicer->GetViewDirectionAsString()),0)] = slicer; } return actionToSliceDimension; } QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget* parent, const char* /*name*/) :QWidget(parent), // ACTION_TO_SLICEDIMENSION( createActionToSliceDimension() ), m_Interpolator( mitk::SegmentationInterpolationController::New() ), m_SurfaceInterpolator(mitk::SurfaceInterpolationController::GetInstance()), m_ToolManager(NULL), m_Initialized(false), m_LastSNC(0), m_LastSliceIndex(0), m_2DInterpolationEnabled(false), m_3DInterpolationEnabled(false) { m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this); QVBoxLayout* vboxLayout = new QVBoxLayout(m_GroupBoxEnableExclusiveInterpolationMode); m_CmbInterpolation = new QComboBox(m_GroupBoxEnableExclusiveInterpolationMode); m_CmbInterpolation->addItem("Disabled"); m_CmbInterpolation->addItem("2-Dimensional"); m_CmbInterpolation->addItem("3-Dimensional"); vboxLayout->addWidget(m_CmbInterpolation); m_BtnApply2D = new QPushButton("Confirm for single slice", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApply2D); m_BtnApplyForAllSlices2D = new QPushButton("Confirm for all slices", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApplyForAllSlices2D); m_BtnApply3D = new QPushButton("Confirm", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApply3D); m_ChkShowPositionNodes = new QCheckBox("Show Position Nodes", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_ChkShowPositionNodes); this->HideAllInterpolationControls(); connect(m_CmbInterpolation, SIGNAL(currentIndexChanged(int)), this, SLOT(OnInterpolationMethodChanged(int))); connect(m_BtnApply2D, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked())); connect(m_BtnApplyForAllSlices2D, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked())); connect(m_BtnApply3D, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked())); connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool))); QHBoxLayout* layout = new QHBoxLayout(this); layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode); this->setLayout(layout); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged ); InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver( itk::ModifiedEvent(), command ); itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged ); SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver( itk::ModifiedEvent(), command2 ); // feedback node and its visualization properties m_FeedbackNode = mitk::DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties( m_FeedbackNode ); m_FeedbackNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "color", mitk::ColorProperty::New(255.0, 255.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", mitk::BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", mitk::IntProperty::New( 20 ) ); m_FeedbackNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_FeedbackNode->SetProperty( "name", mitk::StringProperty::New("Interpolation feedback") ); m_FeedbackNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_FeedbackNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode = mitk::DataNode::New(); m_InterpolatedSurfaceNode->SetProperty( "color", mitk::ColorProperty::New(255.0,255.0,0.0) ); m_InterpolatedSurfaceNode->SetProperty( "name", mitk::StringProperty::New("Surface Interpolation feedback") ); m_InterpolatedSurfaceNode->SetProperty( "opacity", mitk::FloatProperty::New(0.5) ); m_InterpolatedSurfaceNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_InterpolatedSurfaceNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode = mitk::DataNode::New(); m_3DContourNode->SetProperty( "color", mitk::ColorProperty::New(0.0, 0.0, 0.0) ); m_3DContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "name", mitk::StringProperty::New("Drawn Contours") ); m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); QWidget::setContentsMargins(0, 0, 0, 0); if ( QWidget::layout() != NULL ) { QWidget::layout()->setContentsMargins(0, 0, 0, 0); } //For running 3D Interpolation in background // create a QFuture and a QFutureWatcher connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); } void QmitkSlicesInterpolator::SetDataStorage( mitk::DataStorage::Pointer storage ) { m_DataStorage = storage; m_SurfaceInterpolator->SetDataStorage(storage); } mitk::DataStorage* QmitkSlicesInterpolator::GetDataStorage() { if ( m_DataStorage.IsNotNull() ) { return m_DataStorage; } else { return NULL; } } void QmitkSlicesInterpolator::Initialize(mitk::ToolManager* toolManager, const QList &controllers) { Q_ASSERT(!controllers.empty()); if (m_Initialized) { // remove old observers Uninitialize(); } m_ToolManager = toolManager; if (m_ToolManager) { // set enabled only if a segmentation is selected mitk::DataNode* node = m_ToolManager->GetWorkingData(0); QWidget::setEnabled( node != NULL ); // react whenever the set of selected segmentation changes m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); // connect to the slice navigation controller. after each change, call the interpolator foreach(mitk::SliceNavigationController* slicer, controllers) { //Has to be initialized m_LastSNC = slicer; m_TimeStep.insert(slicer, slicer->GetTime()->GetPos()); itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted); m_ControllerToDeleteObserverTag.insert(slicer, slicer->AddObserver(itk::DeleteEvent(), deleteCommand)); itk::MemberCommand::Pointer timeChangedCommand = itk::MemberCommand::New(); timeChangedCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTimeChanged); m_ControllerToTimeObserverTag.insert(slicer, slicer->AddObserver(mitk::SliceNavigationController::TimeGeometryEvent(NULL,0), timeChangedCommand)); itk::MemberCommand::Pointer sliceChangedCommand = itk::MemberCommand::New(); sliceChangedCommand->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSliceChanged); m_ControllerToSliceObserverTag.insert(slicer, slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(NULL,0), sliceChangedCommand)); } ACTION_TO_SLICEDIMENSION = createActionToSliceDimension(); } m_Initialized = true; } void QmitkSlicesInterpolator::Uninitialize() { if (m_ToolManager.IsNotNull()) { m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified); m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate(this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified); } foreach(mitk::SliceNavigationController* slicer, m_ControllerToSliceObserverTag.keys()) { slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer)); slicer->RemoveObserver(m_ControllerToTimeObserverTag.take(slicer)); slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer)); } ACTION_TO_SLICEDIMENSION.clear(); m_ToolManager = NULL; m_Initialized = false; } QmitkSlicesInterpolator::~QmitkSlicesInterpolator() { if (m_Initialized) { // remove old observers Uninitialize(); } if(m_DataStorage->Exists(m_3DContourNode)) m_DataStorage->Remove(m_3DContourNode); if(m_DataStorage->Exists(m_InterpolatedSurfaceNode)) m_DataStorage->Remove(m_InterpolatedSurfaceNode); // remove observer m_Interpolator->RemoveObserver( InterpolationInfoChangedObserverTag ); m_SurfaceInterpolator->RemoveObserver( SurfaceInterpolationInfoChangedObserverTag ); delete m_Timer; } /** External enableization... */ void QmitkSlicesInterpolator::setEnabled( bool enable ) { QWidget::setEnabled(enable); //Set the gui elements of the different interpolation modi enabled if (enable) { if (m_2DInterpolationEnabled) { this->Show2DInterpolationControls(true); m_Interpolator->Activate2DInterpolation(true); } else if (m_3DInterpolationEnabled) { this->Show3DInterpolationControls(true); this->Show3DInterpolationResult(true); } } //Set all gui elements of the interpolation disabled else { this->HideAllInterpolationControls(); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status) { OnInterpolationActivated(status); m_Interpolator->Activate2DInterpolation(status); } void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status) { On3DInterpolationActivated(status); } void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status) { if (status) { OnInterpolationActivated(!status); On3DInterpolationActivated(!status); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::HideAllInterpolationControls() { this->Show2DInterpolationControls(false); this->Show3DInterpolationControls(false); } void QmitkSlicesInterpolator::Show2DInterpolationControls(bool show) { m_BtnApply2D->setVisible(show); m_BtnApplyForAllSlices2D->setVisible(show); } void QmitkSlicesInterpolator::Show3DInterpolationControls(bool show) { m_BtnApply3D->setVisible(show); m_ChkShowPositionNodes->setVisible(show); } void QmitkSlicesInterpolator::OnInterpolationMethodChanged(int index) { switch(index) { case 0: // Disabled m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation"); this->HideAllInterpolationControls(); this->OnInterpolationActivated(false); this->On3DInterpolationActivated(false); this->Show3DInterpolationResult(false); break; case 1: // 2D m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)"); this->HideAllInterpolationControls(); this->Show2DInterpolationControls(true); this->OnInterpolationActivated(true); this->On3DInterpolationActivated(false); m_Interpolator->Activate2DInterpolation(true); break; case 2: // 3D m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)"); this->HideAllInterpolationControls(); this->Show3DInterpolationControls(true); this->OnInterpolationActivated(false); this->On3DInterpolationActivated(true); break; default: MITK_ERROR << "Unknown interpolation method!"; m_CmbInterpolation->setCurrentIndex(0); break; } } void QmitkSlicesInterpolator::OnShowMarkers(bool state) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); } } void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified() { if (m_ToolManager->GetWorkingData(0) != 0) { m_Segmentation = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); } else { //If no workingdata is set, remove the interpolation feedback this->GetDataStorage()->Remove(m_FeedbackNode); m_FeedbackNode->SetData(NULL); + this->GetDataStorage()->Remove(m_3DContourNode); + m_3DContourNode->SetData(NULL); + this->GetDataStorage()->Remove(m_InterpolatedSurfaceNode); + m_InterpolatedSurfaceNode->SetData(NULL); return; } //Updating the current selected segmentation for the 3D interpolation SetCurrentContourListID(); if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } this->CheckSupportedImageDimension(); } void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified() { } void QmitkSlicesInterpolator::OnTimeChanged(itk::Object* sender, const itk::EventObject& e) { //Check if we really have a GeometryTimeEvent if (!dynamic_cast(&e)) return; mitk::SliceNavigationController* slicer = dynamic_cast(sender); Q_ASSERT(slicer); m_TimeStep[slicer]/* = event.GetPos()*/; //TODO Macht das hier wirklich Sinn???? if (m_LastSNC == slicer) { slicer->SendSlice();//will trigger a new interpolation } } void QmitkSlicesInterpolator::OnSliceChanged(itk::Object *sender, const itk::EventObject &e) { //Check whether we really have a GeometrySliceEvent if (!dynamic_cast(&e)) return; mitk::SliceNavigationController* slicer = dynamic_cast(sender); if (TranslateAndInterpolateChangedSlice(e, slicer)) { slicer->GetRenderer()->RequestUpdate(); } } bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject& e, mitk::SliceNavigationController* slicer) { if (!m_2DInterpolationEnabled) return false; try { const mitk::SliceNavigationController::GeometrySliceEvent& event = dynamic_cast(e); mitk::TimeGeometry* tsg = event.GetTimeGeometry(); if (tsg && m_TimeStep.contains(slicer)) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(tsg->GetGeometryForTimeStep(m_TimeStep[slicer]).GetPointer()); if (slicedGeometry) { m_LastSNC = slicer; mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetGeometry2D( event.GetPos() )); if (plane) Interpolate( plane, m_TimeStep[slicer], slicer ); return true; } } } catch(std::bad_cast) { return false; // so what } return false; } void QmitkSlicesInterpolator::Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep, mitk::SliceNavigationController* slicer ) { if (m_ToolManager) { mitk::DataNode* node = m_ToolManager->GetWorkingData(0); if (node) { m_Segmentation = dynamic_cast(node->GetData()); if (m_Segmentation) { int clickedSliceDimension(-1); int clickedSliceIndex(-1); // calculate real slice position, i.e. slice of the image and not slice of the TimeSlicedGeometry mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex ); mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( clickedSliceDimension, clickedSliceIndex, plane, timeStep ); m_FeedbackNode->SetData( interpolation ); m_LastSNC = slicer; m_LastSliceIndex = clickedSliceIndex; } } } } void QmitkSlicesInterpolator::OnSurfaceInterpolationFinished() { mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if(interpolatedSurface.IsNotNull() && workingNode && workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) { m_BtnApply3D->setEnabled(true); m_InterpolatedSurfaceNode->SetData(interpolatedSurface); m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface()); this->Show3DInterpolationResult(true); if( !m_DataStorage->Exists(m_InterpolatedSurfaceNode) && !m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Add(m_3DContourNode); m_DataStorage->Add(m_InterpolatedSurfaceNode); } } else if (interpolatedSurface.IsNull()) { m_BtnApply3D->setEnabled(false); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { this->Show3DInterpolationResult(false); } } foreach (mitk::SliceNavigationController* slicer, m_ControllerToTimeObserverTag.keys()) { slicer->GetRenderer()->RequestUpdate(); } } void QmitkSlicesInterpolator::OnAcceptInterpolationClicked() { if (m_Segmentation && m_FeedbackNode->GetData()) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // Set slice as input mitk::Image::Pointer slice = dynamic_cast(m_FeedbackNode->GetData()); reslice->SetInputSlice(slice->GetSliceData()->GetVtkImageData(slice)); //set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( m_Segmentation ); unsigned int timestep = m_LastSNC->GetTime()->GetPos(); extractor->SetTimeStep( timestep ); extractor->SetWorldGeometry( m_LastSNC->GetCurrentPlaneGeometry() ); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry( m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep( timestep ) ); extractor->Modified(); extractor->Update(); //the image was modified within the pipeline, but not marked so m_Segmentation->Modified(); m_Segmentation->GetVtkImageData()->Modified(); m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::AcceptAllInterpolations(mitk::SliceNavigationController* slicer) { /* * What exactly is done here: * 1. We create an empty diff image for the current segmentation * 2. All interpolated slices are written into the diff image * 3. Then the diffimage is applied to the original segmentation */ if (m_Segmentation) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); mitk::Image::Pointer image3D = m_Segmentation; unsigned int timeStep( slicer->GetTime()->GetPos() ); if (m_Segmentation->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput( m_Segmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->Update(); image3D = timeSelector->GetOutput(); } // create a empty diff image for the undo operation mitk::Image::Pointer diffImage = mitk::Image::New(); diffImage->Initialize( image3D ); // Set all pixels to zero mitk::PixelType pixelType( mitk::MakeScalarPixelType() ); memset( diffImage->GetData(), 0, (pixelType.GetBpe() >> 3) * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2) ); // Since we need to shift the plane it must be clone so that the original plane isn't altered mitk::PlaneGeometry::Pointer reslicePlane = slicer->GetCurrentPlaneGeometry()->Clone(); int sliceDimension(-1); int sliceIndex(-1); mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, reslicePlane, sliceDimension, sliceIndex ); unsigned int zslices = m_Segmentation->GetDimension( sliceDimension ); mitk::ProgressBar::GetInstance()->AddStepsToDo(zslices); mitk::Point3D origin = reslicePlane->GetOrigin(); unsigned int totalChangedSlices(0); for (unsigned int sliceIndex = 0; sliceIndex < zslices; ++sliceIndex) { // Transforming the current origin of the reslice plane // so that it matches the one of the next slice m_Segmentation->GetSlicedGeometry()->WorldToIndex(origin, origin); origin[sliceDimension] = sliceIndex; m_Segmentation->GetSlicedGeometry()->IndexToWorld(origin, origin); reslicePlane->SetOrigin(origin); //Set the slice as 'input' mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( sliceDimension, sliceIndex, reslicePlane, timeStep ); if (interpolation.IsNotNull()) // we don't check if interpolation is necessary/sensible - but m_Interpolator does { //Setting up the reslicing pipeline which allows us to write the interpolation results back into //the image volume vtkSmartPointer reslice = vtkSmartPointer::New(); //set overwrite mode to true to write back to the image volume reslice->SetInputSlice(interpolation->GetSliceData()->GetVtkImageData(interpolation)); reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer diffslicewriter = mitk::ExtractSliceFilter::New(reslice); diffslicewriter->SetInput( diffImage ); diffslicewriter->SetTimeStep( timeStep ); diffslicewriter->SetWorldGeometry(reslicePlane); diffslicewriter->SetVtkOutputRequest(true); diffslicewriter->SetResliceTransformByGeometry( diffImage->GetTimeGeometry()->GetGeometryForTimeStep( timeStep ) ); diffslicewriter->Modified(); diffslicewriter->Update(); ++totalChangedSlices; } mitk::ProgressBar::GetInstance()->Progress(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); if (totalChangedSlices > 0) { // store undo stack items if ( true ) { // create do/undo operations mitk::ApplyDiffImageOperation* doOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); mitk::ApplyDiffImageOperation* undoOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); undoOp->SetFactor( -1.0 ); std::stringstream comment; comment << "Confirm all interpolations (" << totalChangedSlices << ")"; mitk::OperationEvent* undoStackItem = new mitk::OperationEvent( mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment.str() ); mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); // acutally apply the changes here to the original image mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation( doOp ); } } m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::FinishInterpolation(mitk::SliceNavigationController* slicer) { //this redirect is for calling from outside if (slicer == NULL) OnAcceptAllInterpolationsClicked(); else AcceptAllInterpolations( slicer ); } void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked() { QMenu orientationPopup(this); std::map::const_iterator it; for(it = ACTION_TO_SLICEDIMENSION.begin(); it != ACTION_TO_SLICEDIMENSION.end(); it++) orientationPopup.addAction(it->first); connect( &orientationPopup, SIGNAL(triggered(QAction*)), this, SLOT(OnAcceptAllPopupActivated(QAction*)) ); orientationPopup.exec( QCursor::pos() ); } void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked() { if (m_InterpolatedSurfaceNode.IsNotNull() && m_InterpolatedSurfaceNode->GetData()) { mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(dynamic_cast(m_InterpolatedSurfaceNode->GetData())); // check if ToolManager holds valid ReferenceData if (m_ToolManager->GetReferenceData(0) == NULL || m_ToolManager->GetWorkingData(0) == NULL) { return; } s2iFilter->SetImage(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); s2iFilter->Update(); mitk::DataNode* segmentationNode = m_ToolManager->GetWorkingData(0); segmentationNode->SetData(s2iFilter->GetOutput()); m_CmbInterpolation->setCurrentIndex(0); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction* action) { try { std::map::const_iterator iter = ACTION_TO_SLICEDIMENSION.find( action ); if (iter != ACTION_TO_SLICEDIMENSION.end()) { mitk::SliceNavigationController* slicer = iter->second; AcceptAllInterpolations( slicer ); } } catch(...) { /* Showing message box with possible memory error */ QMessageBox errorInfo; errorInfo.setWindowTitle("Interpolation Process"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!"); errorInfo.exec(); //additional error message on std::cerr std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl; } } void QmitkSlicesInterpolator::OnInterpolationActivated(bool on) { m_2DInterpolationEnabled = on; try { if ( m_DataStorage.IsNotNull() ) { if (on && !m_DataStorage->Exists(m_FeedbackNode)) { m_DataStorage->Add( m_FeedbackNode ); } } } catch(...) { // don't care (double add/remove) } if (m_ToolManager) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); QWidget::setEnabled( workingNode != NULL ); m_BtnApply2D->setEnabled( on ); m_FeedbackNode->SetVisibility( on ); if (!on) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } if (workingNode) { mitk::Image* segmentation = dynamic_cast(workingNode->GetData()); if (segmentation) { m_Interpolator->SetSegmentationVolume( segmentation ); if (referenceNode) { mitk::Image* referenceImage = dynamic_cast(referenceNode->GetData()); m_Interpolator->SetReferenceVolume( referenceImage ); // may be NULL } } } } UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::Run3DInterpolation() { m_SurfaceInterpolator->Interpolate(); } void QmitkSlicesInterpolator::StartUpdateInterpolationTimer() { m_Timer->start(500); } void QmitkSlicesInterpolator::StopUpdateInterpolationTimer() { m_Timer->stop(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,0.0)); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::ChangeSurfaceColor() { float currentColor[3]; m_InterpolatedSurfaceNode->GetColor(currentColor); float yellow[3] = {255.0,255.0,0.0}; if( currentColor[2] == yellow[2]) { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,255.0)); } else { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(yellow)); } m_InterpolatedSurfaceNode->Update(); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on) { m_3DInterpolationEnabled = on; this->CheckSupportedImageDimension(); try { if ( m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); if ((workingNode->IsSelected() && workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) && !isInterpolationResult && m_3DInterpolationEnabled) { int ret = QMessageBox::Yes; if (m_SurfaceInterpolator->EstimatePortionOfNeededMemory() > 0.5) { QMessageBox msgBox; msgBox.setText("Due to short handed system memory the 3D interpolation may be very slow!"); msgBox.setInformativeText("Are you sure you want to activate the 3D interpolation?"); msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); ret = msgBox.exec(); } if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); if (ret == QMessageBox::Yes) { m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } else { m_CmbInterpolation->setCurrentIndex(0); } } else if (!m_3DInterpolationEnabled) { this->Show3DInterpolationResult(false); m_BtnApply3D->setEnabled(m_3DInterpolationEnabled); } } else { QWidget::setEnabled( false ); m_ChkShowPositionNodes->setEnabled(m_3DInterpolationEnabled); } } if (!m_3DInterpolationEnabled) { this->Show3DInterpolationResult(false); m_BtnApply3D->setEnabled(m_3DInterpolationEnabled); } } catch(...) { MITK_ERROR<<"Error with 3D surface interpolation!"; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::EnableInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated OnInterpolationActivated(on); } void QmitkSlicesInterpolator::Enable3DInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated On3DInterpolationActivated(on); } void QmitkSlicesInterpolator::UpdateVisibleSuggestion() { if (m_2DInterpolationEnabled && m_LastSNC) { // determine which one is the current view, try to do an initial interpolation mitk::BaseRenderer* renderer = m_LastSNC->GetRenderer(); if (renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { const mitk::TimeGeometry* timeGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeGeometry) { mitk::SliceNavigationController::GeometrySliceEvent event( const_cast(timeGeometry), renderer->GetSlice() ); TranslateAndInterpolateChangedSlice(event, m_LastSNC); } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject& /*e*/) { // something (e.g. undo) changed the interpolation info, we should refresh our display UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject& /*e*/) { if(m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } void QmitkSlicesInterpolator:: SetCurrentContourListID() { // New ContourList = hide current interpolation Show3DInterpolationResult(false); if ( m_DataStorage.IsNotNull() && m_ToolManager && m_LastSNC ) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); bool isVisible (workingNode->IsVisible(m_LastSNC->GetRenderer())); if (isVisible && !isInterpolationResult) { QWidget::setEnabled( true ); //TODO Aufruf hier pruefen! mitk::Vector3D spacing = workingNode->GetData()->GetGeometry( m_LastSNC->GetTime()->GetPos() )->GetSpacing(); double minSpacing (100); double maxSpacing (0); for (int i =0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } else if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } m_SurfaceInterpolator->SetSegmentationImage(dynamic_cast(workingNode->GetData())); m_SurfaceInterpolator->SetMaxSpacing(maxSpacing); m_SurfaceInterpolator->SetMinSpacing(minSpacing); m_SurfaceInterpolator->SetDistanceImageVolume(50000); mitk::Image* segmentationImage = dynamic_cast(workingNode->GetData()); if (segmentationImage->GetDimension() == 3) m_SurfaceInterpolator->SetCurrentSegmentationInterpolationList(segmentationImage); else MITK_INFO<<"3D Interpolation is only supported for 3D images at the moment!"; if (m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } } else { QWidget::setEnabled(false); } } } void QmitkSlicesInterpolator::Show3DInterpolationResult(bool status) { if (m_InterpolatedSurfaceNode.IsNotNull()) m_InterpolatedSurfaceNode->SetVisibility(status); if (m_3DContourNode.IsNotNull()) m_3DContourNode->SetVisibility(status, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::CheckSupportedImageDimension() { if (m_3DInterpolationEnabled && m_Segmentation->GetDimension() != 3) { QMessageBox info; info.setWindowTitle("3D Interpolation Process"); info.setIcon(QMessageBox::Information); info.setText("3D Interpolation is only supported for 3D images at the moment!"); info.exec(); m_CmbInterpolation->setCurrentIndex(0); } } void QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject& /*e*/) { //Don't know how to avoid const_cast here?! mitk::SliceNavigationController* slicer = dynamic_cast(const_cast(sender)); if (slicer) { m_ControllerToTimeObserverTag.remove(slicer); m_ControllerToSliceObserverTag.remove(slicer); m_ControllerToDeleteObserverTag.remove(slicer); } } diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.cpp b/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.cpp index 46cdc6b870..b17518a4fc 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.cpp +++ b/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.cpp @@ -1,345 +1,352 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include "mitkImageReadAccessor.h" #include #include #include #include #include #include #include #include #include #include mitk::ToFDistanceImageToSurfaceFilter::ToFDistanceImageToSurfaceFilter() : m_IplScalarImage(NULL), m_CameraIntrinsics(), m_TextureImageWidth(0), m_TextureImageHeight(0), m_InterPixelDistance(), m_TextureIndex(0), m_GenerateTriangularMesh(true), m_TriangulationThreshold(0.0) { m_InterPixelDistance.Fill(0.045); m_CameraIntrinsics = mitk::CameraIntrinsics::New(); m_CameraIntrinsics->SetFocalLength(273.138946533,273.485900879); m_CameraIntrinsics->SetPrincipalPoint(107.867935181,98.3807373047); m_CameraIntrinsics->SetDistorsionCoeffs(-0.486690014601f,0.553943634033f,0.00222016777843f,-0.00300851115026f); m_ReconstructionMode = WithInterPixelDistance; } mitk::ToFDistanceImageToSurfaceFilter::~ToFDistanceImageToSurfaceFilter() { } void mitk::ToFDistanceImageToSurfaceFilter::SetInput( Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics ) { this->SetCameraIntrinsics(cameraIntrinsics); this->SetInput(0,distanceImage); } void mitk::ToFDistanceImageToSurfaceFilter::SetInput( unsigned int idx, Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics ) { this->SetCameraIntrinsics(cameraIntrinsics); this->SetInput(idx,distanceImage); } void mitk::ToFDistanceImageToSurfaceFilter::SetInput( mitk::Image* distanceImage ) { this->SetInput(0,distanceImage); } void mitk::ToFDistanceImageToSurfaceFilter::SetInput( unsigned int idx, mitk::Image* distanceImage ) { if ((distanceImage == NULL) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to NULL, reduce the number of inputs by one this->SetNumberOfInputs(this->GetNumberOfInputs() - 1); else this->ProcessObject::SetNthInput(idx, distanceImage); // Process object is not const-correct so the const_cast is required here this->CreateOutputsForAllInputs(); } mitk::Image* mitk::ToFDistanceImageToSurfaceFilter::GetInput() { return this->GetInput(0); } mitk::Image* mitk::ToFDistanceImageToSurfaceFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) { mitkThrow() << "No input given for ToFDistanceImageToSurfaceFilter"; } return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx)); } void mitk::ToFDistanceImageToSurfaceFilter::GenerateData() { mitk::Surface::Pointer output = this->GetOutput(); assert(output); mitk::Image::Pointer input = this->GetInput(); assert(input); // mesh points int xDimension = input->GetDimension(0); int yDimension = input->GetDimension(1); unsigned int size = xDimension*yDimension; //size of the image-array std::vector isPointValid; isPointValid.resize(size); vtkSmartPointer points = vtkSmartPointer::New(); points->SetDataTypeToDouble(); vtkSmartPointer polys = vtkSmartPointer::New(); vtkSmartPointer vertices = vtkSmartPointer::New(); vtkSmartPointer scalarArray = vtkSmartPointer::New(); vtkSmartPointer textureCoords = vtkSmartPointer::New(); textureCoords->SetNumberOfComponents(2); textureCoords->Allocate(size); //Make a vtkIdList to save the ID's of the polyData corresponding to the image //pixel ID's. See below for more documentation. m_VertexIdList = vtkSmartPointer::New(); //Allocate the object once else it would automatically allocate new memory //for every vertex and perform a copy which is expensive. m_VertexIdList->Allocate(size); m_VertexIdList->SetNumberOfIds(size); for(unsigned int i = 0; i < size; ++i) { m_VertexIdList->SetId(i, 0); } float* scalarFloatData = NULL; if (this->m_IplScalarImage) // if scalar image is defined use it for texturing { scalarFloatData = (float*)this->m_IplScalarImage->imageData; } else if (this->GetInput(m_TextureIndex)) // otherwise use intensity image (input(2)) { ImageReadAccessor inputAcc(this->GetInput(m_TextureIndex)); scalarFloatData = (float*)inputAcc.GetData(); } ImageReadAccessor inputAcc(input, input->GetSliceData(0,0,0)); float* inputFloatData = (float*)inputAcc.GetData(); //calculate world coordinates mitk::ToFProcessingCommon::ToFPoint2D focalLengthInPixelUnits; mitk::ToFProcessingCommon::ToFScalarType focalLengthInMm; if((m_ReconstructionMode == WithOutInterPixelDistance) || (m_ReconstructionMode == Kinect)) { focalLengthInPixelUnits[0] = m_CameraIntrinsics->GetFocalLengthX(); focalLengthInPixelUnits[1] = m_CameraIntrinsics->GetFocalLengthY(); } else if( m_ReconstructionMode == WithInterPixelDistance) { //convert focallength from pixel to mm focalLengthInMm = (m_CameraIntrinsics->GetFocalLengthX()*m_InterPixelDistance[0]+m_CameraIntrinsics->GetFocalLengthY()*m_InterPixelDistance[1])/2.0; } mitk::ToFProcessingCommon::ToFPoint2D principalPoint; principalPoint[0] = m_CameraIntrinsics->GetPrincipalPointX(); principalPoint[1] = m_CameraIntrinsics->GetPrincipalPointY(); mitk::Point3D origin = input->GetGeometry()->GetOrigin(); + mitk::Vector3D spacing = input->GetGeometry()->GetSpacing(); for (int j=0; jInsertPoint(pixelID, cartesianCoordinates.GetDataPointer()). //If we use points->InsertNextPoint(...) instead, the ID's do not //correspond to the image pixel ID's. Thus, we have to save them //in the vertexIdList. m_VertexIdList->SetId(pixelID, points->InsertNextPoint(cartesianCoordinates.GetDataPointer())); if (m_GenerateTriangularMesh) { if((i >= 1) && (j >= 1)) { //This little piece of art explains the ID's: // // P(x_1y_1)---P(xy_1) // | | // | | // | | // P(x_1y)-----P(xy) // //We can only start triangulation if we are at vertex (1,1), //because we need the other 3 vertices near this one. //To go one pixel line back in the image array, we have to //subtract 1x xDimension. vtkIdType xy = pixelID; vtkIdType x_1y = pixelID-1; vtkIdType xy_1 = pixelID-xDimension; vtkIdType x_1y_1 = xy_1-1; //Find the corresponding vertex ID's in the saved vertexIdList: vtkIdType xyV = m_VertexIdList->GetId(xy); vtkIdType x_1yV = m_VertexIdList->GetId(x_1y); vtkIdType xy_1V = m_VertexIdList->GetId(xy_1); vtkIdType x_1y_1V = m_VertexIdList->GetId(x_1y_1); if (isPointValid[xy]&&isPointValid[x_1y]&&isPointValid[x_1y_1]&&isPointValid[xy_1]) // check if points of cell are valid { double pointXY[3], pointX_1Y[3], pointXY_1[3], pointX_1Y_1[3]; points->GetPoint(xyV, pointXY); points->GetPoint(x_1yV, pointX_1Y); points->GetPoint(xy_1V, pointXY_1); points->GetPoint(x_1y_1V, pointX_1Y_1); if( (mitk::Equal(m_TriangulationThreshold, 0.0)) || ((vtkMath::Distance2BetweenPoints(pointXY, pointX_1Y) <= m_TriangulationThreshold) && (vtkMath::Distance2BetweenPoints(pointXY, pointXY_1) <= m_TriangulationThreshold) && (vtkMath::Distance2BetweenPoints(pointX_1Y, pointX_1Y_1) <= m_TriangulationThreshold) && (vtkMath::Distance2BetweenPoints(pointXY_1, pointX_1Y_1) <= m_TriangulationThreshold))) { polys->InsertNextCell(3); polys->InsertCellPoint(x_1yV); polys->InsertCellPoint(xyV); polys->InsertCellPoint(x_1y_1V); polys->InsertNextCell(3); polys->InsertCellPoint(x_1y_1V); polys->InsertCellPoint(xyV); polys->InsertCellPoint(xy_1V); } else { //We dont want triangulation, but we want to keep the vertex vertices->InsertNextCell(1); vertices->InsertCellPoint(xyV); } } //Scalar values are necessary for mapping colors/texture onto the surface if (scalarFloatData) { scalarArray->InsertTuple1(m_VertexIdList->GetId(pixelID), scalarFloatData[pixelID]); } //These Texture Coordinates will map color pixel and vertices 1:1 (e.g. for Kinect). float xNorm = (((float)i)/xDimension);// correct video texture scale for kinect float yNorm = ((float)j)/yDimension; //don't flip. we don't need to flip. textureCoords->InsertTuple2(m_VertexIdList->GetId(pixelID), xNorm, yNorm); } } else { //We dont want triangulation, we only want vertices vertices->InsertNextCell(1); vertices->InsertCellPoint(m_VertexIdList->GetId(pixelID)); } } } } vtkSmartPointer mesh = vtkSmartPointer::New(); mesh->SetPoints(points); mesh->SetPolys(polys); mesh->SetVerts(vertices); //Pass the scalars to the polydata (if they were set). if (scalarArray->GetNumberOfTuples()>0) { mesh->GetPointData()->SetScalars(scalarArray); } //Pass the TextureCoords to the polydata anyway (to save them). mesh->GetPointData()->SetTCoords(textureCoords); output->SetVtkPolyData(mesh); } void mitk::ToFDistanceImageToSurfaceFilter::CreateOutputsForAllInputs() { this->SetNumberOfOutputs(this->GetNumberOfInputs()); // create outputs for all inputs for (unsigned int idx = 0; idx < this->GetNumberOfOutputs(); ++idx) if (this->GetOutput(idx) == NULL) { DataObjectPointer newOutput = this->MakeOutput(idx); this->SetNthOutput(idx, newOutput); } this->Modified(); } void mitk::ToFDistanceImageToSurfaceFilter::GenerateOutputInformation() { this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); } void mitk::ToFDistanceImageToSurfaceFilter::SetScalarImage(IplImage* iplScalarImage) { this->m_IplScalarImage = iplScalarImage; this->Modified(); } IplImage* mitk::ToFDistanceImageToSurfaceFilter::GetScalarImage() { return this->m_IplScalarImage; } void mitk::ToFDistanceImageToSurfaceFilter::SetTextureImageWidth(int width) { this->m_TextureImageWidth = width; } void mitk::ToFDistanceImageToSurfaceFilter::SetTextureImageHeight(int height) { this->m_TextureImageHeight = height; } void mitk::ToFDistanceImageToSurfaceFilter::SetTriangulationThreshold(double triangulationThreshold) { //vtkMath::Distance2BetweenPoints returns the squared distance between two points and //hence we square m_TriangulationThreshold in order to save run-time. this->m_TriangulationThreshold = triangulationThreshold*triangulationThreshold; } diff --git a/Plugins/org.mitk.gui.qt.properties/files.cmake b/Plugins/org.mitk.gui.qt.properties/files.cmake index 6a61ba0288..91f67d3f75 100644 --- a/Plugins/org.mitk.gui.qt.properties/files.cmake +++ b/Plugins/org.mitk.gui.qt.properties/files.cmake @@ -1,45 +1,49 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES org_mitk_gui_qt_properties_Activator.cpp QmitkPropertiesPreferencePage.cpp QmitkPropertyItemDelegate.cpp QmitkPropertyItem.cpp QmitkPropertyItemModel.cpp QmitkPropertyItemSortFilterProxyModel.cpp QmitkPropertyTreeView.cpp + QmitkAddNewPropertyDialog.cpp ) set(UI_FILES src/internal/QmitkPropertiesPreferencePage.ui src/internal/QmitkPropertyTreeView.ui + src/internal/QmitkAddNewPropertyDialog.ui ) set(MOC_H_FILES src/internal/QmitkPropertiesPreferencePage.h src/internal/org_mitk_gui_qt_properties_Activator.h src/internal/QmitkPropertyItemDelegate.h src/internal/QmitkPropertyItemModel.h src/internal/QmitkPropertyItemSortFilterProxyModel.h src/internal/QmitkPropertyTreeView.h + src/internal/QmitkAddNewPropertyDialog.h ) set(CACHED_RESOURCE_FILES resources/icon.png plugin.xml ) set(QRC_FILES + resources/Properties.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach() foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach() diff --git a/Plugins/org.mitk.gui.qt.properties/resources/Properties.qrc b/Plugins/org.mitk.gui.qt.properties/resources/Properties.qrc new file mode 100644 index 0000000000..2c45d2a2e6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.properties/resources/Properties.qrc @@ -0,0 +1,5 @@ + + + new.png + + diff --git a/Plugins/org.mitk.gui.qt.properties/resources/new.png b/Plugins/org.mitk.gui.qt.properties/resources/new.png new file mode 100644 index 0000000000..4c3efdd6fa Binary files /dev/null and b/Plugins/org.mitk.gui.qt.properties/resources/new.png differ diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.cpp b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.cpp new file mode 100644 index 0000000000..55e6f2c830 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.cpp @@ -0,0 +1,172 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkAddNewPropertyDialog.h" +#include +#include +#include + +QmitkAddNewPropertyDialog::QmitkAddNewPropertyDialog(mitk::DataNode::Pointer dataNode, mitk::BaseRenderer::Pointer renderer, QWidget* parent) + : QDialog(parent), + m_DataNode(dataNode), + m_Renderer(renderer) +{ + m_Controls.setupUi(this); + + QStringList types; + types << "bool" << "double" << "float" << "int" << "string"; + + m_Controls.typeComboBox->addItems(types); + + connect(m_Controls.typeComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(ShowAdequateValueWidget(const QString&))); + connect(m_Controls.addButton, SIGNAL(clicked()), this, SLOT(AddNewProperty())); + connect(m_Controls.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); + + this->ShowAdequateValueWidget(types[0]); +} + +QmitkAddNewPropertyDialog::~QmitkAddNewPropertyDialog() +{ +} + +void QmitkAddNewPropertyDialog::AddNewProperty() +{ + if (m_Controls.nameLineEdit->text().isEmpty()) + { + QMessageBox::critical(this, "No name specified", "Enter a property name."); + return; + } + + if (!this->ValidateValue()) + { + QMessageBox::critical(this, "Invalid value", "Enter a valid " + m_Controls.typeComboBox->currentText() + " value."); + return; + } + + m_DataNode->SetProperty(m_Controls.nameLineEdit->text().toAscii(), this->CreateProperty(), m_Renderer); + + this->accept(); +} + +mitk::BaseProperty::Pointer QmitkAddNewPropertyDialog::CreateProperty() const +{ + QString type = m_Controls.typeComboBox->currentText(); + + if (type == "bool") + { + return mitk::BoolProperty::New(m_Controls.valueCheckBox->isChecked()).GetPointer(); + } + else if (type == "double") + { + return mitk::DoubleProperty::New(m_Controls.valueLineEdit->text().toDouble()).GetPointer(); + } + else if (type == "float") + { + return mitk::FloatProperty::New(m_Controls.valueLineEdit->text().toFloat()).GetPointer(); + } + else if (type == "int") + { + return mitk::IntProperty::New(m_Controls.valueLineEdit->text().toInt()).GetPointer(); + } + else if (type == "string") + { + return mitk::StringProperty::New(m_Controls.valueLineEdit->text().toStdString()).GetPointer(); + } + else + { + assert(false && "Property creation for selected type not implemented!"); + } + + return NULL; +} + +bool QmitkAddNewPropertyDialog::ValidateValue() +{ + QString type = m_Controls.typeComboBox->currentText(); + + if (type == "bool") + { + return true; + } + else if (type == "double") + { + bool ok = false; + m_Controls.valueLineEdit->text().toDouble(&ok); + + return ok; + } + else if (type == "float") + { + bool ok = false; + m_Controls.valueLineEdit->text().toFloat(&ok); + + return ok; + } + else if (type == "int") + { + bool ok = false; + m_Controls.valueLineEdit->text().toInt(&ok); + + return ok; + } + else if (type == "string") + { + return true; + } + else + { + assert(false && "Value validation for selected type not implemented!"); + } + + return false; +} + +void QmitkAddNewPropertyDialog::ShowAdequateValueWidget(const QString& type) +{ + m_Controls.valueLineEdit->clear(); + m_Controls.valueLineEdit->hide(); + + m_Controls.valueCheckBox->setChecked(false); + m_Controls.valueCheckBox->hide(); + + if (type == "bool") + { + m_Controls.valueCheckBox->show(); + } + else if (type == "double") + { + m_Controls.valueLineEdit->setText("0"); + m_Controls.valueLineEdit->show(); + } + else if (type == "float") + { + m_Controls.valueLineEdit->setText("0"); + m_Controls.valueLineEdit->show(); + } + else if (type == "int") + { + m_Controls.valueLineEdit->setText("0"); + m_Controls.valueLineEdit->show(); + } + else if (type == "string") + { + m_Controls.valueLineEdit->show(); + } + else + { + assert(false && "No adequate value widget specified for selected type!"); + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.h b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.h new file mode 100644 index 0000000000..67a6a42e70 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.h @@ -0,0 +1,47 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QmitkAddNewPropertyDialog_h +#define QmitkAddNewPropertyDialog_h + +#include +#include +#include +#include +#include + +class QmitkAddNewPropertyDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QmitkAddNewPropertyDialog(mitk::DataNode::Pointer dataNode, mitk::BaseRenderer::Pointer renderer = NULL, QWidget* parent = NULL); + ~QmitkAddNewPropertyDialog(); + +private slots: + void AddNewProperty(); + void ShowAdequateValueWidget(const QString& type); + bool ValidateValue(); + +private: + mitk::BaseProperty::Pointer CreateProperty() const; + + Ui::QmitkAddNewPropertyDialog m_Controls; + mitk::DataNode::Pointer m_DataNode; + mitk::BaseRenderer::Pointer m_Renderer; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.ui b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.ui new file mode 100644 index 0000000000..cdc9275679 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkAddNewPropertyDialog.ui @@ -0,0 +1,127 @@ + + + QmitkAddNewPropertyDialog + + + + 0 + 0 + 253 + 150 + + + + Add new property + + + + + + + + Name + + + + + + + + + + Type + + + + + + + + + + Value + + + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 2 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Add + + + false + + + true + + + + + + + &Cancel + + + false + + + + + + + + + nameLineEdit + typeComboBox + valueLineEdit + valueCheckBox + addButton + cancelButton + + + + diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.cpp b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.cpp index c68d346a73..67a6181d93 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.cpp +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.cpp @@ -1,79 +1,82 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkPropertiesPreferencePage.h" #include const std::string QmitkPropertiesPreferencePage::FILTER_PROPERTIES = "filter properties"; const std::string QmitkPropertiesPreferencePage::SHOW_ALIASES = "show aliases"; const std::string QmitkPropertiesPreferencePage::SHOW_DESCRIPTIONS = "show descriptions"; const std::string QmitkPropertiesPreferencePage::SHOW_ALIASES_IN_DESCRIPTION = "show aliases in description"; +const std::string QmitkPropertiesPreferencePage::DEVELOPER_MODE = "enable developer mode"; QmitkPropertiesPreferencePage::QmitkPropertiesPreferencePage() : m_Control(NULL), m_Preferences(berry::Platform::GetServiceRegistry().GetServiceById(berry::IPreferencesService::ID)->GetSystemPreferences()->Node("/org.mitk.views.properties")) { } QmitkPropertiesPreferencePage::~QmitkPropertiesPreferencePage() { } void QmitkPropertiesPreferencePage::CreateQtControl(QWidget* parent) { m_Control = new QWidget(parent); m_Controls.setupUi(m_Control); connect(m_Controls.showDescriptionsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnShowDescriptionsStateChanged(int))); this->Update(); } QWidget* QmitkPropertiesPreferencePage::GetQtControl() const { return m_Control; } void QmitkPropertiesPreferencePage::Init(berry::IWorkbench::Pointer) { } void QmitkPropertiesPreferencePage::OnShowDescriptionsStateChanged(int state) { m_Controls.showAliasesInDescriptionCheckBox->setEnabled(state != Qt::Unchecked); } bool QmitkPropertiesPreferencePage::PerformOk() { m_Preferences->PutBool(FILTER_PROPERTIES, m_Controls.filterPropertiesCheckBox->isChecked()); m_Preferences->PutBool(SHOW_ALIASES, m_Controls.showAliasesCheckBox->isChecked()); m_Preferences->PutBool(SHOW_DESCRIPTIONS, m_Controls.showDescriptionsCheckBox->isChecked()); m_Preferences->PutBool(SHOW_ALIASES_IN_DESCRIPTION, m_Controls.showAliasesInDescriptionCheckBox->isChecked()); + m_Preferences->PutBool(DEVELOPER_MODE, m_Controls.enableDeveloperModeCheckBox->isChecked()); return true; } void QmitkPropertiesPreferencePage::PerformCancel() { } void QmitkPropertiesPreferencePage::Update() { m_Controls.filterPropertiesCheckBox->setChecked(m_Preferences->GetBool(FILTER_PROPERTIES, true)); m_Controls.showAliasesCheckBox->setChecked(m_Preferences->GetBool(SHOW_ALIASES, true)); m_Controls.showDescriptionsCheckBox->setChecked(m_Preferences->GetBool(SHOW_DESCRIPTIONS, true)); m_Controls.showAliasesInDescriptionCheckBox->setChecked(m_Preferences->GetBool(SHOW_ALIASES_IN_DESCRIPTION, true)); + m_Controls.enableDeveloperModeCheckBox->setChecked(m_Preferences->GetBool(DEVELOPER_MODE, false)); } diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.h b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.h index c8aa2ac2b7..ac9e77823e 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.h +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.h @@ -1,53 +1,54 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkPropertiesPreferencePage_h #define QmitkPropertiesPreferencePage_h #include #include class QmitkPropertiesPreferencePage : public QObject, public berry::IQtPreferencePage { Q_OBJECT Q_INTERFACES(berry::IPreferencePage) public: static const std::string FILTER_PROPERTIES; static const std::string SHOW_ALIASES; static const std::string SHOW_DESCRIPTIONS; static const std::string SHOW_ALIASES_IN_DESCRIPTION; + static const std::string DEVELOPER_MODE; QmitkPropertiesPreferencePage(); ~QmitkPropertiesPreferencePage(); void CreateQtControl(QWidget* parent); QWidget* GetQtControl() const; void Init(berry::IWorkbench::Pointer workbench); bool PerformOk(); void PerformCancel(); void Update(); private slots: void OnShowDescriptionsStateChanged(int state); private: QWidget* m_Control; Ui::QmitkPropertiesPreferencePage m_Controls; berry::IPreferences::Pointer m_Preferences; }; #endif diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.ui b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.ui index 98ad654dc8..318d3156ff 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.ui +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertiesPreferencePage.ui @@ -1,108 +1,130 @@ QmitkPropertiesPreferencePage 0 0 400 - 300 + 577 - + Aliases Replace genuine property names by aliases true Descriptions Show descriptions true margin-left: 18px; Show aliases of genuine property names true Filters Only show filtered properties true + + + + Developer Mode + + + + + + true + + + Enabled + + + false + + + + + + Qt::Vertical 20 204 showAliasesCheckBox showDescriptionsCheckBox diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyItemModel.cpp b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyItemModel.cpp index ec5b066bda..f57e188d6a 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyItemModel.cpp +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyItemModel.cpp @@ -1,515 +1,520 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkGetPropertyService.h" #include "QmitkPropertiesPreferencePage.h" #include "QmitkPropertyItem.h" #include "QmitkPropertyItemModel.h" #include #include #include #include #include #include #include #include static QColor MitkToQt(const mitk::Color &color) { return QColor(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255); } static mitk::BaseProperty* GetBaseProperty(const QVariant& data) { return data.isValid() ? reinterpret_cast(data.value()) : NULL; } static mitk::Color QtToMitk(const QColor &color) { mitk::Color mitkColor; mitkColor.SetRed(color.red() / 255.0f); mitkColor.SetGreen(color.green() / 255.0f); mitkColor.SetBlue(color.blue() / 255.0f); return mitkColor; } class PropertyEqualTo { public: PropertyEqualTo(const mitk::BaseProperty* property) : m_Property(property) { } bool operator()(const mitk::PropertyList::PropertyMapElementType& pair) const { return pair.second.GetPointer() == m_Property; } private: const mitk::BaseProperty* m_Property; }; QmitkPropertyItemModel::QmitkPropertyItemModel(QObject* parent) : QAbstractItemModel(parent), m_PropertyAliases(NULL), m_PropertyFilters(NULL) { this->CreateRootItem(); } QmitkPropertyItemModel::~QmitkPropertyItemModel() { this->SetNewPropertyList(NULL); } int QmitkPropertyItemModel::columnCount(const QModelIndex& parent) const { if (parent.isValid()) return static_cast(parent.internalPointer())->GetColumnCount(); else return m_RootItem->GetColumnCount(); } void QmitkPropertyItemModel::CreateRootItem() { QList rootData; rootData << "Property" << "Value"; m_RootItem.reset(new QmitkPropertyItem(rootData)); this->reset(); } QVariant QmitkPropertyItemModel::data(const QModelIndex& index, int role) const { if(!index.isValid()) return QVariant(); mitk::BaseProperty* property = index.column() == 1 ? GetBaseProperty(static_cast(index.internalPointer())->GetData(1)) : NULL; if (role == Qt::DisplayRole) { if (index.column() == 0) { return static_cast(index.internalPointer())->GetData(0); } else if (index.column() == 1 && property != NULL) { if (mitk::ColorProperty* colorProperty = dynamic_cast(property)) return MitkToQt(colorProperty->GetValue()); else if (dynamic_cast(property) == NULL) return QString::fromStdString(property->GetValueAsString()); } } else if (index.column() == 1 && property != NULL) { if (role == Qt::CheckStateRole) { if (mitk::BoolProperty* boolProperty = dynamic_cast(property)) return boolProperty->GetValue() ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::EditRole) { if (dynamic_cast(property) != NULL) { return QString::fromStdString(property->GetValueAsString()); } else if (mitk::IntProperty* intProperty = dynamic_cast(property)) { return intProperty->GetValue(); } else if (mitk::FloatProperty* floatProperty = dynamic_cast(property)) { return floatProperty->GetValue(); } else if (mitk::DoubleProperty* doubleProperty = dynamic_cast(property)) { return doubleProperty->GetValue(); } else if (mitk::EnumerationProperty* enumProperty = dynamic_cast(property)) { QStringList values; for (mitk::EnumerationProperty::EnumConstIterator it = enumProperty->Begin(); it != enumProperty->End(); it++) values << QString::fromStdString(it->second); return values; } else if (mitk::ColorProperty* colorProperty = dynamic_cast(property)) { return MitkToQt(colorProperty->GetValue()); } } else if (role == mitk::PropertyRole) { return QVariant::fromValue(property); } } return QVariant(); } QModelIndex QmitkPropertyItemModel::FindProperty(const mitk::BaseProperty* property) { if (property == NULL) return QModelIndex(); typedef mitk::PropertyList::PropertyMap PropertyMap; const PropertyMap* propertyMap = m_PropertyList->GetMap(); PropertyMap::const_iterator it = std::find_if(propertyMap->begin(), propertyMap->end(), PropertyEqualTo(property)); if (it == propertyMap->end()) return QModelIndex(); QString name = QString::fromStdString(it->first); if (!name.contains('.')) { QModelIndexList item = this->match(index(0, 0), Qt::DisplayRole, name, 1, Qt::MatchExactly); if (!item.empty()) return item[0]; } else { QStringList names = name.split('.'); QModelIndexList items = this->match(index(0, 0), Qt::DisplayRole, names.last(), -1, Qt::MatchRecursive | Qt::MatchExactly); foreach(QModelIndex item, items) { QModelIndex candidate = item; for (int i = names.length() - 1; i != 0; --i) { QModelIndex parent = item.parent(); if (parent.parent() == QModelIndex()) { if (parent.data() != names.first()) break; return candidate; } if (parent.data() != names[i - 1]) break; item = parent; } } } return QModelIndex(); } Qt::ItemFlags QmitkPropertyItemModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (index.column() == 1) { if (index.data(Qt::EditRole).isValid()) flags |= Qt::ItemIsEditable; if (index.data(Qt::CheckStateRole).isValid()) flags |= Qt::ItemIsUserCheckable; } return flags; } mitk::PropertyList* QmitkPropertyItemModel::GetPropertyList() const { return m_PropertyList.GetPointer(); } QVariant QmitkPropertyItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return m_RootItem->GetData(section); return QVariant(); } QModelIndex QmitkPropertyItemModel::index(int row, int column, const QModelIndex& parent) const { if (!this->hasIndex(row, column, parent)) return QModelIndex(); QmitkPropertyItem* parentItem = parent.isValid() ? static_cast(parent.internalPointer()) : m_RootItem.get(); QmitkPropertyItem* childItem = parentItem->GetChild(row); return childItem != NULL ? this->createIndex(row, column, childItem) : QModelIndex(); } void QmitkPropertyItemModel::OnPreferencesChanged(const berry::IBerryPreferences* preferences) { bool showAliases = preferences->GetBool(QmitkPropertiesPreferencePage::SHOW_ALIASES, true); bool filterProperties = preferences->GetBool(QmitkPropertiesPreferencePage::FILTER_PROPERTIES, true); bool updateAliases = showAliases != (m_PropertyAliases != NULL); bool updateFilters = filterProperties != (m_PropertyFilters != NULL); bool resetPropertyList = false; if (updateAliases) { m_PropertyAliases = showAliases ? mitk::GetPropertyService() : NULL; resetPropertyList = m_PropertyList.IsNotNull(); } if (updateFilters) { m_PropertyFilters = filterProperties ? mitk::GetPropertyService() : NULL; if (!resetPropertyList) resetPropertyList = m_PropertyList.IsNotNull(); } if (resetPropertyList) this->SetNewPropertyList(m_PropertyList.GetPointer()); } void QmitkPropertyItemModel::OnPropertyDeleted(const itk::Object* property, const itk::EventObject&) { /*QModelIndex index = this->FindProperty(static_cast(property)); if (index != QModelIndex()) this->reset();*/ } void QmitkPropertyItemModel::OnPropertyListDeleted(const itk::Object*) { this->CreateRootItem(); } void QmitkPropertyItemModel::OnPropertyModified(const itk::Object* property, const itk::EventObject&) { QModelIndex index = this->FindProperty(static_cast(property)); if (index != QModelIndex()) emit dataChanged(index, index); } QModelIndex QmitkPropertyItemModel::parent(const QModelIndex& child) const { if (!child.isValid()) return QModelIndex(); QmitkPropertyItem* parentItem = static_cast(child.internalPointer())->GetParent(); if (parentItem == m_RootItem.get()) return QModelIndex(); return this->createIndex(parentItem->GetRow(), 0, parentItem); } int QmitkPropertyItemModel::rowCount(const QModelIndex& parent) const { if (parent.column() > 0) return 0; QmitkPropertyItem *parentItem = parent.isValid() ? static_cast(parent.internalPointer()) : m_RootItem.get(); return parentItem->GetChildCount(); } bool QmitkPropertyItemModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid() || index.column() != 1 || (role != Qt::EditRole && role != Qt::CheckStateRole)) return false; mitk::BaseProperty* property = GetBaseProperty(static_cast(index.internalPointer())->GetData(1)); if (property == NULL) return false; if (mitk::BoolProperty* boolProperty = dynamic_cast(property)) { boolProperty->SetValue(value.toInt() == Qt::Checked ? true : false); } else if (mitk::StringProperty* stringProperty = dynamic_cast(property)) { stringProperty->SetValue(value.toString().toStdString()); } else if (mitk::IntProperty* intProperty = dynamic_cast(property)) { intProperty->SetValue(value.toInt()); } else if (mitk::FloatProperty* floatProperty = dynamic_cast(property)) { floatProperty->SetValue(value.toFloat()); } else if (mitk::DoubleProperty* doubleProperty = dynamic_cast(property)) { doubleProperty->SetValue(value.toDouble()); } else if (mitk::EnumerationProperty* enumProperty = dynamic_cast(property)) { std::string selection = value.toString().toStdString(); if (selection != enumProperty->GetValueAsString() && enumProperty->IsValidEnumerationValue(selection)) enumProperty->SetValue(selection); } else if (mitk::ColorProperty* colorProperty = dynamic_cast(property)) { colorProperty->SetValue(QtToMitk(value.value())); } m_PropertyList->InvokeEvent(itk::ModifiedEvent()); m_PropertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } void QmitkPropertyItemModel::SetNewPropertyList(mitk::PropertyList* propertyList) { typedef mitk::PropertyList::PropertyMap PropertyMap; this->beginResetModel(); if (m_PropertyList.IsNotNull()) { mitk::MessageDelegate1 delegate(this, &QmitkPropertyItemModel::OnPropertyListDeleted); m_PropertyList.ObjectDelete.RemoveListener(delegate); const PropertyMap* propertyMap = m_PropertyList->GetMap(); for (PropertyMap::const_iterator propertyIt = propertyMap->begin(); propertyIt != propertyMap->end(); ++propertyIt) { std::map::const_iterator tagIt = m_PropertyModifiedTags.find(propertyIt->first); if (tagIt != m_PropertyModifiedTags.end()) propertyIt->second->RemoveObserver(tagIt->second); tagIt = m_PropertyDeletedTags.find(propertyIt->first); if (tagIt != m_PropertyDeletedTags.end()) propertyIt->second->RemoveObserver(tagIt->second); } m_PropertyModifiedTags.clear(); } m_PropertyList = propertyList; if (m_PropertyList.IsNotNull()) { mitk::MessageDelegate1 delegate(this, &QmitkPropertyItemModel::OnPropertyListDeleted); m_PropertyList.ObjectDelete.AddListener(delegate); mitk::MessageDelegate2 propertyDelegate(this, &QmitkPropertyItemModel::OnPropertyModified); itk::MemberCommand::Pointer modifiedCommand = itk::MemberCommand::New(); modifiedCommand->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyModified); const PropertyMap* propertyMap = m_PropertyList->GetMap(); for (PropertyMap::const_iterator it = propertyMap->begin(); it != propertyMap->end(); ++it) m_PropertyModifiedTags.insert(std::make_pair(it->first, it->second->AddObserver(itk::ModifiedEvent(), modifiedCommand))); itk::MemberCommand::Pointer deletedCommand = itk::MemberCommand::New(); deletedCommand->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyDeleted); for (PropertyMap::const_iterator it = propertyMap->begin(); it != propertyMap->end(); ++it) m_PropertyDeletedTags.insert(std::make_pair(it->first, it->second->AddObserver(itk::DeleteEvent(), deletedCommand))); } this->CreateRootItem(); if (m_PropertyList != NULL && !m_PropertyList->IsEmpty()) { mitk::PropertyList::PropertyMap filteredProperties; bool filterProperties = false; if (m_PropertyFilters != NULL && (m_PropertyFilters->HasFilter() || m_PropertyFilters->HasFilter(m_ClassName.toStdString()))) { filteredProperties = m_PropertyFilters->ApplyFilter(*m_PropertyList->GetMap(), m_ClassName.toStdString()); filterProperties = true; } const mitk::PropertyList::PropertyMap* propertyMap = !filterProperties ? m_PropertyList->GetMap() : &filteredProperties; mitk::PropertyList::PropertyMap::const_iterator end = propertyMap->end(); for (mitk::PropertyList::PropertyMap::const_iterator iter = propertyMap->begin(); iter != end; ++iter) { std::vector aliases; if (m_PropertyAliases != NULL) { aliases = m_PropertyAliases->GetAliases(iter->first, m_ClassName.toStdString()); if (aliases.empty() && !m_ClassName.isEmpty()) aliases = m_PropertyAliases->GetAliases(iter->first); } if (aliases.empty()) { QList data; data << QString::fromStdString(iter->first) << QVariant::fromValue((reinterpret_cast(iter->second.GetPointer()))); m_RootItem->AppendChild(new QmitkPropertyItem(data)); } else { std::vector::const_iterator end = aliases.end(); for (std::vector::const_iterator aliasIter = aliases.begin(); aliasIter != end; ++aliasIter) { QList data; data << QString::fromStdString(*aliasIter) << QVariant::fromValue((reinterpret_cast(iter->second.GetPointer()))); m_RootItem->AppendChild(new QmitkPropertyItem(data)); } } } } this->endResetModel(); } void QmitkPropertyItemModel::SetPropertyList(mitk::PropertyList* propertyList, const QString& className) { if (m_PropertyList.GetPointer() != propertyList) { m_ClassName = className; this->SetNewPropertyList(propertyList); } } + +void QmitkPropertyItemModel::Update() +{ + this->SetNewPropertyList(m_PropertyList); +} diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyItemModel.h b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyItemModel.h index 4bd0601b89..d744598fef 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyItemModel.h +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyItemModel.h @@ -1,79 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkPropertyItemModel_h #define QmitkPropertyItemModel_h #include #include #include class QmitkPropertyItem; namespace berry { struct IBerryPreferences; } namespace mitk { class IPropertyAliases; class IPropertyFilters; enum { PropertyRole = Qt::UserRole + 1 }; } class QmitkPropertyItemModel : public QAbstractItemModel { Q_OBJECT public: explicit QmitkPropertyItemModel(QObject* parent = NULL); ~QmitkPropertyItemModel(); int columnCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; Qt::ItemFlags flags(const QModelIndex& index) const; mitk::PropertyList* GetPropertyList() const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; void OnPreferencesChanged(const berry::IBerryPreferences* preferences); QModelIndex parent(const QModelIndex& child) const; int rowCount(const QModelIndex& parent = QModelIndex()) const; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); void SetPropertyList(mitk::PropertyList* propertyList, const QString& className = ""); + void Update(); private: void CreateRootItem(); QModelIndex FindProperty(const mitk::BaseProperty* property); void OnPropertyDeleted(const itk::Object* property, const itk::EventObject& event); void OnPropertyListDeleted(const itk::Object* propertyList); void OnPropertyModified(const itk::Object* property, const itk::EventObject& event); void SetNewPropertyList(mitk::PropertyList* propertyList); mitk::IPropertyAliases* m_PropertyAliases; mitk::IPropertyFilters* m_PropertyFilters; mitk::WeakPointer m_PropertyList; QString m_ClassName; std::auto_ptr m_RootItem; std::map m_PropertyDeletedTags; std::map m_PropertyModifiedTags; }; #endif diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.cpp b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.cpp index 13f9810a7e..007d4da891 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.cpp +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.cpp @@ -1,289 +1,390 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkGetPropertyService.h" +#include "QmitkAddNewPropertyDialog.h" #include "QmitkPropertiesPreferencePage.h" #include "QmitkPropertyItemDelegate.h" #include "QmitkPropertyItemModel.h" #include "QmitkPropertyItemSortFilterProxyModel.h" #include "QmitkPropertyTreeView.h" #include #include #include +#include #include const std::string QmitkPropertyTreeView::VIEW_ID = "org.mitk.views.properties"; QmitkPropertyTreeView::QmitkPropertyTreeView() - : m_PropertyNameChangedTag(0), + : m_Parent(NULL), + m_PropertyNameChangedTag(0), m_PropertyAliases(NULL), m_PropertyDescriptions(NULL), m_ShowAliasesInDescription(true), + m_DeveloperMode(false), m_ProxyModel(NULL), m_Model(NULL), - m_Delegate(NULL) + m_Delegate(NULL), + m_Renderer(NULL) { } QmitkPropertyTreeView::~QmitkPropertyTreeView() { } void QmitkPropertyTreeView::CreateQtPartControl(QWidget* parent) { + m_Parent = parent; + m_Controls.setupUi(parent); - m_Controls.filter->SetDefaultText("Filter"); + m_Controls.propertyListComboBox->addItem("independent"); + + mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); + + if (renderWindowPart != NULL) + { + QHash renderWindows = renderWindowPart->GetQmitkRenderWindows(); + + Q_FOREACH(QString renderWindow, renderWindows.keys()) + { + m_Controls.propertyListComboBox->addItem(renderWindow); + } + } + + m_Controls.newButton->setEnabled(false); + + m_Controls.filterLineEdit->SetDefaultText("Filter"); m_Controls.description->hide(); + m_Controls.propertyListLabel->hide(); + m_Controls.propertyListComboBox->hide(); + m_Controls.newButton->hide(); m_ProxyModel = new QmitkPropertyItemSortFilterProxyModel(m_Controls.treeView); m_Model = new QmitkPropertyItemModel(m_ProxyModel); m_ProxyModel->setSourceModel(m_Model); m_ProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_ProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_ProxyModel->setDynamicSortFilter(true); m_Delegate = new QmitkPropertyItemDelegate(m_Controls.treeView); m_Controls.treeView->setItemDelegateForColumn(1, m_Delegate); m_Controls.treeView->setModel(m_ProxyModel); - m_Controls.treeView->setColumnWidth(0, 180); + m_Controls.treeView->setColumnWidth(0, 160); m_Controls.treeView->sortByColumn(0, Qt::AscendingOrder); m_Controls.treeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.treeView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.treeView->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::DoubleClicked); - connect(m_Controls.filter, SIGNAL(textChanged(const QString&)), this, SLOT(OnFilterTextChanged(const QString&))); + connect(m_Controls.filterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnFilterTextChanged(const QString&))); + connect(m_Controls.propertyListComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnPropertyListChanged(int))); + connect(m_Controls.newButton, SIGNAL(clicked()), this, SLOT(OnAddNewProperty())); connect(m_Controls.treeView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(OnCurrentRowChanged(const QModelIndex&, const QModelIndex&))); connect(m_Model, SIGNAL(modelReset()), this, SLOT(OnModelReset())); } QString QmitkPropertyTreeView::GetPropertyNameOrAlias(const QModelIndex& index) { QString propertyName; if (index.isValid()) { QModelIndex current = index; while (current.isValid()) { QString name = m_ProxyModel->data(m_ProxyModel->index(current.row(), 0, current.parent())).toString(); propertyName.prepend(propertyName.isEmpty() ? name : name.append('.')); current = current.parent(); } } return propertyName; } void QmitkPropertyTreeView::OnCurrentRowChanged(const QModelIndex& current, const QModelIndex&) { if (m_PropertyDescriptions != NULL && current.isValid()) { QString name = this->GetPropertyNameOrAlias(current); if (!name.isEmpty()) { QString alias; bool isTrueName = true; if (m_PropertyAliases != NULL) { std::string trueName = m_PropertyAliases->GetPropertyName(name.toStdString()); if (trueName.empty() && !m_SelectionClassName.empty()) trueName = m_PropertyAliases->GetPropertyName(name.toStdString(), m_SelectionClassName); if (!trueName.empty()) { alias = name; name = QString::fromStdString(trueName); isTrueName = false; } } QString description = QString::fromStdString(m_PropertyDescriptions->GetDescription(name.toStdString())); std::vector aliases; if (!isTrueName && m_PropertyAliases != NULL) { aliases = m_PropertyAliases->GetAliases(name.toStdString(), m_SelectionClassName); if (aliases.empty() && !m_SelectionClassName.empty()) aliases = m_PropertyAliases->GetAliases(name.toStdString()); } if (!description.isEmpty() || !aliases.empty()) { QString customizedDescription; if (m_ShowAliasesInDescription && !aliases.empty()) { customizedDescription = "

" + name + "

"; std::size_t numAliases = aliases.size(); std::size_t lastAlias = numAliases - 1; for (std::size_t i = 0; i < numAliases; ++i) { customizedDescription += i != lastAlias ? "
" : "
"; customizedDescription += QString::fromStdString(aliases[i]) + "
"; } } else { customizedDescription = "

" + name + "

"; } if (!description.isEmpty()) customizedDescription += description; m_Controls.description->setText(customizedDescription); m_Controls.description->show(); return; } } } m_Controls.description->hide(); } void QmitkPropertyTreeView::OnFilterTextChanged(const QString& filter) { m_ProxyModel->setFilterWildcard(filter); if (filter.isEmpty()) m_Controls.treeView->collapseAll(); else m_Controls.treeView->expandAll(); } void QmitkPropertyTreeView::OnModelReset() { m_Controls.description->hide(); } void QmitkPropertyTreeView::OnPreferencesChanged(const berry::IBerryPreferences* preferences) { bool showAliases = preferences->GetBool(QmitkPropertiesPreferencePage::SHOW_ALIASES, true); bool showDescriptions = preferences->GetBool(QmitkPropertiesPreferencePage::SHOW_DESCRIPTIONS, true); bool showAliasesInDescription = preferences->GetBool(QmitkPropertiesPreferencePage::SHOW_ALIASES_IN_DESCRIPTION, true); + bool developerMode = preferences->GetBool(QmitkPropertiesPreferencePage::DEVELOPER_MODE, false); bool updateAliases = showAliases != (m_PropertyAliases != NULL); bool updateDescriptions = showDescriptions != (m_PropertyDescriptions != NULL); bool updateAliasesInDescription = showAliasesInDescription != m_ShowAliasesInDescription; + bool updateDeveloperMode = developerMode != m_DeveloperMode; if (updateAliases) m_PropertyAliases = showAliases ? mitk::GetPropertyService() : NULL; if (updateDescriptions) m_PropertyDescriptions = showDescriptions ? mitk::GetPropertyService() : NULL; if (updateAliasesInDescription) m_ShowAliasesInDescription = showAliasesInDescription; if (updateDescriptions || updateAliasesInDescription) { QModelIndexList selection = m_Controls.treeView->selectionModel()->selectedRows(); if (!selection.isEmpty()) this->OnCurrentRowChanged(selection[0], selection[0]); } + if (updateDeveloperMode) + { + m_DeveloperMode = developerMode; + + if (!developerMode) + m_Controls.propertyListComboBox->setCurrentIndex(0); + + m_Controls.propertyListLabel->setVisible(developerMode); + m_Controls.propertyListComboBox->setVisible(developerMode); + m_Controls.newButton->setVisible(developerMode); + } + m_Model->OnPreferencesChanged(preferences); } void QmitkPropertyTreeView::OnPropertyNameChanged(const itk::EventObject&) { mitk::PropertyList* propertyList = m_Model->GetPropertyList(); if (propertyList != NULL) { mitk::BaseProperty* nameProperty = propertyList->GetProperty("name"); if (nameProperty != NULL) { std::ostringstream partName; partName << "Properties (" << nameProperty->GetValueAsString() << ')'; this->SetPartName(partName.str()); } } } void QmitkPropertyTreeView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& nodes) { mitk::PropertyList* propertyList = m_Model->GetPropertyList(); if (propertyList != NULL) { mitk::BaseProperty* nameProperty = propertyList->GetProperty("name"); if (nameProperty != NULL) nameProperty->RemoveObserver(m_PropertyNameChangedTag); m_PropertyNameChangedTag = 0; } if (nodes.empty() || nodes.front().IsNull()) { + m_SelectedNode = NULL; + this->SetPartName("Properties"); m_Model->SetPropertyList(NULL); m_Delegate->SetPropertyList(NULL); + + m_Controls.newButton->setEnabled(false); } else { - QString selectionClassName = nodes.front()->GetData() != NULL - ? nodes.front()->GetData()->GetNameOfClass() + m_SelectedNode = nodes.front(); + + QString selectionClassName = m_SelectedNode->GetData() != NULL + ? m_SelectedNode->GetData()->GetNameOfClass() : ""; m_SelectionClassName = selectionClassName.toStdString(); - m_Model->SetPropertyList(nodes.front()->GetPropertyList(), selectionClassName); - m_Delegate->SetPropertyList(nodes.front()->GetPropertyList()); + m_Model->SetPropertyList(m_SelectedNode->GetPropertyList(m_Renderer), selectionClassName); + m_Delegate->SetPropertyList(m_SelectedNode->GetPropertyList(m_Renderer)); OnPropertyNameChanged(itk::ModifiedEvent()); - mitk::BaseProperty* nameProperty = nodes.front()->GetProperty("name"); + mitk::BaseProperty* nameProperty = m_SelectedNode->GetProperty("name"); if (nameProperty != NULL) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkPropertyTreeView::OnPropertyNameChanged); m_PropertyNameChangedTag = nameProperty->AddObserver(itk::ModifiedEvent(), command); } + + m_Controls.newButton->setEnabled(true); } if (!m_ProxyModel->filterRegExp().isEmpty()) m_Controls.treeView->expandAll(); } void QmitkPropertyTreeView::SetFocus() { - m_Controls.filter->setFocus(); + m_Controls.filterLineEdit->setFocus(); +} + +void QmitkPropertyTreeView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) +{ + if (m_Controls.propertyListComboBox->count() == 1) + { + QHash renderWindows = this->GetRenderWindowPart()->GetQmitkRenderWindows(); + + Q_FOREACH(QString renderWindow, renderWindows.keys()) + { + m_Controls.propertyListComboBox->addItem(renderWindow); + } + } +} + +void QmitkPropertyTreeView::RenderWindowPartDeactivated(mitk::IRenderWindowPart*) +{ + if (m_Controls.propertyListComboBox->count() > 1) + { + m_Controls.propertyListComboBox->clear(); + m_Controls.propertyListComboBox->addItem("independent"); + } +} + +void QmitkPropertyTreeView::OnPropertyListChanged(int index) +{ + if (index == -1) + return; + + QString renderer = m_Controls.propertyListComboBox->itemText(index); + + m_Renderer = renderer != "independent" + ? this->GetRenderWindowPart()->GetQmitkRenderWindow(renderer)->GetRenderer() + : NULL; + + QList nodes; + + if (m_SelectedNode.IsNotNull()) + nodes << m_SelectedNode; + + berry::IWorkbenchPart::Pointer workbenchPart; + + this->OnSelectionChanged(workbenchPart, nodes); +} + +void QmitkPropertyTreeView::OnAddNewProperty() +{ + QmitkAddNewPropertyDialog dialog(m_SelectedNode, m_Renderer, m_Parent); + + if (dialog.exec() == QDialog::Accepted) + this->m_Model->Update(); } diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.h b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.h index f5daca356d..bd2362423e 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.h +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.h @@ -1,73 +1,84 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkPropertyTreeView_h #define QmitkPropertyTreeView_h +#include +#include #include #include class QmitkPropertyItemDelegate; class QmitkPropertyItemModel; class QmitkPropertyItemSortFilterProxyModel; namespace mitk { class IPropertyAliases; class IPropertyDescriptions; } -class QmitkPropertyTreeView : public QmitkAbstractView +class QmitkPropertyTreeView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: static const std::string VIEW_ID; berryObjectMacro(QmitkPropertyTreeView); QmitkPropertyTreeView(); ~QmitkPropertyTreeView(); void SetFocus(); + void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart); + void RenderWindowPartDeactivated(mitk::IRenderWindowPart*); + protected: void CreateQtPartControl(QWidget* parent); private: QString GetPropertyNameOrAlias(const QModelIndex& index); void OnPreferencesChanged(const berry::IBerryPreferences* preferences); void OnPropertyNameChanged(const itk::EventObject& event); void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList& nodes); private slots: void OnCurrentRowChanged(const QModelIndex& current, const QModelIndex& previous); + void OnPropertyListChanged(int index); + void OnAddNewProperty(); void OnFilterTextChanged(const QString& filter); void OnModelReset(); private: + QWidget* m_Parent; unsigned long m_PropertyNameChangedTag; std::string m_SelectionClassName; mitk::IPropertyAliases* m_PropertyAliases; mitk::IPropertyDescriptions* m_PropertyDescriptions; bool m_ShowAliasesInDescription; + bool m_DeveloperMode; Ui::QmitkPropertyTreeView m_Controls; QmitkPropertyItemSortFilterProxyModel* m_ProxyModel; QmitkPropertyItemModel* m_Model; QmitkPropertyItemDelegate* m_Delegate; + mitk::DataNode::Pointer m_SelectedNode; + mitk::BaseRenderer* m_Renderer; }; #endif diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.ui b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.ui index 87333b23d5..94bfeceb32 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.ui +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.ui @@ -1,85 +1,137 @@ QmitkPropertyTreeView 0 0 300 600 - + - + - + + + + + + 0 + 0 + + + + Property List + + + + + + + + 0 + 0 + + + + + + + + Add new property + + + + + + + :/Properties/new.png:/Properties/new.png + + + + 16 + 16 + + + + false + + + + + + + Qt::NoFocus ::item { border-right: 1px solid palette(midlight); padding-bottom: 1px; padding-top: 1px; } ::item:last { border-right: 0; } ::item:selected { background: palette(highlight); color: palette(highlighted-text); } Qt::ScrollBarAsNeeded true true 54 0 0 Qt::RichText true QmitkLineEdit QLineEdit
QmitkLineEdit.h
- + + +
diff --git a/README.md b/README.md new file mode 100644 index 0000000000..5e16e7e39f --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +![MITK Logo][logo] + +The [Medical Imaging Interaction Toolkit][mitk] (MITK) is a free open-source software +system for development of interactive medical image processing software. MITK +combines the [Insight Toolkit][itk] (ITK) and the [Visualization Toolkit][vtk] (VTK) with an application framework. + +See the [MITK homepage][mitk] for details. + +Supported Platforms +----------------------------- + +MITK is a cross-platform C++ toolkit and officially supports: + + - Windows + - MacOS X + - Linux + +For details, please read the [Supported Platforms][platforms] page. + +License +----------- + +Copyright (c) [German Cancer Research Center][dkfz]. + +MITK is available as free open-source software under a [BSD-style license][license]. + +Download +--------- + +The MITK source code and binaries for the *mitkWorkbench* application are released regularly according to the [MITK release cycle][release-cycle]. See the [Download][download] page for a list of releases. + +The official MITK source code is available in the [MITK Git repository][gitweb]. The Git clone command is + + git clone http://git.mitk.org/MITK.git + +Active development takes place in the MITK master branch and its usage is advised for advanced users only. + +How to Contribute +-------------- + +Contributions of all kind are happily accepted. However, to make the contribution process as smooth as possible, please read the [How to contribute to MITK][contribute] page if you plan to contribute to MITK. + + +Build Instructions +------------------------ + +MITK uses [CMake][cmake] to configure a build tree. The following is a crash course about cloning, configuring, and building MITK on a Linux/Unix system: + + git clone http://git.mitk.org/MITK.git + mkdir MITK-build + cd MITK-build + cmake ../MITK + make -j4 + +Read the comprehensive [Build Instructions][build] page for details. + +Useful Links +------------------ + + - [Homepage][mitk] + - [Download][download] + - [Mailing List][mailinglist] + - [Bug Tracker][bugs] + + +[logo]: https://github.com/MITK/MITK/raw/master/mitk.png +[mitk]: http://mitk.org +[itk]: http://www.itk.org +[vtk]: http://www.vtk.org +[platforms]: http://docs.mitk.org/nightly-qt4/SupportedPlatformsPage.html +[dkfz]: http://www.dkfz.de +[license]: http://mitk.org/git/?p=MITK.git;a=blob_plain;f=LICENSE.txt +[release-cycle]: http://mitk.org/MitkReleaseCycle +[download]: http://mitk.org/Download +[gitweb]: http://mitk.org/git/?p=MITK.git +[contribute]: http://mitk.org/How_to_contribute +[cmake]: http://www.cmake.org +[build]: http://docs.mitk.org/nightly-qt4/BuildInstructionsPage.html +[mailinglist]: http://mitk.org/Mailinglist +[bugs]: http://bugs.mitk.org/ diff --git a/mitk.png b/mitk.png new file mode 100644 index 0000000000..af1b463140 Binary files /dev/null and b/mitk.png differ