pax_global_header00006660000000000000000000000064126301335520014512gustar00rootroot0000000000000052 comment=55a738f345e9608f867b52baf03db8c6f46ee269 itksnap-3.4.0/000077500000000000000000000000001263013355200131675ustar00rootroot00000000000000itksnap-3.4.0/.gitignore000066400000000000000000000002411263013355200151540ustar00rootroot00000000000000# VIM swap files .*.sw? *~ # MACOS files .DS_Store* Thumbs.db # Backup files *.bak # Qt Creator stuff CMakeLists.txt.user* CTestConfig.cmake.user* *.autosave itksnap-3.4.0/CMake/000077500000000000000000000000001263013355200141475ustar00rootroot00000000000000itksnap-3.4.0/CMake/DeployQt5.cmake000066400000000000000000000271171263013355200170070ustar00rootroot00000000000000#.rst: # DeployQt5 # --------- # # Functions to help assemble a standalone Qt5 executable. # # A collection of CMake utility functions useful for deploying Qt5 # executables. # # The following functions are provided by this module: # # :: # # write_qt5_conf # resolve_qt5_paths # fixup_qt5_executable # install_qt5_plugin_path # install_qt5_plugin # install_qt5_executable # # Requires CMake 2.8.9 or greater because Qt 5 does. # Also depends on BundleUtilities.cmake. # # :: # # WRITE_QT5_CONF( ) # # Writes a qt.conf file with the into . # # :: # # RESOLVE_QT5_PATHS( []) # # Loop through list and if any don't exist resolve them # relative to the (if supplied) or the # CMAKE_INSTALL_PREFIX. # # :: # # FIXUP_QT5_EXECUTABLE( [ ]) # # Copies Qt plugins, writes a Qt configuration file (if needed) and # fixes up a Qt5 executable using BundleUtilities so it is standalone # and can be drag-and-drop copied to another machine as long as all of # the system libraries are compatible. # # should point to the executable to be fixed-up. # # should contain a list of the names or paths of any Qt # plugins to be installed. # # will be passed to BundleUtilities and should be a list of any # already installed plugins, libraries or executables to also be # fixed-up. # # will be passed to BundleUtilities and should contain and # directories to be searched to find library dependencies. # # allows an custom plugins directory to be used. # # will force a qt.conf file to be written even if not # needed. # # :: # # INSTALL_QT5_PLUGIN_PATH(plugin executable copy installed_plugin_path_var ) # # Install (or copy) a resolved to the default plugins directory # (or ) relative to and store the result in # . # # If is set to TRUE then the plugins will be copied rather than # installed. This is to allow this module to be used at CMake time # rather than install time. # # If is set then anything installed will use this COMPONENT. # # :: # # INSTALL_QT5_PLUGIN(plugin executable copy installed_plugin_path_var ) # # Install (or copy) an unresolved to the default plugins # directory (or ) relative to and store the # result in . See documentation of # INSTALL_QT5_PLUGIN_PATH. # # :: # # INSTALL_QT5_EXECUTABLE( [ ]) # # Installs Qt plugins, writes a Qt configuration file (if needed) and # fixes up a Qt5 executable using BundleUtilities so it is standalone # and can be drag-and-drop copied to another machine as long as all of # the system libraries are compatible. The executable will be fixed-up # at install time. is the COMPONENT used for bundle fixup # and plugin installation. See documentation of FIXUP_QT5_BUNDLE. #============================================================================= # Copyright 2011 Mike McQuaid # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) # The functions defined in this file depend on the fixup_bundle function # (and others) found in BundleUtilities.cmake include(BundleUtilities) set(DeployQt5_cmake_dir "${CMAKE_CURRENT_LIST_DIR}") set(DeployQt5_apple_plugins_dir "PlugIns") function(write_qt5_conf qt_conf_dir qt_conf_contents) set(qt_conf_path "${qt_conf_dir}/qt.conf") message(STATUS "Writing ${qt_conf_path}") file(WRITE "${qt_conf_path}" "${qt_conf_contents}") endfunction() function(resolve_qt5_paths paths_var) set(executable_path ${ARGV1}) set(paths_resolved) foreach(path ${${paths_var}}) if(EXISTS "${path}") list(APPEND paths_resolved "${path}") else() if(${executable_path}) list(APPEND paths_resolved "${executable_path}/${path}") else() list(APPEND paths_resolved "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${path}") endif() endif() endforeach() set(${paths_var} ${paths_resolved} PARENT_SCOPE) endfunction() function(fixup_qt5_executable executable) set(qtplugins ${ARGV1}) set(libs ${ARGV2}) set(dirs ${ARGV3}) set(plugins_dir ${ARGV4}) set(request_qt_conf ${ARGV5}) message(STATUS "fixup_qt5_executable") message(STATUS " executable='${executable}'") message(STATUS " qtplugins='${qtplugins}'") message(STATUS " libs='${libs}'") message(STATUS " dirs='${dirs}'") message(STATUS " plugins_dir='${plugins_dir}'") message(STATUS " request_qt_conf='${request_qt_conf}'") if(QT_LIBRARY_DIR) list(APPEND dirs "${QT_LIBRARY_DIR}") endif() if(QT_BINARY_DIR) list(APPEND dirs "${QT_BINARY_DIR}") endif() if(APPLE) set(qt_conf_dir "${executable}/Contents/Resources") set(executable_path "${executable}") set(write_qt_conf TRUE) if(NOT plugins_dir) set(plugins_dir "${DeployQt5_apple_plugins_dir}") endif() else() get_filename_component(executable_path "${executable}" PATH) if(NOT executable_path) set(executable_path ".") endif() set(qt_conf_dir "${executable_path}") set(write_qt_conf ${request_qt_conf}) endif() foreach(plugin ${qtplugins}) set(installed_plugin_path "") install_qt5_plugin("${plugin}" "${executable}" 1 installed_plugin_path) list(APPEND libs ${installed_plugin_path}) endforeach() foreach(lib ${libs}) if(NOT EXISTS "${lib}") message(FATAL_ERROR "Library does not exist: ${lib}") endif() endforeach() resolve_qt5_paths(libs "${executable_path}") if(write_qt_conf) set(qt_conf_contents "[Paths]\nPlugins = ${plugins_dir}") write_qt5_conf("${qt_conf_dir}" "${qt_conf_contents}") endif() fixup_bundle("${executable}" "${libs}" "${dirs}") endfunction() function(install_qt5_plugin_path plugin executable copy installed_plugin_path_var) set(plugins_dir ${ARGV4}) set(component ${ARGV5}) set(configurations ${ARGV6}) if(EXISTS "${plugin}") if(APPLE) if(NOT plugins_dir) set(plugins_dir "${DeployQt5_apple_plugins_dir}") endif() set(plugins_path "${executable}/Contents/${plugins_dir}") else() get_filename_component(plugins_path "${executable}" PATH) if(NOT plugins_path) set(plugins_path ".") endif() if(plugins_dir) set(plugins_path "${plugins_path}/${plugins_dir}") endif() endif() set(plugin_group "") get_filename_component(plugin_path "${plugin}" PATH) get_filename_component(plugin_parent_path "${plugin_path}" PATH) get_filename_component(plugin_parent_dir_name "${plugin_parent_path}" NAME) get_filename_component(plugin_name "${plugin}" NAME) string(TOLOWER "${plugin_parent_dir_name}" plugin_parent_dir_name) if("${plugin_parent_dir_name}" STREQUAL "plugins") get_filename_component(plugin_group "${plugin_path}" NAME) set(${plugin_group_var} "${plugin_group}") endif() set(plugins_path "${plugins_path}/${plugin_group}") if(${copy}) file(MAKE_DIRECTORY "${plugins_path}") file(COPY "${plugin}" DESTINATION "${plugins_path}") else() if(configurations AND (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE)) set(configurations CONFIGURATIONS ${configurations}) else() unset(configurations) endif() install(FILES "${plugin}" DESTINATION "${plugins_path}" ${configurations} ${component}) endif() set(${installed_plugin_path_var} "${plugins_path}/${plugin_name}" PARENT_SCOPE) endif() endfunction() function(install_qt5_plugin plugin executable copy installed_plugin_path_var) set(plugins_dir ${ARGV4}) set(component ${ARGV5}) if(EXISTS "${plugin}") install_qt5_plugin_path("${plugin}" "${executable}" "${copy}" "${installed_plugin_path_var}" "${plugins_dir}" "${component}") else() string(TOUPPER "QT_${plugin}_PLUGIN" plugin_var) set(plugin_release_var "${plugin_var}_RELEASE") set(plugin_debug_var "${plugin_var}_DEBUG") set(plugin_release "${${plugin_release_var}}") set(plugin_debug "${${plugin_debug_var}}") if(DEFINED "${plugin_release_var}" AND DEFINED "${plugin_debug_var}" AND NOT EXISTS "${plugin_release}" AND NOT EXISTS "${plugin_debug}") message(WARNING "Qt plugin \"${plugin}\" not recognized or found.") endif() if(NOT EXISTS "${${plugin_debug_var}}") set(plugin_debug "${plugin_release}") endif() if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) install_qt5_plugin_path("${plugin_release}" "${executable}" "${copy}" "${installed_plugin_path_var}_release" "${plugins_dir}" "${component}" "Release|RelWithDebInfo|MinSizeRel") install_qt5_plugin_path("${plugin_debug}" "${executable}" "${copy}" "${installed_plugin_path_var}_debug" "${plugins_dir}" "${component}" "Debug") if(CMAKE_BUILD_TYPE MATCHES "^Debug$") set(${installed_plugin_path_var} ${${installed_plugin_path_var}_debug}) else() set(${installed_plugin_path_var} ${${installed_plugin_path_var}_release}) endif() else() install_qt5_plugin_path("${plugin_release}" "${executable}" "${copy}" "${installed_plugin_path_var}" "${plugins_dir}" "${component}") endif() endif() set(${installed_plugin_path_var} ${${installed_plugin_path_var}} PARENT_SCOPE) endfunction() function(install_qt5_executable executable) set(qtplugins ${ARGV1}) set(libs ${ARGV2}) set(dirs ${ARGV3}) set(plugins_dir ${ARGV4}) set(request_qt_conf ${ARGV5}) set(component ${ARGV6}) if(QT_LIBRARY_DIR) list(APPEND dirs "${QT_LIBRARY_DIR}") endif() if(QT_BINARY_DIR) list(APPEND dirs "${QT_BINARY_DIR}") endif() if(component) set(component COMPONENT ${component}) else() unset(component) endif() get_filename_component(executable_absolute "${executable}" ABSOLUTE) if(EXISTS "${QT_QTCORE_LIBRARY_RELEASE}") gp_file_type("${executable_absolute}" "${QT_QTCORE_LIBRARY_RELEASE}" qtcore_type) elseif(EXISTS "${QT_QTCORE_LIBRARY_DEBUG}") gp_file_type("${executable_absolute}" "${QT_QTCORE_LIBRARY_DEBUG}" qtcore_type) endif() if(qtcore_type STREQUAL "system") set(qt_plugins_dir "") endif() if(QT_IS_STATIC) message(WARNING "Qt built statically: not installing plugins.") else() if(APPLE) get_property(loc TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE) install_qt5_plugin("${loc}" "${executable}" 0 installed_plugin_paths "PlugIns" "${component}") list(APPEND libs ${installed_plugin_paths}) endif() foreach(plugin ${qtplugins}) set(installed_plugin_paths "") install_qt5_plugin("${plugin}" "${executable}" 0 installed_plugin_paths "${plugins_dir}" "${component}") list(APPEND libs ${installed_plugin_paths}) endforeach() endif() resolve_qt5_paths(libs "") install(CODE "include(\"${DeployQt5_cmake_dir}/DeployQt5.cmake\") set(BU_CHMOD_BUNDLE_ITEMS TRUE) FIXUP_QT5_EXECUTABLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${executable}\" \"\" \"${libs}\" \"${dirs}\" \"${plugins_dir}\" \"${request_qt_conf}\")" ${component} ) endfunction() itksnap-3.4.0/CMake/GitBranch.cmake000066400000000000000000000010451263013355200170120ustar00rootroot00000000000000# This code assigns the git branch to a variable function(get_git_branch RESULTNAME) # Find Git and its libraries if(NOT GIT_FOUND) find_package(Git QUIET) endif(NOT GIT_FOUND) if(GIT_FOUND) # Call git to get branch id execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE SNAP_VERSION_GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE) set(${RESULTNAME} ${SNAP_VERSION_GIT_BRANCH} PARENT_SCOPE) endif(GIT_FOUND) endfunction() itksnap-3.4.0/CMake/rpavlik/000077500000000000000000000000001263013355200156175ustar00rootroot00000000000000itksnap-3.4.0/CMake/rpavlik/GetGitRevisionDescription.cmake000066400000000000000000000102111263013355200237220ustar00rootroot00000000000000# - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ ...]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) function(get_git_head_revision _refspecvar _hashvar) set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(GIT_DIR "${GIT_PARENT_DIR}/.git") while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) # We have reached the root directory, we are not in git set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() set(GIT_DIR "${GIT_PARENT_DIR}/.git") endwhile() # check if this is a submodule if(NOT IS_DIRECTORY ${GIT_DIR}) file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) endif() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${GIT_DIR}/HEAD") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process(COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() itksnap-3.4.0/CMake/rpavlik/GetGitRevisionDescription.cmake.in000066400000000000000000000023011263013355200243300ustar00rootroot00000000000000# # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) set(HEAD_HASH "${HEAD_REF}") endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() itksnap-3.4.0/CMake/standalone.cmake000066400000000000000000000033121263013355200173000ustar00rootroot00000000000000############################################# # REQUIRE ITK 3.20 OR LATER # ############################################# FIND_PACKAGE(ITK REQUIRED) INCLUDE(${ITK_USE_FILE}) ############################################# # REQUIRE VTK # ############################################# FIND_PACKAGE(VTK REQUIRED) INCLUDE (${VTK_USE_FILE}) ############################################# # REQUIRE QT # ############################################# IF(NOT SNAP_USE_QT4) FIND_PACKAGE(Qt5Widgets) FIND_PACKAGE(Qt5OpenGL) FIND_PACKAGE(Qt5Concurrent) FIND_PACKAGE(Qt5Qml) SET(SNAP_QT_INCLUDE_DIRS ${Qt5Widgets_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS} ${Qt5Concurrent_INCLUDE_DIRS} ${Qt5Qml_INCLUDE_DIRS} ) SET(SNAP_QT_LIBRARIES Qt5::Widgets Qt5::OpenGL Qt5::Concurrent Qt5::Qml ) # Set vars for the QT binary and library directories GET_FILENAME_COMPONENT(QT_BINARY_DIR "${Qt5Core_DIR}/../../../bin" ABSOLUTE) GET_FILENAME_COMPONENT(QT_LIBRARY_DIR "${Qt5Core_DIR}/../../" ABSOLUTE) # Set the QTVERSION var SET(QTVERSION ${Qt5Widgets_VERSION}) ELSE(NOT SNAP_USE_QT4) FIND_PACKAGE(Qt4 4.8 COMPONENTS QtCore QtGui QtOpenGL REQUIRED) INCLUDE(${QT_USE_FILE}) ADD_DEFINITIONS(${QT_DEFINITIONS}) SET(SNAP_QT_INCLUDE_DIRS ${QT_QTSCRIPT_INCLUDE_DIR} ${QT_QTSCRIPTTOOLS_INCLUDE_DIR} ) SET(SNAP_QT_LIBRARIES ${QT_LIBRARIES} ${QT_QTMAIN_LIBRARY} ${QT_QTSCRIPT_LIBRARY} ${QT_QTSCRIPTTOOLS_LIBRARY} ) ENDIF(NOT SNAP_USE_QT4) # Look for OpenGL. FIND_PACKAGE(OpenGL REQUIRED) # Link libraries from the parent CMAKE file #LINK_LIBRARIES(ITKAlgorithms ITKCommon ITKBasicFilters) itksnap-3.4.0/CMakeLists.txt000066400000000000000000001421631263013355200157360ustar00rootroot00000000000000#-------------------------------------------------------------------------------- # PROJECT: SNAP #-------------------------------------------------------------------------------- # This CMake file is modeled after QtTest example project from # http://www.cmake.org/Wiki/BundleUtilitiesExample PROJECT(SNAP) #-------------------------------------------------------------------------------- # CMAKE PRELIMINARIES #-------------------------------------------------------------------------------- cmake_minimum_required(VERSION 2.8.12) IF(POLICY CMP0026) cmake_policy(SET CMP0026 OLD) ENDIF(POLICY CMP0026) SET(CMAKE_MODULE_PATH ${SNAP_SOURCE_DIR}/CMake) #-------------------------------------------------------------------------------- # MACROS #-------------------------------------------------------------------------------- # Get today's date (see http://cmake.3232098.n2.nabble.com/How-to-get-the-current-date-td5776870.html) MACRO (TODAY RESULT) IF (WIN32) EXECUTE_PROCESS(COMMAND "cmd" " /C date /T" OUTPUT_VARIABLE ${RESULT}) string(REGEX REPLACE "(..)/(..)/(....).*" "\\1/\\2/\\3" ${RESULT} ${${RESULT}}) ELSEIF(UNIX) EXECUTE_PROCESS(COMMAND "date" "+%b %d, %Y" OUTPUT_VARIABLE ${RESULT}) string(REGEX REPLACE "(...) (..), (....).*" "\\1 \\2, \\3" ${RESULT} ${${RESULT}}) ELSE (WIN32) MESSAGE(SEND_ERROR "date not implemented") SET(${RESULT} 000000) ENDIF (WIN32) string(REPLACE "\n" "" ${RESULT} ${${RESULT}}) string(REPLACE " " "" ${RESULT} ${${RESULT}}) ENDMACRO (TODAY) get_cmake_property(_variableNames VARIABLES) foreach (_variableName ${_variableNames}) message(STATUS "${_variableName}=${${_variableName}}") endforeach() #-------------------------------------------------------------------------------- # VERSION INFORMATION #-------------------------------------------------------------------------------- # On SNAP versions. # ================= # The SNAP version consists of four fields: major, minor, patch and qualifier # for example, version 1.7.3-beta has major version 1, minor version 7, patch 3 # and qualifier "-beta". Major, minor and patch must be numbers, but the qualifier # is an arbitrary string and may be blank. # These four fields should be modified when versions change SET(SNAP_VERSION_MAJOR 3) SET(SNAP_VERSION_MINOR 4) SET(SNAP_VERSION_PATCH 0) SET(SNAP_VERSION_QUALIFIER "") # These fields should also be modified each time SET(SNAP_VERSION_RELEASE_DATE "20151130") SET(SNAP_VERSION_RELEASE_DATE_FORMATTED "Nov 30, 2015") # This field should only change when the format of the settings files changes # in a non backwards-compatible way SET(SNAP_VERSION_LAST_COMPATIBLE_RELEASE_DATE "20131201") # This should not need to change SET(SNAP_VERSION_FULL "${SNAP_VERSION_MAJOR}.${SNAP_VERSION_MINOR}.${SNAP_VERSION_PATCH}${SNAP_VERSION_QUALIFIER}") # Get today's date (see http://cmake.3232098.n2.nabble.com/How-to-get-the-current-date-td5776870.html) TODAY(SNAP_VERSION_COMPILE_DATE) # Get the current git hash list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake/rpavlik/") include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC SNAP_VERSION_GIT_SHA1) # Get the current git branch include(GitBranch) get_git_branch(SNAP_VERSION_GIT_BRANCH) # Print the Git information MESSAGE(STATUS " GIT Info: BRANCH ${SNAP_VERSION_GIT_BRANCH}, SHA: ${SNAP_VERSION_GIT_SHA1}") #-------------------------------------------------------------------------------- # FIND PACKAGES IF BUILDING OUTSIDE INSIGHTAPPLICATIONS #-------------------------------------------------------------------------------- # Add option to build on qt 4.x OPTION(SNAP_USE_QT4 "Compile using Qt version 4.8 for compatibility with older systems and VNC/x2go" OFF) IF(DEFINED InsightApplications_SOURCE_DIR) SET(BUILD_OUTSIDE_INSIGHT_APPLICATIONS FALSE CACHE BOOL "Is SNAP being built separate from InsightApplications?") ELSE(DEFINED InsightApplications_SOURCE_DIR) SET(BUILD_OUTSIDE_INSIGHT_APPLICATIONS TRUE CACHE BOOL "Is SNAP being built separate from InsightApplications?") ENDIF(DEFINED InsightApplications_SOURCE_DIR) IF( BUILD_OUTSIDE_INSIGHT_APPLICATIONS ) INCLUDE(${SNAP_SOURCE_DIR}/CMake/standalone.cmake) ENDIF( BUILD_OUTSIDE_INSIGHT_APPLICATIONS ) #-------------------------------------------------------------------------------- # CPACK PACKAGE NAME # Create a complete name for the package, including the system information # (Shamelessly stolen from ParaView's CMakeLists.txt) #-------------------------------------------------------------------------------- # *** THIS CODE MUST APPEAR BEFORE CALLING CONFIGURE_FILE on SNAPCommon.cxx *** #-------------------------------------------------------------------------------- SET(CPACK_SOURCE_PACKAGE_FILE_NAME "itksnap-${SNAP_VERSION_FULL}-${SNAP_VERSION_RELEASE_DATE}") IF (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown") EXEC_PROGRAM(uname ARGS "-m" OUTPUT_VARIABLE CMAKE_SYSTEM_PROCESSOR) ENDIF (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown") IF(NOT DEFINED CPACK_SYSTEM_NAME) SET(CPACK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}) ENDIF(NOT DEFINED CPACK_SYSTEM_NAME) IF(${CPACK_SYSTEM_NAME} MATCHES Windows) IF(CMAKE_CL_64) SET(CPACK_SYSTEM_NAME win64-${CMAKE_SYSTEM_PROCESSOR}) ELSE(CMAKE_CL_64) SET(CPACK_SYSTEM_NAME win32-${CMAKE_SYSTEM_PROCESSOR}) ENDIF(CMAKE_CL_64) ENDIF(${CPACK_SYSTEM_NAME} MATCHES Windows) # For Apple, we need to base the filename on the architecture IF(CMAKE_SYSTEM_NAME MATCHES Darwin) MESSAGE(STATUS " INARCH ${CMAKE_OSX_ARCHITECTURES}") # TODO: when CMAKE_OSX_ARCHITECTURES is not set this causes an obscure # CMake error. Should generate a meaningful message instead STRING(REPLACE ";" "-" ARCH "${CMAKE_OSX_ARCHITECTURES}") SET(CPACK_SYSTEM_NAME "MacOS-${ARCH}") MESSAGE(STATUS " ARCH ${ARCH}") MESSAGE(STATUS " CMAKE_OSX_ARCHITECTURES ${CMAKE_OSX_ARCHITECTURES}") ENDIF(CMAKE_SYSTEM_NAME MATCHES Darwin) MESSAGE(STATUS " CPACK_SYSTEM_NAME ${CPACK_SYSTEM_NAME}") IF(NOT SNAP_USE_QT4) SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}") ELSE(NOT SNAP_USE_QT4) SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}-qt4") ENDIF(NOT SNAP_USE_QT4) MESSAGE(STATUS " CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}") # Additional CPack resources SET(CPACK_RESOURCE_FILE_LICENSE "${SNAP_SOURCE_DIR}/COPYING") #-------------------------------------------------------------------------------- # SOURCE FILE SPECIFICATION #-------------------------------------------------------------------------------- # One of the files needs to be configured (to insert version info) CONFIGURE_FILE( ${SNAP_SOURCE_DIR}/Common/SNAPCommon.cxx.in ${SNAP_BINARY_DIR}/SNAPCommon.cxx @ONLY IMMEDIATE) # Option to use GPU for SNAP OPTION(SNAP_USE_GPU "Use GPU in SNAP" OFF) # Pass the option SNAP_USE_GPU to a header file CONFIGURE_FILE( ${SNAP_SOURCE_DIR}/Common/GPUSettings.h.in ${SNAP_BINARY_DIR}/GPUSettings.h @ONLY IMMEDIATE) # The part of the source code devoted to the SNAP application logic # is organized into a separate library SET(LOGIC_CXX ${SNAP_BINARY_DIR}/SNAPCommon.cxx Common/AbstractModel.cxx Common/AbstractPropertyContainerModel.cxx Common/CommandLineArgumentParser.cxx Common/EventBucket.cxx Common/HistoryManager.cxx Common/IPCHandler.cxx Common/IRISException.cxx Common/Rebroadcaster.cxx Common/Registry.cxx Common/SNAPOpenGL.cxx Common/SystemInterface.cxx Common/ThreadSpecificData.cxx Common/ITKExtras/itkVoxBoCUBImageIO.cxx Common/ITKExtras/itkVoxBoCUBImageIOFactory.cxx Common/Trackball.cxx Logic/Common/ColorLabelTable.cxx Logic/Common/ColorMap.cxx Logic/Common/ColorMapPresetManager.cxx Logic/Common/ImageCoordinateGeometry.cxx Logic/Common/ImageCoordinateTransform.cxx Logic/Common/IRISDisplayGeometry.cxx Logic/Common/LabelUseHistory.cxx Logic/Common/MetaDataAccess.cxx Logic/Common/SegmentationStatistics.cxx Logic/Common/SNAPAppearanceSettings.cxx Logic/Common/SNAPRegistryIO.cxx Logic/Common/SNAPSegmentationROISettings.cxx Logic/Framework/DefaultBehaviorSettings.cxx Logic/Framework/GenericImageData.cxx Logic/Framework/GlobalState.cxx Logic/Framework/ImageAnnotationData.cxx Logic/Framework/ImageIODelegates.cxx Logic/Framework/IRISApplication.cxx Logic/Framework/IRISImageData.cxx Logic/Framework/LayerIterator.cxx Logic/Framework/SNAPImageData.cxx Logic/Framework/UndoDataManager_LabelType.cxx Logic/ImageWrapper/CommonRepresentationPolicy.cxx Logic/ImageWrapper/DisplayMappingPolicy.cxx Logic/ImageWrapper/ImageWrapperBase.cxx Logic/ImageWrapper/ImageWrapper.cxx Logic/ImageWrapper/InputSelectionImageFilter.cxx Logic/ImageWrapper/GuidedNativeImageIO.cxx Logic/ImageWrapper/ScalarImageHistogram.cxx Logic/ImageWrapper/ScalarImageWrapper.cxx Logic/ImageWrapper/VectorImageWrapper.cxx Logic/LevelSet/SnakeParameters.cxx Logic/LevelSet/SnakeParametersPreviewPipeline.cxx Logic/Mesh/AllPurposeProgressAccumulator.cxx Logic/Mesh/GuidedMeshIO.cxx Logic/Mesh/MultiLabelMeshPipeline.cxx Logic/Mesh/LevelSetMeshPipeline.cxx Logic/Mesh/MeshManager.cxx Logic/Mesh/MeshOptions.cxx Logic/Mesh/VTKMeshPipeline.cxx Logic/Preprocessing/EdgePreprocessingSettings.cxx Logic/Preprocessing/PreprocessingFilterConfigTraits.cxx Logic/Preprocessing/ThresholdSettings.cxx Logic/Preprocessing/GMM/EMGaussianMixtures.cxx Logic/Preprocessing/GMM/Gaussian.cxx Logic/Preprocessing/GMM/GaussianMixtureModel.cxx Logic/Preprocessing/GMM/KMeansPlusPlus.cxx Logic/Preprocessing/GMM/UnsupervisedClustering.cxx Logic/Preprocessing/RandomForest/RFClassificationEngine.cxx Logic/Preprocessing/RandomForest/RandomForestClassifier.cxx Logic/Preprocessing/Texture/MomentTextures.cxx Logic/Slicing/IntensityCurveVTK.cxx Logic/Slicing/IntensityToColorLookupTableImageFilter.cxx Logic/Slicing/LookupTableIntensityMappingFilter.cxx Logic/Slicing/RGBALookupTableIntensityMappingFilter.cxx ) # The headers for the Logic code SET(LOGIC_HEADERS ${SNAP_BINARY_DIR}/GPUSettings.h Common/AbstractModel.h Common/AbstractPropertyContainerModel.h Common/CommandLineArgumentParser.h Common/Credits.h Common/HistoryManager.h Common/IPCHandler.h Common/IRISException.h Common/IRISVectorTypes.h Common/IRISVectorTypes.txx Common/IRISVectorTypesToITKConversion.h Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.h Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.txx Common/ITKExtras/itkBinaryDiamondStructuringElement.h Common/ITKExtras/itkBinaryDiamondStructuringElement.txx Common/ITKExtras/itkCoxDeBoorBSplineKernelFunction.h Common/ITKExtras/itkCoxDeBoorBSplineKernelFunction.txx Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.h Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.txx Common/ITKExtras/itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.h Common/ITKExtras/itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.txx Common/ITKExtras/itkVoxBoCUBImageIO.h Common/ITKExtras/itkVoxBoCUBImageIOFactory.h Common/PresetManager.h Common/PresetManager.hxx Common/PropertyModel.h Common/Rebroadcaster.h Common/Registry.h Common/SNAPBorlandDummyTypes.h Common/SNAPCommon.h Common/SNAPOpenGL.h Common/SNAPEvents.h Common/SystemInterface.h Common/ThreadSpecificData.h Common/Trackball.h Logic/Common/ColorLabel.h Logic/Common/ColorLabelTable.h Logic/Common/ColorMap.h Logic/Common/ColorMapPresetManager.h Logic/Common/ImageCoordinateGeometry.h Logic/Common/ImageCoordinateTransform.h Logic/Common/IRISDisplayGeometry.h Logic/Common/LabelUseHistory.h Logic/Common/SegmentationStatistics.h Logic/Common/ImageRayIntersectionFinder.h Logic/Common/ImageRayIntersectionFinder.txx Logic/Common/MetaDataAccess.h Logic/Common/SNAPAppearanceSettings.h Logic/Common/SNAPRegistryIO.h Logic/Common/SNAPSegmentationROISettings.h Logic/Framework/DefaultBehaviorSettings.h Logic/Framework/GenericImageData.h Logic/Framework/GlobalState.h Logic/Framework/ImageAnnotationData.h Logic/Framework/ImageIODelegates.h Logic/Framework/IRISApplication.h Logic/Framework/IRISImageData.h Logic/Framework/LayerAssociation.h Logic/Framework/LayerAssociation.txx Logic/Framework/LayerIterator.h Logic/Framework/SNAPImageData.h Logic/Framework/UndoDataManager.h Logic/Framework/UndoDataManager.txx Logic/ImageWrapper/CommonRepresentationPolicy.h Logic/ImageWrapper/DisplayMappingPolicy.h Logic/ImageWrapper/GuidedNativeImageIO.h Logic/ImageWrapper/ImageWrapper.h Logic/ImageWrapper/ImageWrapperBase.h Logic/ImageWrapper/ImageWrapperTraits.h Logic/ImageWrapper/InputSelectionImageFilter.h Logic/ImageWrapper/LabelToRGBAFilter.h Logic/ImageWrapper/NativeIntensityMappingPolicy.h Logic/ImageWrapper/ScalarImageHistogram.h Logic/ImageWrapper/ScalarImageWrapper.h Logic/ImageWrapper/ThreadedHistogramImageFilter.h Logic/ImageWrapper/ThreadedHistogramImageFilter.hxx Logic/ImageWrapper/VectorImageWrapper.h Logic/ImageWrapper/CPUImageToGPUImageFilter.h Logic/ImageWrapper/CPUImageToGPUImageFilter.hxx Logic/LevelSet/LevelSetExtensionFilter.h Logic/LevelSet/SnakeParametersPreviewPipeline.h Logic/LevelSet/SNAPAdvectionFieldImageFilter.h Logic/LevelSet/SNAPAdvectionFieldImageFilter.txx Logic/LevelSet/SNAPLevelSetDriver.h Logic/LevelSet/SNAPLevelSetDriver.txx Logic/LevelSet/SNAPLevelSetFunction.h Logic/LevelSet/SNAPLevelSetFunction.txx Logic/LevelSet/SNAPLevelSetStopAndGoFilter.h Logic/LevelSet/SNAPLevelSetStopAndGoFilter.txx Logic/LevelSet/SignedDistanceFilter.h Logic/LevelSet/SignedDistanceFilter.txx Logic/LevelSet/SnakeParameters.h Logic/Mesh/AllPurposeProgressAccumulator.h Logic/Mesh/GuidedMeshIO.h Logic/Mesh/MultiLabelMeshPipeline.h Logic/Mesh/LevelSetMeshPipeline.h Logic/Mesh/MeshManager.h Logic/Mesh/MeshOptions.h Logic/Mesh/VTKMeshPipeline.h Logic/Preprocessing/EdgePreprocessingImageFilter.h Logic/Preprocessing/EdgePreprocessingImageFilter.txx Logic/Preprocessing/EdgePreprocessingSettings.h Logic/Preprocessing/GMMClassifyImageFilter.h Logic/Preprocessing/GMMClassifyImageFilter.txx Logic/Preprocessing/PreprocessingFilterConfigTraits.h Logic/Preprocessing/RandomForestClassifyImageFilter.h Logic/Preprocessing/RandomForestClassifyImageFilter.txx Logic/Preprocessing/SlicePreviewFilterWrapper.h Logic/Preprocessing/SlicePreviewFilterWrapper.txx Logic/Preprocessing/SmoothBinaryThresholdImageFilter.h Logic/Preprocessing/SmoothBinaryThresholdImageFilter.txx Logic/Preprocessing/ThresholdSettings.h Logic/Preprocessing/GMM/EMGaussianMixtures.h Logic/Preprocessing/GMM/Gaussian.h Logic/Preprocessing/GMM/GaussianMixtureModel.h Logic/Preprocessing/GMM/KMeansPlusPlus.h Logic/Preprocessing/GMM/UnsupervisedClustering.h Logic/Preprocessing/RandomForest/RFClassificationEngine.h Logic/Preprocessing/RandomForest/RandomForestClassifier.h Logic/Preprocessing/Texture/MomentTextures.h Logic/Slicing/IRISSlicer.h Logic/Slicing/IRISSlicer.txx Logic/Slicing/IntensityCurveInterface.h Logic/Slicing/IntensityCurveVTK.h Logic/Slicing/IntensityToColorLookupTableImageFilter.h Logic/Slicing/LookupTableIntensityMappingFilter.h Logic/Slicing/RGBALookupTableIntensityMappingFilter.h ) # These files have the UI model code, which is GUI-TK independent SET(UI_GENERIC_CXX GUI/Model/AnnotationModel.cxx GUI/Model/ColorLabelQuickListModel.cxx GUI/Model/ColorLabelPropertyModel.cxx GUI/Model/ColorMapModel.cxx GUI/Model/CursorInspectionModel.cxx GUI/Model/DisplayLayoutModel.cxx GUI/Model/Generic3DModel.cxx GUI/Model/GenericSliceModel.cxx GUI/Model/GlobalPreferencesModel.cxx GUI/Model/GlobalUIModel.cxx GUI/Model/ImageIOWizardModel.cxx GUI/Model/ImageInfoModel.cxx GUI/Model/ImageRegistrationManager.cxx GUI/Model/IntensityCurveModel.cxx GUI/Model/LabelEditorModel.cxx GUI/Model/LayerGeneralPropertiesModel.cxx GUI/Model/LayerTableRowModel.cxx GUI/Model/LayerSelectionModel.cxx GUI/Model/MeshExportModel.cxx GUI/Model/OrthogonalSliceCursorNavigationModel.cxx GUI/Model/PaintbrushModel.cxx GUI/Model/PaintbrushSettingsModel.cxx GUI/Model/PolygonDrawingModel.cxx GUI/Model/PolygonSettingsModel.cxx GUI/Model/ReorientImageModel.cxx GUI/Model/SaveModifiedLayersModel.cxx GUI/Model/SliceWindowCoordinator.cxx GUI/Model/SnakeParameterModel.cxx GUI/Model/SnakeROIModel.cxx GUI/Model/SnakeROIResampleModel.cxx GUI/Model/SnakeWizardModel.cxx GUI/Model/StateManagement.cxx GUI/Model/SynchronizationModel.cxx GUI/Model/UIAction.cxx GUI/Renderer/AbstractRenderer.cxx GUI/Renderer/AbstractVTKRenderer.cxx GUI/Renderer/AbstractVTKSceneRenderer.cxx GUI/Renderer/AnnotationRenderer.cxx GUI/Renderer/ColorMapRenderer.cxx GUI/Renderer/CrosshairsRenderer.cxx GUI/Renderer/EdgePreprocessingSettingsRenderer.cxx GUI/Renderer/GenericSliceRenderer.cxx GUI/Renderer/Generic3DRenderer.cxx GUI/Renderer/GLToPNG.cxx GUI/Renderer/GMMRenderer.cxx GUI/Renderer/IntensityCurveVTKRenderer.cxx GUI/Renderer/LayerHistogramPlotAssembly.cxx GUI/Renderer/OpenGLSliceTexture.cxx GUI/Renderer/OptimizationProgressRenderer.cxx GUI/Renderer/OrientationGraphicRenderer.cxx GUI/Renderer/PaintbrushRenderer.cxx GUI/Renderer/PolygonDrawingRenderer.cxx GUI/Renderer/PolygonScanConvert.cxx GUI/Renderer/SliceWindowDecorationRenderer.cxx GUI/Renderer/SnakeParameterPreviewRenderer.cxx GUI/Renderer/SnakeROIRenderer.cxx GUI/Renderer/SnakeModeRenderer.cxx GUI/Renderer/ThresholdSettingsRenderer.cxx GUI/Renderer/Window3DPicker.cxx GUI/Renderer/OrientationWidget/Reorient/AbstractScannerHelper.cxx GUI/Renderer/OrientationWidget/Reorient/AxesWidget.cxx GUI/Renderer/OrientationWidget/Reorient/ScannedHuman.cxx GUI/Renderer/OrientationWidget/Reorient/ScanningROI.cxx GUI/Renderer/OrientationWidget/Reorient/ReorientProps.cxx GUI/Renderer/OrientationWidget/Reorient/PolyDataAlgorithm2ActorPipe.cxx ) SET(UI_GENERIC_HEADERS GUI/Model/AbstractLayerAssociatedModel.h GUI/Model/AbstractLayerInfoItemSetDomain.h GUI/Model/AnnotationModel.h GUI/Model/ColorMapModel.h GUI/Model/ColorLabelQuickListModel.h GUI/Model/ColorLabelPropertyModel.h GUI/Model/CursorInspectionModel.h GUI/Model/DisplayLayoutModel.h GUI/Model/Generic3DModel.h GUI/Model/GenericSliceModel.h GUI/Model/GlobalPreferencesModel.h GUI/Model/GlobalUIModel.h GUI/Model/ImageInfoModel.h GUI/Model/ImageIOWizardModel.h GUI/Model/ImageRegistrationManager.h GUI/Model/IntensityCurveModel.h GUI/Model/LabelEditorModel.h GUI/Model/LayerGeneralPropertiesModel.h GUI/Model/LayerSelectionModel.h GUI/Model/LayerTableRowModel.h GUI/Model/MeshExportModel.h GUI/Model/OrthogonalSliceCursorNavigationModel.h GUI/Model/PaintbrushModel.h GUI/Model/PaintbrushSettingsModel.h GUI/Model/PolygonSettingsModel.h GUI/Model/PolygonDrawingModel.h GUI/Model/ReorientImageModel.h GUI/Model/SaveModifiedLayersModel.h GUI/Model/SNAPUIFlag.h GUI/Model/SNAPUIFlag.txx GUI/Model/SliceWindowCoordinator.h GUI/Model/SnakeParameterModel.h GUI/Model/SnakeROIModel.h GUI/Model/SnakeROIResampleModel.h GUI/Model/SnakeWizardModel.h GUI/Model/StateManagement.h GUI/Model/SynchronizationModel.h GUI/Model/UIAction.h GUI/Model/UIReporterDelegates.h GUI/Model/UIState.h GUI/Renderer/AbstractRenderer.h GUI/Renderer/AbstractVTKRenderer.h GUI/Renderer/AbstractVTKSceneRenderer.h GUI/Renderer/AnnotationRenderer.h GUI/Renderer/ColorMapRenderer.h GUI/Renderer/CrosshairsRenderer.h GUI/Renderer/EdgePreprocessingSettingsRenderer.h GUI/Renderer/Generic3DRenderer.h GUI/Renderer/GenericSliceRenderer.h GUI/Renderer/GLToPNG.h GUI/Renderer/GMMRenderer.h GUI/Renderer/IntensityCurveVTKRenderer.h GUI/Renderer/LayerHistogramPlotAssembly.h GUI/Renderer/OptimizationProgressRenderer.h GUI/Renderer/OrientationGraphicRenderer.h GUI/Renderer/PaintbrushRenderer.h GUI/Renderer/PolygonDrawingRenderer.h GUI/Renderer/PolygonScanConvert.h GUI/Renderer/SliceWindowDecorationRenderer.h GUI/Renderer/SnakeParameterPreviewRenderer.h GUI/Renderer/SnakeROIRenderer.h GUI/Renderer/SnakeModeRenderer.h GUI/Renderer/ThresholdSettingsRenderer.h GUI/Renderer/Window3DPicker.h GUI/Renderer/OrientationWidget/Reorient/AbstractScannerHelper.h GUI/Renderer/OrientationWidget/Reorient/AxesWidget.h GUI/Renderer/OrientationWidget/Reorient/ScannedHuman.h GUI/Renderer/OrientationWidget/Reorient/ScanningROI.h GUI/Renderer/OrientationWidget/Reorient/ReorientProps.h GUI/Renderer/OrientationWidget/Reorient/PolyDataAlgorithm2ActorPipe.h ) # These files contain the Qt-specific user interface source code SET(UI_QT_CXX GUI/Qt/Components/AnnotationToolPanel.cxx GUI/Qt/Components/CollapsableGroupBox.cxx GUI/Qt/Components/ColorLabelQuickListWidget.cxx GUI/Qt/Components/ColorMapInspector.cxx GUI/Qt/Components/ContrastInspector.cxx GUI/Qt/Components/CursorInspector.cxx GUI/Qt/Components/DisplayLayoutInspector.cxx GUI/Qt/Components/FileChooserPanelWithHistory.cxx GUI/Qt/Components/HistoryQListModel.cxx GUI/Qt/Components/ImageInfoInspector.cxx GUI/Qt/Components/LabelInspector.cxx GUI/Qt/Components/LabelMiniInspector.cxx GUI/Qt/Components/LabelSelectionButton.cxx GUI/Qt/Components/LatentITKEventNotifier.cxx GUI/Qt/Components/LayerInspectorRowDelegate.cxx GUI/Qt/Components/MetadataInspector.cxx GUI/Qt/Components/GeneralLayerInspector.cxx GUI/Qt/Components/PaintbrushToolPanel.cxx GUI/Qt/Components/PolygonToolPanel.cxx GUI/Qt/Components/QActionButton.cxx GUI/Qt/Components/QColorButtonWidget.cxx GUI/Qt/Components/QDoubleSlider.cxx GUI/Qt/Components/QDoubleSliderWithEditor.cxx GUI/Qt/Components/QtHideOnDeactivateContainer.cxx GUI/Qt/Components/QtIPCManager.cxx GUI/Qt/Components/QtRendererPlatformSupport.cxx GUI/Qt/Components/QtReporterDelegates.cxx GUI/Qt/Components/QtWarningDialog.cxx GUI/Qt/Components/QtWidgetActivator.cxx GUI/Qt/Components/RecentHistoryItemsView.cxx GUI/Qt/Components/SnakeToolROIPanel.cxx GUI/Qt/Components/SnakeWizardPanel.cxx GUI/Qt/Components/SNAPComponent.cxx GUI/Qt/Components/SNAPQtCommon.cxx GUI/Qt/Components/SliceViewPanel.cxx GUI/Qt/Components/SynchronizationInspector.cxx GUI/Qt/Components/ViewPanel3D.cxx GUI/Qt/Components/VoxelIntensityQTableModel.cxx GUI/Qt/Components/ZoomInspector.cxx GUI/Qt/External/ColorWheel/ColorWheel.cxx GUI/Qt/ModelView/GMMTableModel.cxx GUI/Qt/View/AnnotationInteractionMode.cxx GUI/Qt/View/ColorMapBox.cxx GUI/Qt/View/CrosshairsInteractionMode.cxx GUI/Qt/View/GenericSliceView.cxx GUI/Qt/View/GenericView3D.cxx GUI/Qt/View/InteractionModeClient.cxx GUI/Qt/View/QtAbstractOpenGLBox.cxx GUI/Qt/View/QtInteractionDelegateWidget.cxx GUI/Qt/View/QtSimpleOpenGLBox.cxx GUI/Qt/View/QtVTKInteractionDelegateWidget.cxx GUI/Qt/View/QtVTKRenderWindowBox.cxx GUI/Qt/View/PaintbrushInteractionMode.cxx GUI/Qt/View/PolygonDrawingInteractionMode.cxx GUI/Qt/View/SliceWindowInteractionDelegateWidget.cxx GUI/Qt/View/SnakeROIInteractionMode.cxx GUI/Qt/View/ThumbnailInteractionMode.cxx GUI/Qt/Windows/AboutDialog.cxx GUI/Qt/Windows/DropActionDialog.cxx GUI/Qt/Windows/ImageIODialog.cxx GUI/Qt/Windows/ImageIOWizard.cxx GUI/Qt/Windows/ImageIOWizard/OverlayRolePage.cxx GUI/Qt/Windows/ImageIOWizard/RegistrationPage.cxx GUI/Qt/Windows/LabelEditorDialog.cxx GUI/Qt/Windows/LayerInspectorDialog.cxx GUI/Qt/Windows/LabelSelectionPopup.cxx GUI/Qt/Windows/MainControlPanel.cxx GUI/Qt/Windows/MainImageWindow.cxx GUI/Qt/Windows/PreferencesDialog.cxx GUI/Qt/Windows/QtStyles.cxx GUI/Qt/Windows/ReorientImageDialog.cxx GUI/Qt/Windows/ResampleDialog.cxx GUI/Qt/Windows/SaveModifiedLayersDialog.cxx GUI/Qt/Windows/SimpleFileDialogWithHistory.cxx GUI/Qt/Windows/SnakeParameterDialog.cxx GUI/Qt/Windows/SpeedImageDialog.cxx GUI/Qt/Windows/SplashPanel.cxx GUI/Qt/Windows/StatisticsDialog.cxx GUI/Qt/Windows/MeshExportWizard/MeshExportWizard.cxx GUI/Qt/Windows/MeshExportWizard/MeshExportModePage.cxx GUI/Qt/Windows/MeshExportWizard/MeshExportBrowsePage.cxx Testing/GUI/Qt/SNAPTestQt.cxx ) # The header files for the UI project SET(UI_MOC_HEADERS GUI/Qt/Components/AnnotationToolPanel.h GUI/Qt/Components/CollapsableGroupBox.h GUI/Qt/Components/ColorLabelQuickListWidget.h GUI/Qt/Components/ColorMapInspector.h GUI/Qt/Components/ContrastInspector.h GUI/Qt/Components/CursorInspector.h GUI/Qt/Components/DisplayLayoutInspector.h GUI/Qt/Components/FileChooserPanelWithHistory.h GUI/Qt/Components/HistoryQListModel.h GUI/Qt/Components/ImageInfoInspector.h GUI/Qt/Components/LabelInspector.h GUI/Qt/Components/LabelMiniInspector.h GUI/Qt/Components/LabelSelectionButton.h GUI/Qt/Components/LatentITKEventNotifier.h GUI/Qt/Components/LayerInspectorRowDelegate.h GUI/Qt/Components/MetadataInspector.h GUI/Qt/Components/GeneralLayerInspector.h GUI/Qt/Components/PaintbrushToolPanel.h GUI/Qt/Components/PolygonToolPanel.h GUI/Qt/Components/QActionButton.h GUI/Qt/Components/QColorButtonWidget.h GUI/Qt/Components/QDoubleSlider.h GUI/Qt/Components/QDoubleSliderWithEditor.h GUI/Qt/Components/QtHideOnDeactivateContainer.h GUI/Qt/Components/QtIPCManager.h GUI/Qt/Components/QtWarningDialog.h GUI/Qt/Components/QtWidgetActivator.h GUI/Qt/Components/RecentHistoryItemsView.h GUI/Qt/Components/SnakeToolROIPanel.h GUI/Qt/Components/SnakeWizardPanel.h GUI/Qt/Components/SNAPComponent.h GUI/Qt/Components/SliceViewPanel.h GUI/Qt/Components/SynchronizationInspector.h GUI/Qt/Components/ViewPanel3D.h GUI/Qt/Components/VoxelIntensityQTableModel.h GUI/Qt/Components/ZoomInspector.h GUI/Qt/Coupling/QtWidgetCoupling.h GUI/Qt/External/ColorWheel/ColorWheel.h GUI/Qt/ModelView/GMMTableModel.h GUI/Qt/View/AnnotationInteractionMode.h GUI/Qt/View/ColorMapBox.h GUI/Qt/View/CrosshairsInteractionMode.h GUI/Qt/View/GenericSliceView.h GUI/Qt/View/GenericView3D.h GUI/Qt/View/QtAbstractOpenGLBox.h GUI/Qt/View/QtInteractionDelegateWidget.h GUI/Qt/View/QtSimpleOpenGLBox.h GUI/Qt/View/QtVTKInteractionDelegateWidget.h GUI/Qt/View/QtVTKRenderWindowBox.h GUI/Qt/View/PaintbrushInteractionMode.h GUI/Qt/View/PolygonDrawingInteractionMode.h GUI/Qt/View/SliceWindowInteractionDelegateWidget.h GUI/Qt/View/SnakeROIInteractionMode.h GUI/Qt/View/ThumbnailInteractionMode.h GUI/Qt/Windows/AboutDialog.h GUI/Qt/Windows/DropActionDialog.h GUI/Qt/Windows/ImageIODialog.h GUI/Qt/Windows/ImageIOWizard.h GUI/Qt/Windows/ImageIOWizard/OverlayRolePage.h GUI/Qt/Windows/ImageIOWizard/RegistrationPage.h GUI/Qt/Windows/LabelEditorDialog.h GUI/Qt/Windows/LayerInspectorDialog.h GUI/Qt/Windows/LabelSelectionPopup.h GUI/Qt/Windows/MainControlPanel.h GUI/Qt/Windows/MainImageWindow.h GUI/Qt/Windows/PreferencesDialog.h GUI/Qt/Windows/ReorientImageDialog.h GUI/Qt/Windows/ResampleDialog.h GUI/Qt/Windows/SaveModifiedLayersDialog.h GUI/Qt/Windows/SimpleFileDialogWithHistory.h GUI/Qt/Windows/SnakeParameterDialog.h GUI/Qt/Windows/SpeedImageDialog.h GUI/Qt/Windows/SplashPanel.h GUI/Qt/Windows/StatisticsDialog.h GUI/Qt/Windows/MeshExportWizard/MeshExportWizard.h GUI/Qt/Windows/MeshExportWizard/MeshExportModePage.h GUI/Qt/Windows/MeshExportWizard/MeshExportBrowsePage.h Testing/GUI/Qt/SNAPTestQt.h ) # These UI headers don't need to be MOC'd SET(UI_NONMOC_HEADERS GUI/Qt/Components/SNAPQtCommon.h GUI/Qt/Components/QtCursorOverride.h GUI/Qt/Components/QtRendererPlatformSupport.h GUI/Qt/Components/QtReporterDelegates.h GUI/Qt/Coupling/QtAbstractButtonCoupling.h GUI/Qt/Coupling/QtAbstractItemViewCoupling.h GUI/Qt/Coupling/QtActionCoupling.h GUI/Qt/Coupling/QtCheckBoxCoupling.h GUI/Qt/Coupling/QtColorWheelCoupling.h GUI/Qt/Coupling/QtComboBoxCoupling.h GUI/Qt/Coupling/QtDoubleSliderWithEditorCoupling.h GUI/Qt/Coupling/QtDoubleSpinBoxCoupling.h GUI/Qt/Coupling/QtLabelCoupling.h GUI/Qt/Coupling/QtLineEditCoupling.h GUI/Qt/Coupling/QtListWidgetCoupling.h GUI/Qt/Coupling/QtRadioButtonCoupling.h GUI/Qt/Coupling/QtScrollbarCoupling.h GUI/Qt/Coupling/QtSliderCoupling.h GUI/Qt/Coupling/QtSpinBoxCoupling.h GUI/Qt/Coupling/QtTableWidgetCoupling.h GUI/Qt/Coupling/QtWidgetArrayCoupling.h GUI/Qt/Coupling/QtWidgetCoupling.h ) SET(UI_FORMS GUI/Qt/Components/AnnotationToolPanel.ui GUI/Qt/Components/CollapsableGroupBox.ui GUI/Qt/Components/ColorMapInspector.ui GUI/Qt/Components/ContrastInspector.ui GUI/Qt/Components/CursorInspector.ui GUI/Qt/Components/DisplayLayoutInspector.ui GUI/Qt/Components/FileChooserPanelWithHistory.ui GUI/Qt/Components/ImageInfoInspector.ui GUI/Qt/Components/LabelInspector.ui GUI/Qt/Components/LabelMiniInspector.ui GUI/Qt/Components/LayerInspectorRowDelegate.ui GUI/Qt/Components/MetadataInspector.ui GUI/Qt/Components/GeneralLayerInspector.ui GUI/Qt/Components/PaintbrushToolPanel.ui GUI/Qt/Components/PolygonToolPanel.ui GUI/Qt/Components/QDoubleSliderWithEditor.ui GUI/Qt/Components/QtWarningDialog.ui GUI/Qt/Components/RecentHistoryItemsView.ui GUI/Qt/Components/SliceViewPanel.ui GUI/Qt/Components/SnakeToolROIPanel.ui GUI/Qt/Components/SnakeWizardPanel.ui GUI/Qt/Components/SynchronizationInspector.ui GUI/Qt/Components/ViewPanel3D.ui GUI/Qt/Components/ZoomInspector.ui GUI/Qt/Windows/AboutDialog.ui GUI/Qt/Windows/DropActionDialog.ui GUI/Qt/Windows/ImageIODialog.ui GUI/Qt/Windows/ImageIOWizard/OverlayRolePage.ui GUI/Qt/Windows/ImageIOWizard/RegistrationPage.ui GUI/Qt/Windows/LabelEditorDialog.ui GUI/Qt/Windows/LayerInspectorDialog.ui GUI/Qt/Windows/LabelSelectionPopup.ui GUI/Qt/Windows/MainControlPanel.ui GUI/Qt/Windows/MainImageWindow.ui GUI/Qt/Windows/PreferencesDialog.ui GUI/Qt/Windows/ReorientImageDialog.ui GUI/Qt/Windows/ResampleDialog.ui GUI/Qt/Windows/SaveModifiedLayersDialog.ui GUI/Qt/Windows/SimpleFileDialogWithHistory.ui GUI/Qt/Windows/SnakeParameterDialog.ui GUI/Qt/Windows/SpeedImageDialog.ui GUI/Qt/Windows/SplashPanel.ui GUI/Qt/Windows/StatisticsDialog.ui GUI/Qt/Windows/MeshExportWizard/MeshExportWizard.ui GUI/Qt/Windows/MeshExportWizard/MeshExportModePage.ui GUI/Qt/Windows/MeshExportWizard/MeshExportBrowsePage.ui ) SET(UI_RESOURCES GUI/Qt/Resources/SNAPResources.qrc GUI/Qt/Resources/SNAPResources_Linux.qrc GUI/Qt/Resources/SNAPResources_MacOS.qrc GUI/Qt/Resources/SNAPResources_Windows.qrc Testing/GUI/Qt/TestingScripts.qrc ) # The source code for SNAP testing project SET(TESTING_CXX Testing/Logic/TestMain.cxx Testing/Logic/SNAPTestDriver.cxx ) # The source code for the tutorial test SET(TESTING_TUTORIAL_CXX Testing/Logic/TutorialTest.cxx ) # The headers for the testing code SET(TESTING_HEADERS Testing/Logic/SNAPTestDriver.h Testing/Logic/TestBase.h Testing/Logic/TestCompareLevelSets.h Testing/Logic/TestImageWrapper.h ) #-------------------------------------------------------------------------------- # Specify include path #-------------------------------------------------------------------------------- # Due to a limitation in Visual studio 6.0 on the length of include directories # that can be specified, (here we are including all the include directories from # ITK, VTK, FLTK and SNAP), if the compiler is VS6, we copy the SNAP source files # to a single path in the binary tree to cut down on the number of # INCLUDE_DIRECTORIES IF( CMAKE_GENERATOR MATCHES "Visual Studio 6" ) FILE( GLOB_RECURSE SNAP_GLOBBED_CXX "${SNAP_SOURCE_DIR}/*.cxx" ) FILE( GLOB_RECURSE SNAP_GLOBBED_H "${SNAP_SOURCE_DIR}/*.h" ) FILE( GLOB_RECURSE SNAP_GLOBBED_TXX "${SNAP_SOURCE_DIR}/*.txx" ) SET(SNAP_SOURCES ${SNAP_GLOBBED_CXX} ${SNAP_GLOBBED_H} ${SNAP_GLOBBED_TXX}) MAKE_DIRECTORY( "${SNAP_BINARY_DIR}/src" ) SET( CONFIGURED_SOURCE_DIRECTORY "${SNAP_BINARY_DIR}/src" ) FOREACH( SourceFile ${SNAP_SOURCES} ) GET_FILENAME_COMPONENT( CONFIGURED_SOURCE_FILE ${SourceFile} NAME ) SET( CONFIGURED_SOURCE_FILE "${CONFIGURED_SOURCE_DIRECTORY}/${CONFIGURED_SOURCE_FILE}" ) CONFIGURE_FILE( ${SourceFile} ${CONFIGURED_SOURCE_FILE} COPYONLY IMMEDIATE ) ENDFOREACH( SourceFile ) INCLUDE_DIRECTORIES( ${CONFIGURED_SOURCE_DIRECTORY} ${ITK_DIR}/Utilities/zlib ${OPENGL_INCLUDE_PATH} ${QT_QTSCRIPT_INCLUDE_DIR} ${QT_QTSCRIPTTOOLS_INCLUDE_DIR}) ELSE( CMAKE_GENERATOR MATCHES "Visual Studio 6" ) # Include directories INCLUDE_DIRECTORIES( ${ITK_DIR}/Utilities/zlib ${SNAP_SOURCE_DIR}/Common ${SNAP_SOURCE_DIR}/Common/ITKExtras ${SNAP_SOURCE_DIR}/Logic ${SNAP_SOURCE_DIR}/Logic/Common ${SNAP_SOURCE_DIR}/Logic/Framework ${SNAP_SOURCE_DIR}/Logic/ImageWrapper ${SNAP_SOURCE_DIR}/Logic/LevelSet ${SNAP_SOURCE_DIR}/Logic/Mesh ${SNAP_SOURCE_DIR}/Logic/Preprocessing ${SNAP_SOURCE_DIR}/Logic/Preprocessing/GMM ${SNAP_SOURCE_DIR}/Logic/Preprocessing/RandomForest ${SNAP_SOURCE_DIR}/Logic/Preprocessing/Texture ${SNAP_SOURCE_DIR}/Logic/Slicing ${SNAP_SOURCE_DIR}/GUI ${SNAP_SOURCE_DIR}/GUI/Model ${SNAP_SOURCE_DIR}/GUI/Renderer ${SNAP_SOURCE_DIR}/GUI/Renderer/OrientationWidget/Reorient ${SNAP_SOURCE_DIR}/GUI/Qt ${SNAP_SOURCE_DIR}/GUI/Qt/Components ${SNAP_SOURCE_DIR}/GUI/Qt/Coupling ${SNAP_SOURCE_DIR}/GUI/Qt/External ${SNAP_SOURCE_DIR}/GUI/Qt/External/ColorWheel ${SNAP_SOURCE_DIR}/GUI/Qt/ModelView ${SNAP_SOURCE_DIR}/GUI/Qt/Testing ${SNAP_SOURCE_DIR}/GUI/Qt/View ${SNAP_SOURCE_DIR}/GUI/Qt/Windows ${SNAP_SOURCE_DIR}/GUI/Qt/Windows/MeshExportWizard ${SNAP_SOURCE_DIR}/Testing/Logic ${SNAP_SOURCE_DIR}/Testing/GUI/Qt ${SNAP_BINARY_DIR} ${OPENGL_INCLUDE_PATH} ${SNAP_QT_INCLUDE_DIRS} ) ENDIF( CMAKE_GENERATOR MATCHES "Visual Studio 6" ) #-------------------------------------------------------------------------------- # Compiler-specific warinings #-------------------------------------------------------------------------------- # Get rid of this ridiculous warning in VS8 IF( CMAKE_GENERATOR MATCHES "Visual Studio 8" OR CMAKE_GENERATOR MATCHES "Visual Studio 9" OR CMAKE_GENERATOR MATCHES "Visual Studio 10" ) ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE) ENDIF( CMAKE_GENERATOR MATCHES "Visual Studio 8" OR CMAKE_GENERATOR MATCHES "Visual Studio 9" OR CMAKE_GENERATOR MATCHES "Visual Studio 10" ) IF( CMAKE_GENERATOR MATCHES "^NMake" OR CMAKE_GENERATOR MATCHES "^Visual Studio" ) ADD_DEFINITIONS(-DNOMINMAX) ENDIF( CMAKE_GENERATOR MATCHES "^NMake" OR CMAKE_GENERATOR MATCHES "^Visual Studio" ) #-------------------------------------------------------------------------------- # Define External Libraries #-------------------------------------------------------------------------------- # ITK Libraries SET(SNAP_ITK_LIBS ${ITK_LIBRARIES}) # Core VTK libraries SET(SNAP_VTK_LIBS vtkChartsCore vtkCommonCore vtkRenderingCore vtkRenderingFreeType vtkRenderingFreeTypeOpenGL vtkRenderingOpenGL vtkRenderingVolume vtkRenderingVolumeOpenGL vtkFiltersCore vtkImagingCore vtkViewsCore vtkViewsContext2D vtkIOCore vtkIOExport vtkIOGeometry vtkIOLegacy ) # System libraries SET(SNAP_SYSTEM_LIBS ${OPENGL_LIBRARIES} ${OPENGL_glu_LIBRARY} ${SYSTEM_LIBS} ) # Designate the external libraries used by SNAP SET(SNAP_EXTERNAL_LIBS ${SNAP_ITK_LIBS} ${SNAP_VTK_LIBS} ${SNAP_SYSTEM_LIBS} ) #-------------------------------------------------------------------------------- # Define SNAP library targets (logic and UI) #-------------------------------------------------------------------------------- # Wrap the QT input files IF(NOT SNAP_USE_QT4) QT5_WRAP_UI(UI_FORM_HEADERS ${UI_FORMS}) QT5_WRAP_CPP(UI_WRAPPED_MOC_HEADERS ${UI_MOC_HEADERS}) QT5_ADD_RESOURCES(UI_RESOURCES_RCC ${UI_RESOURCES}) ELSE(NOT SNAP_USE_QT4) QT4_WRAP_UI(UI_FORM_HEADERS ${UI_FORMS}) QT4_WRAP_CPP(UI_WRAPPED_MOC_HEADERS ${UI_MOC_HEADERS}) QT4_ADD_RESOURCES(UI_RESOURCES_RCC ${UI_RESOURCES}) ENDIF(NOT SNAP_USE_QT4) # The SNAP logic library ADD_LIBRARY(itksnaplogic ${LOGIC_CXX} ${LOGIC_HEADERS}) # The UI model library ADD_LIBRARY(itksnapui_model ${UI_GENERIC_CXX} ${UI_GENERIC_HEADERS}) # The user interface code library ADD_LIBRARY(itksnapui_qt ${UI_QT_CXX} ${UI_WRAPPED_MOC_HEADERS} ${UI_MOC_HEADERS} ${UI_NONMOC_HEADERS} ${UI_FORM_HEADERS} ${UI_RESOURCES_RCC}) # This is experimental: it seems that shared libraries do not # build accurately (at least on MacOS) without the following # two lines TARGET_LINK_LIBRARIES(itksnaplogic ${SNAP_EXTERNAL_LIBS}) TARGET_LINK_LIBRARIES(itksnapui_model itksnaplogic ${SNAP_EXTERNAL_LIBS}) TARGET_LINK_LIBRARIES(itksnapui_qt ${SNAP_QT_LIBRARIES} ${SNAP_EXTERNAL_LIBS}) # Designate the SNAP internal libraries SET(SNAP_INTERNAL_LIBS itksnapui_qt itksnapui_model itksnaplogic) #-------------------------------------------------------------------------------- # Define main SNAP executable #-------------------------------------------------------------------------------- # Code for the main application SET(SNAP_MAIN_SRC GUI/Qt/main.cxx) # On Apple, configure the application icon IF(APPLE) # the icon file SET(SNAP_OSX_ICON ${SNAP_SOURCE_DIR}/Utilities/MacOS/BundleResources/itksnap.icns) # set how it shows up in the Info.plist file SET(MACOSX_BUNDLE_ICON_FILE itksnap.icns) # set where in the bundle to put the icns file SET_SOURCE_FILES_PROPERTIES(${SNAP_OSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) # include the icns file in the target SET(SNAP_MAIN_SRC ${SNAP_MAIN_SRC} ${SNAP_OSX_ICON}) ENDIF(APPLE) # On Windows, add the .RC file to the sources IF(WIN32) # Add the .rc file SET(SNAP_MAIN_SRC ${SNAP_MAIN_SRC} ${SNAP_SOURCE_DIR}/Utilities/Win32/itksnap.rc) ENDIF(WIN32) # Define the main SNAP executable SET(SNAP_BUNDLE_NAME ITK-SNAP) # Configure the executable's sources and libraries ADD_EXECUTABLE(${SNAP_BUNDLE_NAME} WIN32 MACOSX_BUNDLE ${SNAP_MAIN_SRC}) TARGET_LINK_LIBRARIES(${SNAP_BUNDLE_NAME} ${SNAP_INTERNAL_LIBS} ${SNAP_EXTERNAL_LIBS}) # On apple, we also need our custom plist.info file to be configured IF(APPLE) SET_TARGET_PROPERTIES(${SNAP_BUNDLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${SNAP_SOURCE_DIR}/Utilities/MacOS/BundleResources/Info.plist) ENDIF(APPLE) #-------------------------------------------------------------------------------- # Testing-related executables #-------------------------------------------------------------------------------- ENABLE_TESTING() include(BundleUtilities) # Acceleration factor for GUI tests SET(SNAP_GUI_TEST_ACCEL "1.0" CACHE STRING "Acceleration factor for GUI tests, use <1 on VMS and slow machines") MARK_AS_ADVANCED(SNAP_GUI_TEST_ACCEL) # The list of Qt animated GUI tests SET(GUI_TESTS Workspace ProbeIntensity RegionCompetition RandomForest) SET(TESTDATA_DIR "${SNAP_SOURCE_DIR}/Testing/TestData") # Set up a test for each GUI test FOREACH(GUI_TEST ${GUI_TESTS}) # The path of the test - not obvious? IF(APPLE) GET_PROPERTY(GUI_TEST_EXE TARGET ${SNAP_BUNDLE_NAME} PROPERTY LOCATION) ELSE(APPLE) SET(GUI_TEST_EXE ${SNAP_BUNDLE_NAME}) ENDIF(APPLE) # Add the test ADD_TEST(${GUI_TEST} ${GUI_TEST_EXE} --test ${GUI_TEST} --testacc ${SNAP_GUI_TEST_ACCEL} --testdir "${TESTDATA_DIR}") # Set the environment for the test to include Qt FILE(TO_NATIVE_PATH ${QT_BINARY_DIR} QT_BINARY_DIR_NATIVE) IF(WIN32) SET_PROPERTY(TEST ${GUI_TEST} APPEND PROPERTY ENVIRONMENT PATH=${QT_BINARY_DIR_NATIVE}) ELSE(WIN32) SET_PROPERTY(TEST ${GUI_TEST} APPEND PROPERTY ENVIRONMENT PATH="${QT_BINARY_DIR_NATIVE}:$ENV{PATH}") ENDIF(WIN32) SET_PROPERTY(TEST ${GUI_TEST} PROPERTY TIMEOUT 180) ENDFOREACH(GUI_TEST) # Orientation widget test SET(TEST_ORIENTATION_WIDGET_HEADERS GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.h ) SET(TEST_ORIENTATION_WIDGET_CXX GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.cxx GUI/Renderer/OrientationWidget/Test_OrientationWidget/main.cxx ) IF(NOT SNAP_USE_QT4) QT5_WRAP_UI(UI_ORIENTATION_WIDGET_SRCS GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.ui) QT5_WRAP_CPP(MOC_ORIENTATION_WIDGET_SRCS GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.h) ELSE(NOT SNAP_USE_QT4) QT4_WRAP_UI(UI_ORIENTATION_WIDGET_SRCS GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.ui) QT4_WRAP_CPP(MOC_ORIENTATION_WIDGET_SRCS GUI/Renderer/OrientationWidget/Test_OrientationWidget/OrientationWidgetGUI.h) ENDIF(NOT SNAP_USE_QT4) ADD_EXECUTABLE(Test_OrientationWidget ${TEST_ORIENTATION_WIDGET_CXX} ${TEST_ORIENTATION_WIDGET_HEADERS} ${UI_ORIENTATION_WIDGET_SRCS} ${MOC_ORIENTATION_WIDGET_SRCS} ) TARGET_LINK_LIBRARIES(Test_OrientationWidget ${SNAP_INTERNAL_LIBS} ${SNAP_EXTERNAL_LIBS} ) INCLUDE(CTest) #-------------------------------------------------------------------------------- # Copy and configure the program data directory #-------------------------------------------------------------------------------- # All program files - use recursive globbing FILE(GLOB_RECURSE SNAP_PROGRAM_DATA_FILES ProgramData "*.txt" "*.html" "*.gif" "*.png" "*.img.gz" "*.hdr") # Copy documentation from the source tree to the build tree FOREACH(DATAFILE ${SNAP_PROGRAM_DATA_FILES}) FILE(RELATIVE_PATH SHORTNAME ${SNAP_SOURCE_DIR} ${DATAFILE}) CONFIGURE_FILE( ${SNAP_SOURCE_DIR}/${SHORTNAME} ${SNAP_BINARY_DIR}/${SHORTNAME} COPYONLY) ENDFOREACH(DATAFILE) #-------------------------------------------------------------------------------- # INSTALLATION AND PACKAGING SECTION #-------------------------------------------------------------------------------- MESSAGE(STATUS "=== Installation Section ===") MESSAGE(STATUS " CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") MESSAGE(STATUS " CMAKE_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}") MESSAGE(STATUS " PACKAGE_DIR ${CPACK_PACKAGE_DIRECTORY}") MESSAGE(STATUS " INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}") MESSAGE(STATUS " DESTDIR $ENV{DESTDIR}") #-------------------------------------------------------------------------------- # Install the application IF(APPLE) # on Apple, the bundle is at the root of the # install tree, and on other platforms it'll go into the bin directory. INSTALL(TARGETS ${SNAP_BUNDLE_NAME} BUNDLE DESTINATION . COMPONENT Runtime) # On apple, we put a binary script along with a README file INSTALL(FILES ${SNAP_SOURCE_DIR}/Utilities/MacOS/BundleResources/itksnap DESTINATION . COMPONENT Runtime PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ) # Try adding a link to /usr/local/bin INSTALL(CODE "EXECUTE_PROCESS(COMMAND ln -sf /usr/local/bin usr_local_bin WORKING_DIRECTORY \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX})") INSTALL(FILES ${SNAP_SOURCE_DIR}/Utilities/MacOS/BundleResources/README.txt DESTINATION . COMPONENT Runtime) #INSTALL(FILES # ${SNAP_BINARY_DIR}/usr_local_bin # DESTINATION . COMPONENT Runtime) # Set the DS STORE SET(CPACK_DMG_DS_STORE ${SNAP_SOURCE_DIR}/Utilities/MacOS/BundleResources/dsstore.bin) SET(CPACK_DMG_BACKGROUND_IMAGE ${SNAP_SOURCE_DIR}/Utilities/MacOS/BundleResources/background.png) IF(NOT SNAP_USE_QT4) # Include the qt4 dependent libraries include(DeployQt5) # Make sure the GIF plugin is included get_property(QT_GIF_PLUGIN TARGET Qt5::QGifPlugin PROPERTY LOCATION_RELEASE) # Install with the plugin install_qt5_executable(${SNAP_BUNDLE_NAME}.app "${QT_GIF_PLUGIN}") ELSE(NOT SNAP_USE_QT4) # Include the qt4 dependent libraries include(DeployQt4) install_qt4_executable(${SNAP_BUNDLE_NAME}.app "qsqlite;qgif") ENDIF(NOT SNAP_USE_QT4) ELSEIF(WIN32) # Install to the bin directory INSTALL(TARGETS ${SNAP_BUNDLE_NAME} RUNTIME DESTINATION bin) IF(NOT SNAP_USE_QT4) # Include the qt4 dependent libraries include(DeployQt5) # Make sure the GIF plugin is included get_property(QT_WIN_PLUGIN TARGET Qt5::QWindowsIntegrationPlugin PROPERTY LOCATION_RELEASE) get_property(QT_GIF_PLUGIN TARGET Qt5::QGifPlugin PROPERTY LOCATION_RELEASE) # Install with the plugin install_qt5_executable(bin/${SNAP_BUNDLE_NAME}.exe "${QT_GIF_PLUGIN};${QT_WIN_PLUGIN}") ELSE(NOT SNAP_USE_QT4) # Include the qt4 dependent libraries include(DeployQt4) install_qt4_executable(bin/${SNAP_BUNDLE_NAME}.exe "qsqlite;qgif") ENDIF(NOT SNAP_USE_QT4) # On windows, we have to configure NSIS SET(CPACK_NSIS_INSTALLED_ICON_NAME "ITK-SNAP.exe") SET(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} ITK-SNAP") SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.itksnap.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.itksnap.org/credits.php") SET(CPACK_NSIS_MODIFY_PATH OFF) # CMake does not yet know to install into (x64) program files or not IF(CMAKE_CL_64) SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") ENDIF(CMAKE_CL_64) # Give it a windowsy directory name SET(CPACK_PACKAGE_INSTALL_DIRECTORY "ITK-SNAP ${SNAP_VERSION_MAJOR}.${SNAP_VERSION_MINOR}") # On Win32, the executable is the actual exe SET(CPACK_PACKAGE_EXECUTABLES ITK-SNAP "ITK-SNAP") # On Win32, we must include the redistributable IF(MSVC_VERSION GREATER 1399) FIND_PROGRAM(VCREDIST_EXE vcredist_x86.exe vcredist_x64.exe) IF(VCREDIST_EXE) GET_FILENAME_COMPONENT(VCREDIST_NAME ${VCREDIST_EXE} NAME) INSTALL(FILES ${VCREDIST_EXE} DESTINATION bin) SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\bin\\\\${VCREDIST_NAME}\\\" /passive'") ENDIF(VCREDIST_EXE) ENDIF(MSVC_VERSION GREATER 1399) ELSE() # On Linux, we generate forward shared executable SUBDIRS(Utilities/Forwarding) SET(SNAP_EXE "ITK-SNAP") SET(SNAP_MAIN_INSTALL_DIR lib/snap-${SNAP_VERSION_FULL}) SET(SNAP_DATA_INSTALL_DIR ${SNAP_MAIN_INSTALL_DIR}) SET(CPACK_PACKAGE_EXECUTABLES "itksnap" "ITK-SNAP") INSTALL(TARGETS ${SNAP_BUNDLE_NAME} RUNTIME DESTINATION ${SNAP_MAIN_INSTALL_DIR}) IF(NOT SNAP_USE_QT4) # Include the qt4 dependent libraries include(DeployQt5) # Add an option to not package QT plugins. This is needed for some special builds, # for example on Debian OPTION(SNAP_PACKAGE_QT_PLUGINS "Try to package Qt5 plugins with the executable" ON) MARK_AS_ADVANCED(SNAP_PACKAGE_QT_PLUGINS) # Get the necessary plugins IF(SNAP_PACKAGE_QT_PLUGINS) get_property(QT_XCB_PLUGIN TARGET Qt5::QXcbIntegrationPlugin PROPERTY LOCATION_RELEASE) get_property(QT_GIF_PLUGIN TARGET Qt5::QGifPlugin PROPERTY LOCATION_RELEASE) # Install the plugin install_qt5_executable(${SNAP_MAIN_INSTALL_DIR}/${SNAP_EXE} "${QT_XCB_PLUGIN};${QT_GIF_PLUGIN}") ELSE(SNAP_PACKAGE_QT_PLUGINS) # Install the exe without plugins install_qt5_executable(${SNAP_MAIN_INSTALL_DIR}/${SNAP_EXE}) ENDIF(SNAP_PACKAGE_QT_PLUGINS) ELSE(NOT SNAP_USE_QT4) include(DeployQt4) install_qt4_executable(${SNAP_MAIN_INSTALL_DIR}/${SNAP_EXE} "qgif") ENDIF(NOT SNAP_USE_QT4) ENDIF() #-------------------------------------------------------------------------------- # Configure CPack # Which generator to use IF(APPLE) SET(CPACK_GENERATOR DragNDrop) SET(CPACK_EXTENSION "dmg") ELSEIF(WIN32) SET(CPACK_GENERATOR NSIS) SET(CPACK_EXTENSION "exe") ELSE(APPLE) SET(CPACK_GENERATOR TGZ) SET(CPACK_EXTENSION "tar.gz") ENDIF(APPLE) #-------------------------------------------------------------------------------- # Construct the name of the package SET(CPACK_PACKAGE_FILE_NAME_WEXT "${CPACK_PACKAGE_FILE_NAME}.${CPACK_EXTENSION}") #-------------------------------------------------------------------------------- # Set up package target include(CPack) #-------------------------------------------------------------------------------- # Uploading code to SourceForge #-------------------------------------------------------------------------------- #-------------------------------------------------------------------------------- # Configure SCP FIND_PROGRAM(SCP_PROGRAM NAMES scp DOC "Location of the scp program (optional)") MARK_AS_ADVANCED(SCP_PROGRAM) SET(SCP_ARGUMENTS "-v" CACHE STRING "Optional arguments to the scp command for uploads to SourceForge") MARK_AS_ADVANCED(SCP_ARGUMENTS) SET(SCP_USERNAME "" CACHE STRING "SourceForge.net account id for uploads") MARK_AS_ADVANCED(SCP_USERNAME) IF(NOT SNAP_USE_QT4) SET(NIGHTLY_TARGET "itksnap-nightly-${SNAP_VERSION_GIT_BRANCH}-${CPACK_SYSTEM_NAME}.${CPACK_EXTENSION}") SET(EXPERIMENTAL_TARGET "itksnap-experimental-${SNAP_VERSION_GIT_BRANCH}-${CPACK_SYSTEM_NAME}.${CPACK_EXTENSION}") ELSE(NOT SNAP_USE_QT4) SET(NIGHTLY_TARGET "itksnap-nightly-${SNAP_VERSION_GIT_BRANCH}-${CPACK_SYSTEM_NAME}-qt4.${CPACK_EXTENSION}") SET(EXPERIMENTAL_TARGET "itksnap-experimental-${SNAP_VERSION_GIT_BRANCH}-${CPACK_SYSTEM_NAME}-qt4.${CPACK_EXTENSION}") ENDIF(NOT SNAP_USE_QT4) SET(SCP_ROOT "frs.sourceforge.net:/home/frs/project/i/it/itk-snap/itk-snap") #-------------------------------------------------------------------------------- # Create targets ADD_CUSTOM_TARGET(upload_nightly VERBATIM COMMAND "${SCP_PROGRAM}" ${SCP_ARGUMENTS} ${CPACK_PACKAGE_FILE_NAME_WEXT} ${SCP_USERNAME},itk-snap@${SCP_ROOT}/Nightly/${NIGHTLY_TARGET} DEPENDS ${CPACK_TARGET} WORKING_DIRECTORY ${SNAP_BINARY_DIR} COMMENT "Uploading package ${CPACK_PACKAGE_FILE_NAME_WEXT} to SourceForge.net as ${NIGHTLY_TARGET}") ADD_CUSTOM_TARGET(upload_experimental VERBATIM COMMAND "${SCP_PROGRAM}" ${SCP_ARGUMENTS} ${CPACK_PACKAGE_FILE_NAME_WEXT} ${SCP_USERNAME},itk-snap@${SCP_ROOT}/Experimental/${EXPERIMENTAL_TARGET} DEPENDS ${CPACK_TARGET} WORKING_DIRECTORY ${SNAP_BINARY_DIR} COMMENT "Uploading package ${CPACK_PACKAGE_FILE_NAME_WEXT} to SourceForge.net as ${EXPERIMENTAL_TARGET}") itksnap-3.4.0/COPYING000066400000000000000000001045131263013355200142260ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . itksnap-3.4.0/CTestConfig.cmake000066400000000000000000000010431263013355200163370ustar00rootroot00000000000000## This file should be placed in the root directory of your project. ## Then modify the CMakeLists.txt file in the root directory of your ## project to incorporate the testing dashboard. ## ## # The following are required to submit to the CDash dashboard: ## ENABLE_TESTING() ## INCLUDE(CTest) set(CTEST_PROJECT_NAME "ITK-SNAP 3.4") set(CTEST_NIGHTLY_START_TIME "00:00:00 UTC") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "itksnap.org") set(CTEST_DROP_LOCATION "/cdash/submit.php?project=ITK-SNAP+3.4") set(CTEST_DROP_SITE_CDASH TRUE) itksnap-3.4.0/Common/000077500000000000000000000000001263013355200144175ustar00rootroot00000000000000itksnap-3.4.0/Common/AbstractModel.cxx000066400000000000000000000064071263013355200176760ustar00rootroot00000000000000#include "AbstractModel.h" #include "EventBucket.h" #include #include #include #include "SNAPEventListenerCallbacks.h" AbstractModel::AbstractModel() : itk::Object() { m_EventBucket = new EventBucket(); } AbstractModel::~AbstractModel() { // Cleanup delete m_EventBucket; for(std::list::iterator it = m_Rebroadcast.begin(); it != m_Rebroadcast.end(); ++it) { delete *it; } } void AbstractModel::Update() { if(!m_EventBucket->IsEmpty()) { #ifdef SNAP_DEBUG_EVENTS if(flag_snap_debug_events) { std::cout << "UPDATE called in model " << this->GetNameOfClass() << " [" << this << "] " << " with " << *m_EventBucket << std::endl << std::flush; } #endif this->OnUpdate(); m_EventBucket->Clear(); } } AbstractModel::Rebroadcaster ::Rebroadcaster(AbstractModel *model, const itk::EventObject &evt) { m_Model = model; m_Event = evt.MakeObject(); } AbstractModel::Rebroadcaster ::~Rebroadcaster() { delete m_Event; } void AbstractModel::Rebroadcaster ::Broadcast(itk::Object *source, const itk::EventObject &evt) { this->Broadcast((const itk::Object *) source, evt); } void AbstractModel::Rebroadcaster ::Broadcast(const itk::Object *source, const itk::EventObject &evt) { #ifdef SNAP_DEBUG_EVENTS if(flag_snap_debug_events) { std::cout << "REBROADCAST event " << evt.GetEventName() << " from " << source->GetNameOfClass() << " [" << source << "] " << " as " << m_Event->GetEventName() << " from " << m_Model->GetNameOfClass() << " [" << m_Model << "] " << std::endl << std::flush; } #endif // SNAP_DEBUG_EVENTS m_Model->m_EventBucket->PutEvent(evt, source); m_Model->InvokeEvent(*m_Event); } void AbstractModel::Rebroadcaster ::BroadcastVTK(vtkObject *source, unsigned long event, void *) { #ifdef SNAP_DEBUG_EVENTS if(flag_snap_debug_events) { std::cout << "REBROADCAST VTK event " << vtkCommand::GetStringFromEventId(event) << " from " << source->GetClassName() << " [" << source << "] " << " as " << m_Event->GetEventName() << " from " << m_Model->GetNameOfClass() << " [" << m_Model << "] " << std::endl << std::flush; } #endif // SNAP_DEBUG_EVENTS // TODO: how to package this up for the bucket? m_Model->m_EventBucket->PutEvent(VTKEvent(), NULL); m_Model->InvokeEvent(*m_Event); } #include "Rebroadcaster.h" unsigned long AbstractModel::Rebroadcast( itk::Object *src, const itk::EventObject &srcEvent, const itk::EventObject &trgEvent) { /* Rebroadcaster *reb = new Rebroadcaster(this, trgEvent); m_Rebroadcast.push_back(reb); return AddListenerPair(src, srcEvent, reb, &Rebroadcaster::Broadcast, &Rebroadcaster::Broadcast); */ return ::Rebroadcaster::Rebroadcast(src, srcEvent, this, trgEvent, m_EventBucket); } unsigned long AbstractModel::Rebroadcast( vtkObject *src, unsigned long srcEvent, const itk::EventObject &trgEvent) { Rebroadcaster *reb = new Rebroadcaster(this, trgEvent); m_Rebroadcast.push_back(reb); return AddListenerVTK(src, srcEvent, reb, &Rebroadcaster::BroadcastVTK); } itksnap-3.4.0/Common/AbstractModel.h000066400000000000000000000063161263013355200173220ustar00rootroot00000000000000#ifndef ABSTRACTMODEL_H #define ABSTRACTMODEL_H #include "itkObject.h" #include "itkObjectFactory.h" #include "SNAPCommon.h" #include "SNAPEvents.h" #include "EventBucket.h" #include #include class vtkObject; /** \class AbstractModel \brief Parent class for all UI models This class provides basic common functionality for all UI models. This includes the event/update mechanism. */ class AbstractModel : public itk::Object { public: /* typedef AbstractModel Self; typedef itk::Object Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; itkTypeMacro(AbstractModel, itk::Object) itkNewMacro(Self) */ irisITKObjectMacro(AbstractModel, itk::Object) /** Call this function to update the model based on events that have been fired since the last time the model was updated. This function simply checks if the event bucket is empty; if not, it calls the OnUpdate() protected virtual method. Subclassers should reimplement OnUpdate(). The event bucket is emptied at the end of this call. */ void Update(); /** Listen to events of type srcEvent on the object src, and rebroadcast them as event trgEvent. In the process, record the srcEvent in the event bucket. This is the main mechanism for model updates. The model listens to events occurring upstream. When an event occurs, the model only records the event and invokes its own event, to which the view objects downstream are listening. It is then the view's responsibility to call the Update() function on the model. This function checks what events are in the event bucket, and processes them in an orderly fashion. */ unsigned long Rebroadcast( itk::Object *src, const itk::EventObject &srcEvent, const itk::EventObject &trgEvent); /** We can also rebroadcast events from vtk objects. This is handled similar to ITK but events are just unsigned long values. The event bucket will include a VTKEvent() object with NULL caller if an event from VTK occurred. (at the present, EventBucket does not support differentiating between different kinds of VTK events and callers). */ unsigned long Rebroadcast(vtkObject *src, unsigned long srcEvent, const itk::EventObject &trgEvent); protected: AbstractModel(); virtual ~AbstractModel(); /** Helper class for AbstractModel used to rebroadcast events */ class Rebroadcaster { public: Rebroadcaster(AbstractModel *model, const itk::EventObject &evt); virtual ~Rebroadcaster(); void Broadcast(itk::Object *source, const itk::EventObject &evt); void Broadcast(const itk::Object *source, const itk::EventObject &evt); void BroadcastVTK(vtkObject *source, unsigned long event, void *); private: AbstractModel *m_Model; itk::EventObject *m_Event; }; /** This is the method called by Update() if there are events in the event bucket */ virtual void OnUpdate() {} // List of rebroadcasters std::list m_Rebroadcast; // Bucket that stores events fired since last call to Update() EventBucket *m_EventBucket; }; #endif // ABSTRACTMODEL_H itksnap-3.4.0/Common/AbstractPropertyContainerModel.cxx000066400000000000000000000046421263013355200233050ustar00rootroot00000000000000#include "AbstractPropertyContainerModel.h" void AbstractPropertyContainerModel::DeepCopy( const AbstractPropertyContainerModel *source) { // This method will only work if both models have the same fields in the // same order. The assertions below check that assert(m_Properties.size() == source->m_Properties.size()); PropertyMapCIter itSrc = source->m_Properties.begin(); PropertyMapIter it = m_Properties.begin(); while(itSrc != source->m_Properties.end()) { assert(it->first == itSrc->first); ConcretePropertyHolderBase *ptr_src = itSrc->second; ConcretePropertyHolderBase *ptr_trg = it->second; ptr_trg->DeepCopy(ptr_src); ++it; ++itSrc; } } bool AbstractPropertyContainerModel::operator == ( const AbstractPropertyContainerModel &source) { // This method will only work if both models have the same fields in the // same order. The assertions below check that assert(m_Properties.size() == source.m_Properties.size()); PropertyMapCIter itSrc = source.m_Properties.begin(); PropertyMapIter it = m_Properties.begin(); while(itSrc != source.m_Properties.end()) { assert(it->first == itSrc->first); ConcretePropertyHolderBase *ptr_src = itSrc->second; ConcretePropertyHolderBase *ptr_trg = it->second; if(!ptr_trg->Equals(ptr_src)) return false; ++it; ++itSrc; } return true; } bool AbstractPropertyContainerModel::operator != ( const AbstractPropertyContainerModel &source) { return !(*this == source); } unsigned long AbstractPropertyContainerModel::GetMTime() const { return this->GetTimeStamp().GetMTime(); } void AbstractPropertyContainerModel::WriteToRegistry(Registry &folder) const { for(PropertyMapCIter it = m_Properties.begin(); it != m_Properties.end(); it++) { it->second->Serialize(folder); } } void AbstractPropertyContainerModel::ReadFromRegistry(Registry &folder) { for(PropertyMapIter it = m_Properties.begin(); it != m_Properties.end(); it++) { it->second->Deserialize(folder); } // Flag this object as modified this->Modified(); } const itk::TimeStamp &AbstractPropertyContainerModel::GetTimeStamp() const { const itk::TimeStamp *ts = &AbstractModel::GetTimeStamp(); for(PropertyMapCIter it = m_Properties.begin(); it != m_Properties.end(); it++) { const itk::TimeStamp &tschild = it->second->GetPropertyTimeStamp(); if(tschild > (*ts)) ts = &tschild; } return *ts; } itksnap-3.4.0/Common/AbstractPropertyContainerModel.h000066400000000000000000000303271263013355200227310ustar00rootroot00000000000000#ifndef ABSTRACTPROPERTYCONTAINERMODEL_H #define ABSTRACTPROPERTYCONTAINERMODEL_H #include "PropertyModel.h" #include "Registry.h" /** * A helper class for AbstractPropertyContainerModel. This is a typeless parent * for ConcretePropertyHolder. */ class ConcretePropertyHolderBase : public itk::Object { public: irisITKAbstractObjectMacro(ConcretePropertyHolderBase, itk::Object) virtual void DeepCopy(const ConcretePropertyHolderBase *source) = 0; virtual bool Equals(const ConcretePropertyHolderBase *other) = 0; virtual void Serialize(Registry &folder) const = 0; virtual void Deserialize(Registry &folder) = 0; virtual const itk::TimeStamp &GetPropertyTimeStamp() const = 0; }; template class DefaultRegistrySerializationTraits { public: void Serialize(RegistryValue &entry, const TAtomic &value) const { entry << value; } void Deserialize(RegistryValue &entry, TAtomic &value, const TAtomic &deflt) const { value = entry[deflt]; } }; template class RegistryEnumSerializationTraits { public: typedef RegistryEnumMap EnumMap; typedef RegistryEnumSerializationTraits Self; RegistryEnumSerializationTraits() {} RegistryEnumSerializationTraits(const EnumMap &enummap) : m_EnumMap(enummap) {} RegistryEnumSerializationTraits(const Self &other) : m_EnumMap(other.m_EnumMap) {} void Serialize(RegistryValue &entry, const TAtomic &value) const { entry.PutEnum(m_EnumMap, value); } void Deserialize(RegistryValue &entry, TAtomic &value, const TAtomic &deflt) const { value = entry.GetEnum(m_EnumMap, deflt); } private: EnumMap m_EnumMap; }; /** * A helper class for AbstractPropertyContainerModel that holds a pointer to * a ConcretePointerModel and supports copy and serialization operations. */ template class ConcretePropertyHolder : public ConcretePropertyHolderBase { public: // ITK stuff typedef ConcretePropertyHolder Self; typedef ConcretePropertyHolderBase Superclass; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkTypeMacro(ConcretePropertyHolder, ConcretePropertyHolderBase) itkNewMacro(Self) // Held property type typedef ConcretePropertyModel PropertyType; virtual void DeepCopy(const ConcretePropertyHolderBase *source) { const Self *source_cast = static_cast(source); PropertyType *source_prop = source_cast->m_Property; m_Property->DeepCopy(source_prop); } virtual bool Equals(const ConcretePropertyHolderBase *other) { const Self *source_cast = static_cast(other); PropertyType *source_prop = source_cast->m_Property; return m_Property->Equals(source_prop); } virtual void Serialize(Registry &folder) const { TAtomic value; if(m_Property->GetValueAndDomain(value, NULL)) { m_Traits.Serialize(folder.Entry(m_RegistryKey), value); } } virtual void Deserialize(Registry &folder) { RegistryValue &rv = folder.Entry(m_RegistryKey); if(!rv.IsNull()) { TAtomic value; m_Traits.Deserialize(rv, value, m_Property->GetValue()); m_Property->SetValue(value); } } virtual const itk::TimeStamp &GetPropertyTimeStamp() const { return m_Property->GetTimeStamp(); } irisGetSetMacro(Property, PropertyType *) irisGetSetMacro(RegistryKey, const std::string &) irisGetSetMacro(Traits, const TRegistryTraits &) protected: // Pointer to the property SmartPtr m_Property; // Registry key for serialization std::string m_RegistryKey; // A traits object for serialization TRegistryTraits m_Traits; }; /** * This class is intended to serve as a parent class for models that hold * a (large) number of individual ConcretePropertyModel objects. For example, * we may want a FooSettings class that holds a bunch of properties that * influence the behavior of Foo. Normally, one would write a struct with * these different fields: * * struct FooSettings { * int FooWidth; * double FooAspectRatio; * bool IsFooable; * } * * However, in the model/view paradigm in ITK-SNAP, we want each of the fields * to be represented by a PropertyModel so that observers can listen to changes * in the individual fields, and so that the fields can be hooked up to GUI * widgets. So instead, FooSettings is represented like this: * * class FooSettingsModel : public AbstractModel * { * public: * ... * irisRangedPropertyAccessMacro(FooWidth, int) * irisRangedPropertyAccessMacro(FooAspectRatio, double) * irisSimplePropertyAccessMacro(IsFooable, bool) * protected: * ... * SmartPtr m_FooWidth; * SmartPtr m_FooAspectRatio; * SmartPtr m_IsFooable; * } * * However, this class does not automatically offer comparison operators or * means to copy the model (deep copy). Writing that code by hand is a pain, * especially when there are lots of fields. So this is where the class * AbstractPropertyContainerModel comes in. It allows the fields to be registered * in the constructor, and provides default implementations of the comparison * operators and DeepCopy function. * * The class AbstractPropertyContainerModel is a way to create a container of * heterogeneous property models that supports comparison, serialization, and * copy operations with little coding. The FooSettingsModel class will still have * a similar structure to the one above. * * class FooSettingsModel : public AbstractPropertyContainerModel * { * public: * ... * irisRangedPropertyAccessMacro(FooWidth, int) * irisRangedPropertyAccessMacro(FooAspectRatio, double) * irisSimplePropertyAccessMacro(IsFooable, bool) * protected: * ... * SmartPtr m_FooWidth; * SmartPtr m_FooAspectRatio; * SmartPtr m_IsFooable; * } * * The difference is that in the constructor, when the properties are created * using the NewSimpleProperty/NewRangedProperty methods, they are automatically * added to an internal list, allowing comparison, copy, and serialization. * * FooSettingsModel::FooSettingsModel * { * m_FooWidth = NewRangedProperty("FooWidth", 2, 0, 4, 1); * m_FooAspectRatio = NewSimpleProperty("FooAspectRatio", 0.25, 0.0, 1.0, 0.01); * m_IsFooable = NewSimpleProperty("FooWidth", true); * } */ class AbstractPropertyContainerModel : public AbstractModel { public: irisITKObjectMacro(AbstractPropertyContainerModel, AbstractModel) FIRES(ChildPropertyChangedEvent) void DeepCopy(const AbstractPropertyContainerModel *source); virtual bool operator == (const AbstractPropertyContainerModel &source); virtual bool operator != (const AbstractPropertyContainerModel &source); /** Return this objects modified time. This will return the latest of our * own modified time and the modified times of all the children */ virtual unsigned long GetMTime() const; virtual const itk::TimeStamp &GetTimeStamp() const; void WriteToRegistry(Registry &folder) const; void ReadFromRegistry(Registry &folder); protected: // Register a child model with this class. This should be called in the // constructor when the model is created. The model should be a concrete // property model. This method should only be called in the constructor template SmartPtr< ConcretePropertyModel > RegisterProperty(const std::string &key, SmartPtr< ConcretePropertyModel > model) { typedef DefaultRegistrySerializationTraits RegTraits; typedef ConcretePropertyHolder HolderType; SmartPtr holder = HolderType::New(); holder->SetProperty(model); holder->SetRegistryKey(key); m_Properties.insert(std::make_pair((std::string)key, (HolderPointer)holder)); // Propagate the modification events from the property Rebroadcast(model, ValueChangedEvent(), ChildPropertyChangedEvent()); Rebroadcast(model, DomainChangedEvent(), ChildPropertyChangedEvent()); return model; } // Register a child model with an enum atomic type. The third parameter is // the enum-to-string mapping used to serialize the enum. template SmartPtr< ConcretePropertyModel > RegisterEnumProperty(const std::string &key, SmartPtr< ConcretePropertyModel > model, const RegistryEnumMap &enummap) { typedef RegistryEnumSerializationTraits RegTraits; RegTraits traits(enummap); typedef ConcretePropertyHolder HolderType; SmartPtr holder = HolderType::New(); holder->SetProperty(model); holder->SetRegistryKey(key); holder->SetTraits(traits); m_Properties.insert(std::make_pair((std::string)key, (HolderPointer)holder)); // Propagate the modification events from the property Rebroadcast(model, ValueChangedEvent(), ChildPropertyChangedEvent()); Rebroadcast(model, DomainChangedEvent(), ChildPropertyChangedEvent()); return model; } // A convenience method that creates a new simple concrete property and // then registers it using RegisterModel template SmartPtr< ConcretePropertyModel > NewSimpleProperty(const std::string &key, const TAtomic &value) { return RegisterProperty(key, NewSimpleConcreteProperty(value)); } // A convenience method that creates a new simple concrete property and // then registers it using RegisterModel template SmartPtr< ConcretePropertyModel > > NewRangedProperty(const std::string &key, const TAtomic &value, const TAtomic &minval, const TAtomic &maxval, const TAtomic &step) { return RegisterProperty(key, NewRangedConcreteProperty(value, minval, maxval, step)); } // A convenience method that creates a new simple concrete property and // then registers it using RegisterModel template SmartPtr< ConcretePropertyModel > NewSimpleEnumProperty(const std::string &key, const TAtomic &value, const RegistryEnumMap &enummap) { return RegisterEnumProperty(key, NewSimpleConcreteProperty(value), enummap); } private: // The storage for the fields typedef SmartPtr HolderPointer; typedef std::map PropertyMap; typedef PropertyMap::iterator PropertyMapIter; typedef PropertyMap::const_iterator PropertyMapCIter; PropertyMap m_Properties; }; /* * These typedefs are commented out because I felt that they made the code too * hard to read. They can reduce coding, but can sow confusion. * #define apcmRangedPropertyAccessMacro(name,type) \ virtual AbstractPropertyModel > * Get##name##Model () const \ { return GetProperty >("##name##"); } \ virtual void Set##name (type _arg) \ { this->Get##name##Model()->SetValue(_arg); } \ virtual type Get##name () const \ { return this->Get##name##Model()->GetValue(); } #define apcmSimplePropertyAccessMacro(name,type) \ virtual AbstractPropertyModel * Get##name##Model () const \ { return GetProperty("##name##"); } \ virtual void Set##name (type _arg) \ { this->Get##name##Model()->SetValue(_arg); } \ virtual type Get##name () const \ { return this->Get##name##Model()->GetValue(); } #define apcmGenericPropertyAccessMacro(name,type,domaintype) \ virtual AbstractPropertyModel * Get##name##Model () const \ { return GetProperty("##name##"); } \ virtual void Set##name (type _arg) \ { this->Get##name##Model()->SetValue(_arg); } \ virtual type Get##name () const \ { return this->Get##name##Model()->GetValue(); } */ #endif // ABSTRACTPROPERTYCONTAINERMODEL_H itksnap-3.4.0/Common/CommandLineArgumentParser.cxx000066400000000000000000000117561263013355200222230ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: CommandLineArgumentParser.cxx,v $ Language: C++ Date: $Date: 2008/12/01 21:27:25 $ Version: $Revision: 1.4 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #include "CommandLineArgumentParser.h" #include #include using namespace std; void CommandLineArgumentParser ::AddOption(const char *name, int nParameters) { // Create a structure for the command OptionType option; option.CommonName = string(name); option.NumberOfParameters = nParameters; // Add the option to the map m_OptionMap[string(name)] = option; } void CommandLineArgumentParser ::AddSynonim(const char *option, const char *synonim) { string strOption(option); string strSynonim(synonim); // The option should exist! assert(m_OptionMap.find(strOption) != m_OptionMap.end()); // Create a new option object OptionType o; o.NumberOfParameters = m_OptionMap[strOption].NumberOfParameters; o.CommonName = strOption; // Insert the option into the map m_OptionMap[strSynonim] = o; } bool CommandLineArgumentParser ::TryParseCommandLine(int argc, char *argv[], CommandLineArgumentParseResult &outResult, bool failOnUnknownTrailingParameters, int &argc_out) { // Clear the result outResult.Clear(); // Go through the arguments for(argc_out=1; argc_out < argc; argc_out++) { // Get the next argument string arg(argv[argc_out]); // Check if the argument is known if(m_OptionMap.find(arg) == m_OptionMap.end()) { if(failOnUnknownTrailingParameters) { // Unknown argument found cerr << "Unrecognized command line option '" << arg << "'" << endl; return false; } else if(arg[0] == '-') { cerr << "Ignoring unknown command line option '" << arg << "'" << endl; continue; } else { return true; } } // Check if the number of parameters is correct int nParameters = m_OptionMap[arg].NumberOfParameters; if(nParameters > 0 && argc_out+nParameters >= argc) { // Too few parameters cerr << "Too few parameters to command line option '" << arg << "'" << endl; return false; } // If the number of parameters is negative, read all parameters that are // not recognized options if(nParameters < 0) { nParameters = 0; for(int j = argc_out+1; j < argc; j++, nParameters++) if(m_OptionMap.find(argv[j]) != m_OptionMap.end()) break; } // Tell the result that the option has been encountered outResult.AddOption(m_OptionMap[arg].CommonName,nParameters); // Pass in the parameters for(int j=0;j. ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __CommandLineArgumentParser_h_ #define __CommandLineArgumentParser_h_ #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #pragma warning ( disable : 4503 ) #endif #include #include #include #include /** * \class CommandLineArgumentParseResult * \brief Object returned by CommandLineArgumentParser * \see CommandLineArgumentParser */ class CommandLineArgumentParseResult { public: /** Check whether the option was passed in or not */ bool IsOptionPresent(const char *option); /** Get one of the parameters to the option */ const char *GetOptionParameter(const char *option, unsigned int number = 0); /** Get the number of parameters for the option */ int GetNumberOfOptionParameters(const char *option); private: typedef std::vector< std::string > ParameterArrayType; typedef std::map< std::string, ParameterArrayType> OptionMapType; void Clear(); /** * @brief Add an option with specified number of parameters. The number of * parameters may be negative, in which case all non-options trailing the * command are read as parameters. * @param option * @param nParms */ void AddOption(const std::string &option, int nParms); void AddParameter(const std::string &option, const std::string ¶meter); OptionMapType m_OptionMap; friend class CommandLineArgumentParser; }; /** * \class CommandLineArgumentParser * \brief Used to parse command line arguments and come back with a list * of parameters. * Usage: * \code * // Set up the parser * CommandLineArgumentParser parser; * parser.AddOption("-f",1); * parser.AddSynonim("-f","--filename"); * parser.AddOption("-v",0); * parser.AddSynonim("-v","--verbose"); * * // Use the parser * CommandLineArgumentParseResult result; * if(parser.TryParseCommandLine(argc,argv,result)) { * if(result.IsOptionPresent("-f")) * cout << "Filename " << result.GetOptionParameter("-f") << endl; * ... * } * \endcode */ class CommandLineArgumentParser { public: /** Add an option with 0 or more parameters (words that follow it) */ void AddOption(const char *name, int nParameters); /** Add a different string that envokes the same option (--file and -f) */ void AddSynonim(const char *option, const char *synonim); /** Try processing a command line. Returns false if something breaks */ bool TryParseCommandLine(int argc, char *argv[], CommandLineArgumentParseResult &outResult, bool failOnUnknownTrailingParameters) { int junk; return this->TryParseCommandLine(argc, argv, outResult, failOnUnknownTrailingParameters, junk); } /** This version returns the index to the first unparsed parameter */ bool TryParseCommandLine(int argc, char *argv[], CommandLineArgumentParseResult &outResult, bool failOnUnknownTrailingParameters, int &argc_out); private: private: // Synonim list type typedef std::list< std::string > NameListType; typedef struct { std::string CommonName; unsigned int NumberOfParameters; } OptionType; typedef std::map< std::string, OptionType> OptionMapType; OptionMapType m_OptionMap; }; #endif // __CommandLineArgumentParser_h_ itksnap-3.4.0/Common/Credits.h000066400000000000000000000105061263013355200161670ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Credits.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:11 $ Version: $Revision: 1.2 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __Credits_h_ #define __Credits_h_ static const char itkCredits[] = "ITK Integration performed under \n" "Purchase Order No. 467-MZ-202446-1 \n" "of the U.S. National Library of Medicine \n" "Oct. 2002 - Sep. 2003 \n" "\n" "Contractor : Cognitica Corporation \n" "Contract PI : Dr. Daniel S. Fritsch \n" "Lead Developer: Dr. Paul A. Yushkevich \n" "Consultants : Dr. Guido Gerig (UNC-CH) \n" " Dr. Stephen R. Aylward \n"; static const char credits[] = "\ University of North Carolina at Chapel Hill \n\ Department of Computer Science \n\ \n\ Client: Dr. Guido Gerig \n\ Comp145 Manager: Kye Hedlund \n\ \n\ SnAP Comp145 development team: \n\ Konstantin Bobkov \n\ Nathan Moon, Producer \n\ Thorsten Scheuermann, Technical Director \n\ Nathan Talbert \n\ \n\ IRIS2000 Comp145 development team: \n\ Ashraf Farrag, Producer \n\ Amy Henderson \n\ Sean Ho, Technical Director \n\ Robin Munesato \n\ Ming Yu \n\ \n\ IRIS99 Comp145 development team:\n\ David Gregg \n\ Eric Larsen \n\ Arun Neelamkavil, Producer \n\ Sanjay Sthapit \n\ Chris Wynn, Technical Director \n\ \n\ Support/Maintenance team: \n\ Sean Ho (2000) \n"; #endif // __Credits_h_ /* *$Log: Credits.h,v $ *Revision 1.2 2007/12/30 04:05:11 pyushkevich *GPL License * *Revision 1.1 2006/12/02 04:22:09 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.4 2003/10/02 13:43:12 pauly *ENH: Development during September code freeze * *Revision 1.7 2003/09/11 13:50:29 pauly *FIX: Enabled loading of images with different orientations *ENH: Implemented image save and load operations * *Revision 1.3 2003/08/27 14:03:20 pauly *FIX: Made sure that -Wall option in gcc generates 0 warnings. *FIX: Removed 'comment within comment' problem in the cvs log. * *Revision 1.2 2003/08/27 04:57:45 pauly *FIX: A large number of bugs has been fixed for 1.4 release * *Revision 1.1 2003/07/12 04:53:16 pauly *Initial checkin of SNAP application to the InsightApplications tree * *Revision 1.5 2003/07/12 01:34:17 pauly *More final changes before ITK checkin * *Revision 1.4 2003/07/11 23:29:46 pauly **** empty log message *** * *Revision 1.3 2003/07/11 21:23:36 pauly *Renames and code cleanup for ITK checkin (temp) * *Revision 1.2 2003/06/08 23:27:56 pauly *Changed variable names using combination of ctags, egrep, and perl. * *Revision 1.1 2003/03/07 19:29:47 pauly *Initial checkin * *Revision 1.1.1.1 2002/12/10 01:35:36 pauly *Started the project repository * * *Revision 1.5 2002/04/25 17:09:22 moon *Changed credits so we come first! There's no scrollbar on the credits *display, so it's useless to put our stuff at the bottom. * *Revision 1.4 2002/04/24 01:33:04 scheuerm *Fixed a compile problem on Windows * *Revision 1.3 2002/04/24 01:04:42 talbert *Added this semester's credits. * *Revision 1.2 2002/03/08 14:06:20 moon *Added Header and Log tags to all files */ itksnap-3.4.0/Common/EventBucket.cxx000066400000000000000000000033201263013355200173600ustar00rootroot00000000000000#include "EventBucket.h" unsigned long EventBucket::m_GlobalMTime = 1; EventBucket::EventBucket() { m_MTime = m_GlobalMTime++; } EventBucket::~EventBucket() { Clear(); } void EventBucket::Clear() { m_Lock.Lock(); // Remove all the event objects for(BucketIt it = m_Bucket.begin(); it != m_Bucket.end(); ++it) { delete(it->first); } m_Bucket.clear(); m_Lock.Unlock(); m_MTime = m_GlobalMTime++; } bool EventBucket::HasEvent(const itk::EventObject &evt, const itk::Object *source) const { m_Lock.Lock(); // Search for the event. Buckets are never too large so a linear search is fine for(BucketIt it = m_Bucket.begin(); it != m_Bucket.end(); ++it) { const BucketEntry &entry = *it; std::string echeck = evt.GetEventName(); std::string eentry = entry.first->GetEventName(); if(evt.CheckEvent(entry.first) && (source == NULL || source == entry.second)) { m_Lock.Unlock(); return true; } } m_Lock.Unlock(); return false; } bool EventBucket::IsEmpty() const { return m_Bucket.size() == 0; } void EventBucket::PutEvent(const itk::EventObject &evt, const itk::Object *source) { if(!this->HasEvent(evt, source)) { BucketEntry entry; entry.first = evt.MakeObject(); entry.second = source; m_Lock.Lock(); m_Bucket.insert(entry); m_Lock.Unlock(); m_MTime = m_GlobalMTime++; } } std::ostream& operator<<(std::ostream& sink, const EventBucket& eb) { sink << "EventBucket["; for(EventBucket::BucketIt it = eb.m_Bucket.begin(); it != eb.m_Bucket.end();) { sink << it->first->GetEventName() << "(" << it->second << ")"; if(++it != eb.m_Bucket.end()) sink << ", "; } sink << "]"; return sink; } itksnap-3.4.0/Common/EventBucket.h000066400000000000000000000040171263013355200170110ustar00rootroot00000000000000#ifndef EVENTBUCKET_H #define EVENTBUCKET_H #include "SNAPEvents.h" #include "itkSimpleFastMutexLock.h" #include #include namespace itk { class Object; } /** A simple 'bucket' that stores events. You can easily add events to the bucket and check if events are present there. TODO: the bucket should keep track of objects that fired the event, not only the event types! */ class EventBucket { public: EventBucket(); virtual ~EventBucket(); /** * @brief Add an event to the bucket. */ void PutEvent(const itk::EventObject &evt, const itk::Object *source); /** * @brief Check if the bucket has an event from a source (or from all * sources if the second parameter has the default value of NULL). This * method checks for child events, so if AEvent is a parent BEvent and the * bucket holds a BEvent, then HasEvent(AEvent()) will return true. */ bool HasEvent(const itk::EventObject &evt, const itk::Object *source = NULL) const; bool IsEmpty() const; void Clear(); unsigned long GetMTime() const { return m_MTime; } friend std::ostream& operator<<(std::ostream& sink, const EventBucket& eb); protected: /** * The bucket entry consists of a pointer to the event, which the * bucket owns and destroys when it is destroyed or cleared, and the * pointer to the originator the event. */ typedef std::pair BucketEntry; typedef std::set BucketType; typedef BucketType::iterator BucketIt; BucketType m_Bucket; itk::SimpleFastMutexLock m_Lock; /** Each bucket has a unique id. This allows code to check whether or not * it has already handled a bucket or not. This should not really be needed * because event handlers should never get called twice with the same * bucket, but it seems that in Qt this does happen sometimes */ unsigned long m_MTime; static unsigned long m_GlobalMTime; }; // IO operator std::ostream& operator<<(std::ostream& sink, const EventBucket& eb); #endif // EVENTBUCKET_H itksnap-3.4.0/Common/GPUSettings.h.in000066400000000000000000000000331263013355200173450ustar00rootroot00000000000000#cmakedefine SNAP_USE_GPU itksnap-3.4.0/Common/HistoryManager.cxx000066400000000000000000000100551263013355200201000ustar00rootroot00000000000000#include "HistoryManager.h" #include "SystemInterface.h" #include "Registry.h" #include "itksys/SystemTools.hxx" #include const unsigned int HistoryManager::HISTORY_SIZE_LOCAL = 5; const unsigned int HistoryManager::HISTORY_SIZE_GLOBAL = 20; HistoryManager::HistoryManager() { } HistoryManager::ConcreteHistoryModel *HistoryManager::GetHistory(const std::string &category, HistoryMap &hmap) { // Does the history exist? HistoryMap::iterator it = hmap.find(category); if(it == hmap.end()) { ConcreteHistoryModelPtr model = ConcreteHistoryModel::New(); hmap.insert(std::make_pair(category, model)); return model; } else return it->second; } void HistoryManager ::SaveHistory(Registry &folder, HistoryManager::HistoryMap &hmap) { // Write all the histories to the registry for(HistoryMap::iterator it = hmap.begin(); it != hmap.end(); it++) { ConcreteHistoryModel *model = it->second; folder.Folder(it->first).PutArray(model->GetValue()); } } void HistoryManager ::LoadHistory(Registry &folder, HistoryManager::HistoryMap &hmap) { hmap.clear(); // Read all the relevant histories from the file. We do this dynamically // although it would also have made sense to hard code here the list of // all histories. I guess it does not really matter. Registry::StringListType historyNames; folder.GetFolderKeys(historyNames); for(Registry::StringListType::iterator it = historyNames.begin(); it != historyNames.end(); it++) { // Histories are created on demand - so we must call the get model method, // which will create and store the history model ConcreteHistoryModel *model = GetHistory(*it, hmap); model->SetValue(folder.Folder(*it).GetArray(std::string(""))); } } void HistoryManager ::UpdateHistoryList(ConcreteHistoryModel *model, const std::string &file, unsigned int maxsize) { // Get the list (passing by value here, but these lists are not huge) HistoryListType array = model->GetValue(); // Remove all occurences of the file from the array array.erase(std::remove(array.begin(), array.end(), file), array.end()); // Append the file to the end of the array array.push_back(file); // Trim the array to appropriate size if(array.size() > maxsize) array.erase(array.begin(), array.begin() + array.size() - maxsize); // Put the new array back in the model model->SetValue(array); } void HistoryManager::UpdateHistory( const std::string &category, const std::string &filename, bool make_local) { // Create a string for the new file std::string fullpath = itksys::SystemTools::CollapseFullPath(filename.c_str()); // Get the current history registry UpdateHistoryList(GetHistory(category, m_GlobalHistory), fullpath, HISTORY_SIZE_GLOBAL); if(make_local) UpdateHistoryList(GetHistory(category, m_LocalHistory), fullpath, HISTORY_SIZE_LOCAL); // TODO: right now, no events are fired to notify changes to the HistoryManager as // a whole. Also, there is no mechanism for sharing histories across sessions. } void HistoryManager::DeleteHistoryItem( const std::string &category, const std::string &file) { // Delete all occurences of file from the history ConcreteHistoryModel *model = GetHistory(category, m_GlobalHistory); HistoryListType hist = model->GetValue(); hist.erase(std::remove(hist.begin(), hist.end(), file), hist.end()); model->SetValue(hist); } void HistoryManager::ClearLocalHistory() { m_LocalHistory.clear(); } HistoryManager::AbstractHistoryModel * HistoryManager::GetLocalHistoryModel(const std::string &category) { return GetHistory(category, m_LocalHistory); } HistoryManager::AbstractHistoryModel * HistoryManager::GetGlobalHistoryModel(const std::string &category) { return GetHistory(category, m_GlobalHistory); } HistoryManager::HistoryListType HistoryManager::GetLocalHistory(const std::string &category) { return GetHistory(category, m_LocalHistory)->GetValue(); } HistoryManager::HistoryListType HistoryManager::GetGlobalHistory(const std::string &category) { return GetHistory(category, m_GlobalHistory)->GetValue(); } itksnap-3.4.0/Common/HistoryManager.h000066400000000000000000000112631263013355200175270ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SystemInterface.h,v $ Language: C++ Date: $Date: 2010/04/14 10:06:23 $ Version: $Revision: 1.11 $ Copyright (c) 2003-2013 Paul A. Yushkevich and Guido Gerig This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef HISTORYMANAGER_H #define HISTORYMANAGER_H #include #include #include #include "PropertyModel.h" class SystemInterface; class Registry; /** * @brief The HistoryManager class * This class manages file IO history in the SNAP application. Starting with * version 3.0, there is a global history for each kind of file (main image, * segmentation, label descriptions, etc) and a history associated with each * main image. This class maintains the history entries. Each history is a * "PropertyModel", so other objects can register to observe changes to the * history. */ class HistoryManager { public: // Typedef for history lists typedef std::vector HistoryListType; // Typedef for the history model typedef AbstractPropertyModel AbstractHistoryModel; /** Update the history in one of the named categories. The last parameter is whether the history entry should be made local, i.e., associated with the currently loaded main image. */ void UpdateHistory(const std::string &category, const std::string &file, bool make_local); /** Delete an item from the global history */ void DeleteHistoryItem(const std::string &category, const std::string &file); /** Clear the local history - should be done when the main image is unloaded */ void ClearLocalHistory(); /** Get the model encapsulating local history */ AbstractHistoryModel *GetLocalHistoryModel(const std::string &category); /** Get the model encapsulating local history */ AbstractHistoryModel *GetGlobalHistoryModel(const std::string &category); /** Get the local history in a category (pass by value) */ HistoryListType GetLocalHistory(const std::string &category); /** Get the global history in a category (pass by value)*/ HistoryListType GetGlobalHistory(const std::string &category); /** Create a registry holding the local history */ void SaveLocalHistory(Registry &folder) { SaveHistory(folder, m_LocalHistory); } /** Read a registry holding the local history */ void LoadLocalHistory(Registry &folder) { LoadHistory(folder, m_LocalHistory); } /** Create a registry holding the local history */ void SaveGlobalHistory(Registry &folder) { SaveHistory(folder, m_GlobalHistory); } /** Read a registry holding the local history */ void LoadGlobalHistory(Registry &folder) { LoadHistory(folder, m_GlobalHistory); } HistoryManager(); protected: static const unsigned int HISTORY_SIZE_LOCAL, HISTORY_SIZE_GLOBAL; // Typedef the concrete history model typedef ConcretePropertyModel ConcreteHistoryModel; typedef SmartPtr ConcreteHistoryModelPtr; // Array of histories for different types of files typedef std::map HistoryMap; HistoryMap m_LocalHistory, m_GlobalHistory; ConcreteHistoryModel *GetHistory(const std::string &category, HistoryMap &hmap); void SaveHistory(Registry &folder, HistoryMap &hmap); void LoadHistory(Registry &folder, HistoryMap &hmap); void UpdateHistoryList( ConcreteHistoryModel *model, const std::string &file, unsigned int maxsize); // The system interface class SystemInterface *m_SystemInterface; }; #endif // HISTORYMANAGER_H itksnap-3.4.0/Common/IPCHandler.cxx000066400000000000000000000126451263013355200170640ustar00rootroot00000000000000#include "IPCHandler.h" #include #include #include #include using namespace std; #ifdef WIN32 #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif //_WIN32_WINNT #define _WIN32_WINNT 0x0600 #include #include #include #include #include #else #include #include #include #include #include #include #endif void IPCHandler::Attach(const char *path, short version, size_t message_size) { // Initialize the data pointer m_SharedData = NULL; // Store the size of the actual message m_MessageSize = message_size; // Save the protocol version m_ProtocolVersion = version; // Determine size of shared memory size_t msize = message_size + sizeof(Header); #ifdef WIN32 // Create a shared memory block (key based on the preferences file) m_Handle = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, msize, path); // If the return value is NULL, something failed if(m_Handle) { // Attach to the shared memory block m_SharedData = MapViewOfFile(m_Handle, FILE_MAP_ALL_ACCESS, 0, 0, msize); } #else // Create a unique key for this user key_t keyid = ftok(path, version); // Get a handle to shared memory m_Handle = shmget(keyid, msize, IPC_CREAT | 0644); // There may be an error! if(m_Handle < 0) { cerr << "Shared memory (shmget) error: " << strerror(errno) << endl; cerr << "This error may occur if a user is running two versions of ITK-SNAP" << endl; cerr << "Multisession support is disabled" << endl; m_SharedData = NULL; } else { // Get a pointer to shared data block m_SharedData = shmat(m_Handle, (void *) 0, 0); // Check errors again if(!m_SharedData) { cerr << "Shared memory (shmat) error: " << strerror(errno) << endl; cerr << "Multisession support is disabled" << endl; m_SharedData = NULL; } } #endif // Set the user data pointer if(m_SharedData) { Header *hptr = static_cast
(m_SharedData); m_UserData = static_cast(hptr + 1); } else { m_UserData = NULL; } } bool IPCHandler::Read(void *target_ptr) { // Must have some shared memory if(!m_SharedData) return false; // Read the header, make sure it's the right version number Header *header = static_cast
(m_SharedData); if(header->version != m_ProtocolVersion) return false; // Store the last sender / id m_LastSender = header->sender_pid; m_LastReceivedMessageID = header->message_id; // Copy the message to the target pointer memcpy(target_ptr, m_UserData, m_MessageSize); // Success! return true; } bool IPCHandler::IsProcessRunning(int pid) { #ifdef WIN32 HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, (DWORD) pid); DWORD exitCode = 0; GetExitCodeProcess(hProcess, &exitCode); CloseHandle(hProcess); return (exitCode == STILL_ACTIVE); #else // Send signal 0 to the PID return (0 == kill(pid, 0)); #endif } bool IPCHandler::ReadIfNew(void *target_ptr) { // Must have some shared memory if(!m_SharedData) return false; // Read the header, make sure it's the right version number Header *header = static_cast
(m_SharedData); if(header->version != m_ProtocolVersion) return false; // Ignore our own messages if(header->sender_pid == m_ProcessID) return false; // If we have already seen this message from this sender, also ignore it if(m_LastSender == header->sender_pid && m_LastReceivedMessageID == header->message_id) return false; // If the PID is known to be dead, ignore it if(m_KnownDeadPIDs.find(header->sender_pid) != m_KnownDeadPIDs.end()) return false; // Check if this is a dead PID if(!this->IsProcessRunning(header->sender_pid)) { m_KnownDeadPIDs.insert(header->sender_pid); return false; } // Store the last sender / id m_LastSender = header->sender_pid; m_LastReceivedMessageID = header->message_id; // Copy the message to the target pointer memcpy(target_ptr, m_UserData, m_MessageSize); // Success! return true; } bool IPCHandler ::Broadcast(const void *message_ptr) { // Write to the shared memory if(m_SharedData) { // Access the message header Header *header = static_cast
(m_SharedData); // Write version number header->version = m_ProtocolVersion; // Write the process ID header->sender_pid = m_ProcessID; header->message_id = ++m_MessageID; // Copy the message contents into the shared memory memcpy(m_UserData, message_ptr, m_MessageSize); // Done return true; } return false; } void IPCHandler::Close() { #ifdef WIN32 CloseHandle(m_Handle); #else // Detach from the shared memory segment shmdt(m_SharedData); // If there is noone attached to the memory segment, destroy it struct shmid_ds dsinfo; shmctl(m_Handle, IPC_STAT, &dsinfo); if(dsinfo.shm_nattch == 0) shmctl(m_Handle, IPC_RMID, NULL); #endif m_SharedData = NULL; } IPCHandler::IPCHandler() { // Set the message ID and last sender/message id values m_LastReceivedMessageID = -1; m_LastSender = -1; m_MessageID = 0; // Reset the shared memory m_SharedData = NULL; // Get the process ID #ifdef WIN32 m_ProcessID = _getpid(); #else m_ProcessID = getpid(); #endif } IPCHandler::~IPCHandler() { } itksnap-3.4.0/Common/IPCHandler.h000066400000000000000000000043011263013355200164770ustar00rootroot00000000000000#ifndef IPCHANDLER_H #define IPCHANDLER_H #include #include /** * Base class for IPCHandler. This class contains the definitions of the * core methods and is independent of the data structure being shared. */ class IPCHandler { public: IPCHandler(); ~IPCHandler(); /** * Attach to the shared memory. The caller should supply the path to the * program executable (or a derived string), and a version number. The version * number is used to prevent problems when the format of the shared data structure * has changed between versions of the program. The version number should be * incremented whenever the data structure being shared changes. The last parameter * is the size of the message in bytes (obtained using size_of) */ void Attach(const char *path, short version, size_t message_size); /** Release shared memory */ void Close(); /** Whether the shared memory is attached */ bool IsAttached() { return m_SharedData != NULL; } /** Read a 'message', i.e., the contents of shared memory */ bool Read(void *target_ptr); /** Read a 'message' but only if it has not been seen before */ bool ReadIfNew(void *target_ptr); /** Broadcast a 'message' (i.e. replace shared memory contents */ bool Broadcast(const void *message_ptr); protected: struct Header { short version; long sender_pid; long message_id; }; // Shared data pointer void *m_SharedData, *m_UserData; // Size of the shared data message size_t m_MessageSize; // Version of the protocol (to avoid problems with older code) short m_ProtocolVersion; // System-specific IPC related stuff #ifdef WIN32 void *m_Handle; #else int m_Handle; #endif // The version of the SNAP-IPC protocol. This way, when versions are different // IPC will not work. This is to account for an off chance of a someone running // two different versions of SNAP static const short IPC_VERSION; // Process ID and other values used by IPC long m_ProcessID, m_MessageID, m_LastSender, m_LastReceivedMessageID; bool IsProcessRunning(int pid); // List of known process ids, with status (0 = alive, -1 = dead) std::set m_KnownDeadPIDs; }; #endif // IPCHANDLER_H itksnap-3.4.0/Common/IRISException.cxx000066400000000000000000000041161263013355200175720ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISException.cxx,v $ Language: C++ Date: $Date: 2010/10/18 08:30:46 $ Version: $Revision: 1.4 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #include "IRISException.h" #include #include using namespace std; IRISException::operator const char *() { return this->what(); } IRISException::IRISException() : exception() { m_SimpleMessage = "Unspecified IRIS exception"; } IRISException::IRISException(const char *message, ...) : exception() { char buffer[1024]; va_list args; va_start(args, message); vsprintf(buffer,message,args); va_end (args); m_SimpleMessage = buffer; } IRISWarning::IRISWarning() : IRISException() { } IRISWarning::IRISWarning(const char *message, ...) : IRISException() { char buffer[1024]; va_list args; va_start(args, message); vsprintf(buffer,message,args); va_end (args); m_SimpleMessage = buffer; } itksnap-3.4.0/Common/IRISException.h000066400000000000000000000054051263013355200172210ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISException.h,v $ Language: C++ Date: $Date: 2009/09/16 20:03:13 $ Version: $Revision: 1.4 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __IRIS_Exceptions_h_ #define __IRIS_Exceptions_h_ #include "SNAPCommon.h" #include #include using std::string; using std::exception; /** * \class IRISException * \brief Sets up a family of SNAP/IRIS exceptions */ class IRISException : public exception { protected: string m_SimpleMessage; public: IRISException(); IRISException(const char *message, ...); virtual ~IRISException() throw() {}; virtual const char * what() const throw() { return m_SimpleMessage.c_str(); } operator const char *(); }; /** Actions can generate warnings. These warnings are simply instances of IRISException that are not fired, but rather stored. Warnings do not keep the action from executing, they are just messages that are sent to the user. Optimally, there should be an option to react to the message. Actions can also generate errors, but these are achieved by throwing an exception. */ class IRISWarning : public IRISException { public: IRISWarning(const char *fmt, ...); IRISWarning(); virtual ~IRISWarning() throw() {} }; /** * Set macro borrowed from VTK and modified. Assumes m_ for private vars */ #define irisExceptionMacro(name,parent) \ class name : public parent { \ public: \ name() : parent() {} \ name(const char *message) : parent(message) {} \ virtual ~name() throw() {} \ }; irisExceptionMacro(IRISExceptionIO,IRISException) #endif itksnap-3.4.0/Common/IRISVectorTypes.h000066400000000000000000000133311263013355200175470ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISVectorTypes.h,v $ Language: C++ Date: $Date: 2008/11/01 11:32:00 $ Version: $Revision: 1.3 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __IRISVectorTypes_h_ #define __IRISVectorTypes_h_ // For the little vector operations #include #include #include #include /** * \class iris_vector_fixed * \brief An extension of the VNL vector with some special trivial * extra functionality. */ template class iris_vector_fixed : public vnl_vector_fixed { public: typedef iris_vector_fixed Self; typedef vnl_vector_fixed Parent; typedef iris_vector_fixed IntegerVectorType; typedef iris_vector_fixed FloatVectorType; typedef iris_vector_fixed DoubleVectorType; typedef itk::Size ITKSizeType; typedef itk::Index ITKIndexType; typedef itk::FixedArray ITKFixedArray; // Initialize the n-vector to zeros iris_vector_fixed() : Parent() { this->fill(0); } // Copy constructor iris_vector_fixed(const Parent& rhs ) : Parent(rhs) {} // Construct an fixed-n-vector copy of rhs. iris_vector_fixed( const vnl_vector& rhs ) : Parent(rhs) {} // Constructs n-vector with elements initialised to \a v explicit iris_vector_fixed(const T& v) : Parent(v) {} // Construct an fixed-n-vector initialized from \a datablck // The data *must* have enough data. No checks performed. // explicit iris_vector_fixed(const T* data) : Parent(data) {} // Convenience constructor for 2-D vectors iris_vector_fixed(const T& x0,const T& x1) : Parent(x0,x1) {} // Convenience constructor for 3-D vectors iris_vector_fixed(const T& x0,const T& x1,const T& x2) : Parent(x0,x1,x2) {} // Initialize with an itk::Size iris_vector_fixed(const ITKSizeType &size) { for(int i = 0; i < VSize; i++) (*this)[i] = static_cast(size[i]); } // Initialize with an itk::Index iris_vector_fixed(const ITKIndexType &idx) { for(int i = 0; i < VSize; i++) (*this)[i] = static_cast(idx[i]); } // Initialize with an itk::FixedArray iris_vector_fixed(const ITKFixedArray &arr) { this->copy_in(arr.GetDataPointer()); } // Assignment operator that takes an int Self &operator = (int value) { this->fill((T) value); return *this; } /** * Clamp the vector between a pair of vectors (the elements of this vector * that are smaller than the corresponding elements of lower are set to lower, * and the same is done for upper). */ Self clamp(const Self &lower, const Self &upper) { Self y; for(unsigned int i=0;iget(i), l = lower(i), u = upper(i); assert(l <= u); y(i) = a < l ? l : (a > u ? u : a); } return y; } }; // Common 2D vector types typedef iris_vector_fixed Vector2i; typedef iris_vector_fixed Vector2ui; typedef iris_vector_fixed Vector2l; typedef iris_vector_fixed Vector2ul; typedef iris_vector_fixed Vector2f; typedef iris_vector_fixed Vector2d; typedef iris_vector_fixed Vector2b; // Common 3D vector types typedef iris_vector_fixed Vector3i; typedef iris_vector_fixed Vector3ui; typedef iris_vector_fixed Vector3l; typedef iris_vector_fixed Vector3ul; typedef iris_vector_fixed Vector3f; typedef iris_vector_fixed Vector3d; typedef iris_vector_fixed Vector3b; // A matrix definition typedef vnl_matrix_fixed Matrix3d; typedef vnl_matrix_fixed Matrix4d; // An equivalent to MATLAB's linspace command template inline vnl_vector linspace(T x0, T x1, unsigned int n) { vnl_vector v(n); double step = (x1 - x0) / (n - 1); for(int i = 0; i < n; i++) v[i] = (i == n-1) ? x1 : x0 + i * step; return v; } template void linspace(T *v, T x0, T x1, unsigned int n) { double step = (x1 - x0) / (n - 1); for(int i = 0; i < n; i++) v[i] = (i == n-1) ? x1 : x0 + i * step; } #ifndef ITK_MANUAL_INSTANTIATION #include "IRISVectorTypes.txx" #endif #endif // __IRISVectorTypes_h_ itksnap-3.4.0/Common/IRISVectorTypes.txx000066400000000000000000000145021263013355200201440ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISVectorTypes.txx,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ Version: $Revision: 1.3 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ // Convert vector to integer vector template inline iris_vector_fixed to_int(const vnl_vector_fixed &x) { iris_vector_fixed z; for(unsigned int i=0;i(x(i)); return z; } // Convert vector to unsigned integer vector template inline iris_vector_fixed to_unsigned_int(const vnl_vector_fixed &x) { iris_vector_fixed z; for(unsigned int i=0;i(x(i)); return z; } // Convert vector to long vector template inline iris_vector_fixed to_long(const vnl_vector_fixed &x) { iris_vector_fixed z; for(unsigned int i=0;i(x(i)); return z; } // Convert vector to unsigned long vector template inline iris_vector_fixed to_unsigned_long(const vnl_vector_fixed &x) { iris_vector_fixed z; for(unsigned int i=0;i(x(i)); return z; } // Convert vector to float vector template inline iris_vector_fixed to_float(const vnl_vector_fixed &x) { iris_vector_fixed z; for(unsigned int i=0;i(x(i)); return z; } // Convert vector to double vector template inline iris_vector_fixed to_double(const vnl_vector_fixed &x) { iris_vector_fixed z; for(unsigned int i=0;i(x(i)); return z; } /** * Given two vectors, get a vector that contains the smaller elements of the * two. Used for bounding box computations */ template inline iris_vector_fixed vector_min(const vnl_vector_fixed &x, const vnl_vector_fixed &y) { iris_vector_fixed z; for(unsigned int i=0;i inline iris_vector_fixed vector_max(const vnl_vector_fixed &x, const vnl_vector_fixed &y) { iris_vector_fixed z; for(unsigned int i=0;i y(i) ? x(i) : y(i); return z; } /** * Multiply the corresponding elements of two vectors and return * a vector containing the results */ template inline iris_vector_fixed vector_multiply(const vnl_vector_fixed &x, const vnl_vector_fixed &y) { iris_vector_fixed z; for(unsigned int i=0;i inline iris_vector_fixed vector_multiply_mixed(const vnl_vector_fixed &x, const vnl_vector_fixed &y) { iris_vector_fixed z; for(unsigned int i=0;i inline iris_vector_fixed affine_transform_point(const vnl_matrix_fixed &A, const vnl_vector_fixed &x) { iris_vector_fixed y; for(unsigned int i=0;i inline iris_vector_fixed affine_transform_vector(const vnl_matrix_fixed &A, const vnl_vector_fixed &x) { iris_vector_fixed y; for(unsigned int i=0;i inline iris_vector_fixed vector_multiply_add_mixed(const vnl_vector_fixed &x, const vnl_vector_fixed &y, const vnl_vector_fixed &z) { iris_vector_fixed r; for(unsigned int i=0;i. ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __IRISVectorTypesToITKConversion_h_ #define __IRISVectorTypesToITKConversion_h_ #include "IRISVectorTypes.h" #include "itkSize.h" #include "itkIndex.h" /** * Convert a VectorNi to itk::Size */ template inline itk::Size to_itkSize(const vnl_vector_fixed &x) { itk::Size z; for(unsigned int i=0;i(x(i)); return z; } /** * Convert a VectorNi to itk::Size */ template inline itk::Index to_itkIndex(const vnl_vector_fixed &x) { itk::Index z; for(unsigned int i=0;i(x(i)); return z; } #endif // __IRISVectorTypesToITKConversion_h_ itksnap-3.4.0/Common/ITKExtras/000077500000000000000000000000001263013355200162355ustar00rootroot00000000000000itksnap-3.4.0/Common/ITKExtras/README.txt000066400000000000000000000001431263013355200177310ustar00rootroot00000000000000This directory contains extra code that may be eventually added to ITK but is needed by ITK-SNAP. itksnap-3.4.0/Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.h000066400000000000000000000241031263013355200272050ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkBSplineScatteredDataPointSetToImageFilter.h,v $ Language: C++ Date: $Date: 2008/10/24 12:52:08 $ Version: $Revision: 1.1 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkBSplineScatteredDataPointSetToImageFilter_h #define __itkBSplineScatteredDataPointSetToImageFilter_h #include "itkPointSetToImageFilter.h" #include "itkCoxDeBoorBSplineKernelFunction.h" #include "itkFixedArray.h" #include "itkVariableSizeMatrix.h" #include "itkVector.h" #include "itkVectorContainer.h" #include "vnl/vnl_matrix.h" namespace itk { /** \class BSplineScatteredDataPointSetToImageFilter.h * \brief Image filter which provides a B-spline output approximation. * * Given an n-D image with scattered data, this filter finds * a fast approximation to that irregulary spaced data using uniform * B-splines. The traditional method of inverting the observation * matrix to find a least-squares fit is made obsolete. Therefore, * memory issues are not a concern and inverting large matrices are * unnecessary. The reference below gives the algorithm for 2-D images * and cubic splines. This class generalizes that work to encompass n-D * images and any *feasible* B-spline order. * * In addition to specifying the input point set, one must specify the number * of control points. If one wishes to use the multilevel component of * this algorithm, one must also specify the number of levels in the * hieararchy. If this is desired, the number of control points becomes * the number of control points for the coarsest level. The algorithm * then increases the number of control points at each level so that * the B-spline n-D grid is refined to twice the previous level. The * scattered data is specified by the pixel values. Pixels which * are not to be included in the calculation of the B-spline grid must * have a value equal to that specified by the variable m_BackgroundValue. * * Note that the specified number of control points must be > m_SplineOrder. * * * \author Nicholas J. Tustison * * Contributed by Nicholas J. Tustison, James C. Gee * in the Insight Journal paper: * http://hdl.handle.net/1926/140 * * \par REFERENCE * S. Lee, G. Wolberg, and S. Y. Shin, "Scattered Data Interpolation * with Multilevel B-Splines", IEEE Transactions on Visualization and * Computer Graphics, 3(3):228-244, 1997. * * N.J. Tustison and J.C. Gee, "Generalized n-D C^k Scattered Data Approximation * with COnfidence Values", Proceedings of the MIAR conference, August 2006. */ template class BSplineScatteredDataPointSetToImageFilter : public PointSetToImageFilter { public: typedef BSplineScatteredDataPointSetToImageFilter Self; typedef PointSetToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Extract dimension from input and output image. */ itkStaticConstMacro( ImageDimension, unsigned int, TOutputImage::ImageDimension ); typedef TOutputImage ImageType; typedef TInputPointSet PointSetType; /** Image typedef support. */ typedef typename ImageType::PixelType PixelType; typedef typename ImageType::RegionType RegionType; typedef typename ImageType::SizeType SizeType; typedef typename ImageType::IndexType IndexType; typedef typename ImageType::PointType ContinuousIndexType; /** PointSet typedef support. */ typedef typename PointSetType::PointType PointType; typedef typename PointSetType::PixelType PointDataType; typedef typename PointSetType::PointDataContainer PointDataContainerType; /** Other typedef */ typedef float RealType; typedef VectorContainer WeightsContainerType; typedef Image PointDataImageType; typedef Image RealImageType; typedef FixedArray ArrayType; typedef VariableSizeMatrix GradientType; typedef RealImageType HessianType; /** Interpolation kernel type (default spline order = 3) */ typedef CoxDeBoorBSplineKernelFunction<3> KernelType; /** Helper functions */ void SetNumberOfLevels( unsigned int ); void SetNumberOfLevels( ArrayType ); itkGetConstReferenceMacro( NumberOfLevels, ArrayType ); void SetSplineOrder( unsigned int ); void SetSplineOrder( ArrayType ); itkGetConstReferenceMacro( SplineOrder, ArrayType ); itkSetMacro( NumberOfControlPoints, ArrayType ); itkGetConstReferenceMacro( NumberOfControlPoints, ArrayType ); itkGetConstReferenceMacro( CurrentNumberOfControlPoints, ArrayType ); itkSetMacro( CloseDimension, ArrayType ); itkGetConstReferenceMacro( CloseDimension, ArrayType ); itkSetMacro( GenerateOutputImage, bool ); itkGetConstReferenceMacro( GenerateOutputImage, bool ); itkBooleanMacro( GenerateOutputImage ); void SetPointWeights( const WeightsContainerType * weights ); /** * Get the control point lattice. */ itkSetMacro( PhiLattice, typename PointDataImageType::Pointer ); itkGetConstMacro( PhiLattice, typename PointDataImageType::Pointer ); /** * Evaluate the resulting B-spline object at a specified * point or index within the image domain. */ void EvaluateAtPoint( PointType, PointDataType & ); void EvaluateAtIndex( IndexType, PointDataType & ); void EvaluateAtContinuousIndex( ContinuousIndexType, PointDataType & ); /** * Evaluate the resulting B-spline object at a specified * parameteric point. Note that the parameterization over * each dimension of the B-spline object is [0, 1). */ void Evaluate( PointType, PointDataType & ); /** * Evaluate the gradient of the resulting B-spline object at a specified * point or index within the image domain. */ void EvaluateGradientAtPoint( PointType, GradientType & ); void EvaluateGradientAtIndex( IndexType, GradientType & ); void EvaluateGradientAtContinuousIndex( ContinuousIndexType, GradientType & ); /** * Evaluate the gradient of the resulting B-spline object * at a specified parameteric point. Note that the * parameterization over each dimension of the B-spline * object is [0, 1). */ void EvaluateGradient( PointType, GradientType & ); protected: BSplineScatteredDataPointSetToImageFilter(); virtual ~BSplineScatteredDataPointSetToImageFilter(); void PrintSelf( std::ostream& os, Indent indent ) const; void GenerateData(); private: BSplineScatteredDataPointSetToImageFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented void GenerateControlLattice(); void RefineControlLattice(); void UpdatePointSet(); void GenerateOutputImage(); void GenerateOutputImageFast(); void CollapsePhiLattice( PointDataImageType *, PointDataImageType *, RealType, unsigned int ); bool m_DoMultilevel; bool m_GenerateOutputImage; bool m_UsePointWeights; unsigned int m_MaximumNumberOfLevels; unsigned int m_CurrentLevel; ArrayType m_NumberOfControlPoints; ArrayType m_CurrentNumberOfControlPoints; ArrayType m_CloseDimension; ArrayType m_SplineOrder; ArrayType m_NumberOfLevels; typename WeightsContainerType::Pointer m_PointWeights; typename KernelType::Pointer m_Kernel[ImageDimension]; vnl_matrix m_RefinedLatticeCoefficients[ImageDimension]; typename PointDataImageType::Pointer m_PhiLattice; typename PointDataImageType::Pointer m_PsiLattice; typename PointDataContainerType::Pointer m_InputPointData; typename PointDataContainerType::Pointer m_OutputPointData; inline typename RealImageType::IndexType NumberToIndex( unsigned int number, typename RealImageType::SizeType size ) { typename RealImageType::IndexType k; k[0] = 1; for ( unsigned int i = 1; i < ImageDimension; i++ ) { k[i] = size[ImageDimension-i-1]*k[i-1]; } typename RealImageType::IndexType index; for ( unsigned int i = 0; i < ImageDimension; i++ ) { index[ImageDimension-i-1] = static_cast( number/k[ImageDimension-i-1] ); number %= k[ImageDimension-i-1]; } return index; } }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkBSplineScatteredDataPointSetToImageFilter.txx" #endif #endif itksnap-3.4.0/Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.txx000066400000000000000000001016241263013355200276050ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkBSplineScatteredDataPointSetToImageFilter.txx,v $ Language: C++ Date: $Date: 2008/10/24 12:52:08 $ Version: $Revision: 1.1 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkBSplineScatteredDataPointSetToImageFilter_txx #define __itkBSplineScatteredDataPointSetToImageFilter_txx #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImageRegionIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkImageDuplicator.h" #include "itkCastImageFilter.h" #include "itkNumericTraits.h" #include "itkTimeProbe.h" #include "vnl/vnl_math.h" #include "vnl/algo/vnl_matrix_inverse.h" #include "vnl/vnl_vector.h" namespace itk { /** * \author Nicholas J. Tustison * * Contributed by Nicholas J. Tustison, James C. Gee * in the Insight Journal paper: * http://hdl.handle.net/1926/140 */ template BSplineScatteredDataPointSetToImageFilter ::BSplineScatteredDataPointSetToImageFilter() { this->m_SplineOrder.Fill( 3 ); for( unsigned int i = 0; i < ImageDimension; i++ ) { this->m_NumberOfControlPoints[i] = ( this->m_SplineOrder[i] + 1 ); this->m_Kernel[i] = KernelType::New(); this->m_Kernel[i]->SetSplineOrder( this->m_SplineOrder[i] ); } this->m_CloseDimension.Fill( 0 ); this->m_DoMultilevel = false; this->m_GenerateOutputImage = true; this->m_NumberOfLevels.Fill( 1 ); this->m_MaximumNumberOfLevels = 1; this->m_PhiLattice = NULL; this->m_PsiLattice = PointDataImageType::New(); this->m_InputPointData = PointDataContainerType::New(); this->m_OutputPointData = PointDataContainerType::New(); this->m_PointWeights = WeightsContainerType::New(); this->m_UsePointWeights = false; } template BSplineScatteredDataPointSetToImageFilter ::~BSplineScatteredDataPointSetToImageFilter() { } template void BSplineScatteredDataPointSetToImageFilter ::SetSplineOrder( unsigned int order ) { this->m_SplineOrder.Fill( order ); this->SetSplineOrder( this->m_SplineOrder ); } template void BSplineScatteredDataPointSetToImageFilter ::SetSplineOrder( ArrayType order ) { itkDebugMacro(<< "Setting m_SplineOrder to " << order); this->m_SplineOrder = order; for( int i = 0; i < ImageDimension; i++ ) { if( this->m_SplineOrder[i] == 0 ) { itkExceptionMacro( "The spline order in each dimension must be greater than 0" ); } this->m_Kernel[i] = KernelType::New(); this->m_Kernel[i]->SetSplineOrder( this->m_SplineOrder[i] ); if( this->m_DoMultilevel ) { typename KernelType::MatrixType C; C = this->m_Kernel[i]->GetShapeFunctionsInZeroToOneInterval(); vnl_matrix R; vnl_matrix S; R.set_size( C.rows(), C.cols() ); S.set_size( C.rows(), C.cols() ); for( unsigned int j = 0; j < C.rows(); j++ ) { for( unsigned int k = 0; k < C.cols(); k++ ) { R(j, k) = S(j, k) = static_cast( C(j, k) ); } } for( unsigned int j = 0; j < C.cols(); j++ ) { RealType c = vcl_pow( static_cast( 2.0 ), static_cast( C.cols() )-j-1 ); for( unsigned int k = 0; k < C.rows(); k++) { R(k, j) *= c; } } R = R.transpose(); R.flipud(); S = S.transpose(); S.flipud(); this->m_RefinedLatticeCoefficients[i] = ( vnl_svd( R ).solve( S ) ).extract( 2, S.cols() ); } } this->Modified(); } template void BSplineScatteredDataPointSetToImageFilter ::SetNumberOfLevels( unsigned int levels ) { this->m_NumberOfLevels.Fill( levels ); this->SetNumberOfLevels( this->m_NumberOfLevels ); } template void BSplineScatteredDataPointSetToImageFilter ::SetNumberOfLevels( ArrayType levels ) { this->m_NumberOfLevels = levels; this->m_MaximumNumberOfLevels = 1; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_NumberOfLevels[i] == 0 ) { itkExceptionMacro( "The number of levels in each dimension must be greater than 0" ); } if( this->m_NumberOfLevels[i] > this->m_MaximumNumberOfLevels ) { this->m_MaximumNumberOfLevels = this->m_NumberOfLevels[i]; } } itkDebugMacro( "Setting m_NumberOfLevels to " << this->m_NumberOfLevels ); itkDebugMacro( "Setting m_MaximumNumberOfLevels to " << this->m_MaximumNumberOfLevels ); if( this->m_MaximumNumberOfLevels > 1 ) { this->m_DoMultilevel = true; } else { this->m_DoMultilevel = false; } this->SetSplineOrder( this->m_SplineOrder ); this->Modified(); } template void BSplineScatteredDataPointSetToImageFilter ::SetPointWeights( const WeightsContainerType * weights ) { this->m_UsePointWeights = true; this->m_PointWeights = weights; this->Modified(); } template void BSplineScatteredDataPointSetToImageFilter ::GenerateData() { /** * Create the output image */ itkDebugMacro( "Origin: " << this->m_Origin ); itkDebugMacro( "Size: " << this->m_Size ); itkDebugMacro( "Spacing: " << this->m_Spacing ); for( unsigned int i = 0; i < ImageDimension; i++) { if( this->m_Size[i] == 0 ) { itkExceptionMacro( "Size must be specified." ); } } this->GetOutput()->SetOrigin( this->m_Origin ); this->GetOutput()->SetSpacing( this->m_Spacing ); this->GetOutput()->SetRegions( this->m_Size ); this->GetOutput()->Allocate(); if( this->m_UsePointWeights && ( this->m_PointWeights->Size() != this->GetInput()->GetNumberOfPoints() ) ) { itkExceptionMacro( "The number of weight points and input points must be equal." ); } for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_NumberOfControlPoints[i] < this->m_SplineOrder[i] + 1 ) { itkExceptionMacro( "The number of control points must be 1 greater than the spline order." ); } } this->m_InputPointData->Initialize(); this->m_OutputPointData->Initialize(); if( this->GetInput()->GetNumberOfPoints() > 0 ) { typename PointSetType::PointDataContainer::ConstIterator It; It = this->GetInput()->GetPointData()->Begin(); while( It != this->GetInput()->GetPointData()->End() ) { if( !this->m_UsePointWeights ) { this->m_PointWeights->InsertElement( It.Index(), 1.0 ); } this->m_InputPointData->InsertElement( It.Index(), It.Value() ); this->m_OutputPointData->InsertElement( It.Index(), It.Value() ); ++It; } } this->m_CurrentLevel = 0; this->m_CurrentNumberOfControlPoints = this->m_NumberOfControlPoints; this->GenerateControlLattice(); this->UpdatePointSet(); if( this->m_DoMultilevel ) { this->m_PsiLattice->SetRegions( this->m_PhiLattice->GetLargestPossibleRegion() ); this->m_PsiLattice->Allocate(); PointDataType P( 0.0 ); this->m_PsiLattice->FillBuffer( P ); } for( this->m_CurrentLevel = 1; this->m_CurrentLevel < this->m_MaximumNumberOfLevels; this->m_CurrentLevel++ ) { ImageRegionIterator ItPsi( this->m_PsiLattice, this->m_PsiLattice->GetLargestPossibleRegion() ); ImageRegionIterator ItPhi( this->m_PhiLattice, this->m_PhiLattice->GetLargestPossibleRegion() ); for( ItPsi.GoToBegin(), ItPhi.GoToBegin(); !ItPsi.IsAtEnd(); ++ItPsi, ++ItPhi ) { ItPsi.Set( ItPhi.Get() + ItPsi.Get() ); } this->RefineControlLattice(); for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_CurrentLevel < this->m_NumberOfLevels[i] ) { this->m_CurrentNumberOfControlPoints[i] = 2*this->m_CurrentNumberOfControlPoints[i]-this->m_SplineOrder[i]; } } itkDebugMacro( << "Current Level = " << this->m_CurrentLevel ); itkDebugMacro( << " Current number of control points = " << this->m_CurrentNumberOfControlPoints ); RealType avg_p = 0.0; RealType totalWeight = 0.0; typename PointDataContainerType::Iterator ItIn, ItOut; ItIn = this->m_InputPointData->Begin(); ItOut = this->m_OutputPointData->Begin(); while( ItIn != this->m_InputPointData->End() ) { this->m_InputPointData->InsertElement( ItIn.Index(), ItIn.Value() - ItOut.Value() ); if( this->GetDebug() ) { RealType weight = this->m_PointWeights->GetElement( ItIn.Index() ); avg_p += ( ItIn.Value() - ItOut.Value() ).GetNorm() * weight; totalWeight += weight; } ++ItIn; ++ItOut; } if( totalWeight > 0 ) { itkDebugMacro( << "The average weighted difference norm of the point set is " << avg_p / totalWeight ); } this->GenerateControlLattice(); this->UpdatePointSet(); } if( this->m_DoMultilevel ) { ImageRegionIterator ItPsi( this->m_PsiLattice, this->m_PsiLattice->GetLargestPossibleRegion() ); ImageRegionIterator ItPhi( this->m_PhiLattice, this->m_PhiLattice->GetLargestPossibleRegion() ); for( ItPsi.GoToBegin(), ItPhi.GoToBegin(); !ItPsi.IsAtEnd(); ++ItPsi, ++ItPhi ) { ItPsi.Set( ItPhi.Get()+ItPsi.Get() ); } typedef ImageDuplicator ImageDuplicatorType; typename ImageDuplicatorType::Pointer Duplicator = ImageDuplicatorType::New(); Duplicator->SetInputImage( this->m_PsiLattice ); Duplicator->Update(); this->m_PhiLattice = Duplicator->GetOutput(); this->UpdatePointSet(); } if( this->m_GenerateOutputImage ) { //this->GenerateOutputImage(); this->GenerateOutputImageFast(); } } template void BSplineScatteredDataPointSetToImageFilter ::RefineControlLattice() { ArrayType NumberOfNewControlPoints = this->m_CurrentNumberOfControlPoints; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_CurrentLevel < this->m_NumberOfLevels[i] ) { NumberOfNewControlPoints[i] = 2*NumberOfNewControlPoints[i]-this->m_SplineOrder[i]; } } typename RealImageType::RegionType::SizeType size; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_CloseDimension[i] ) { size[i] = NumberOfNewControlPoints[i] - this->m_SplineOrder[i]; } else { size[i] = NumberOfNewControlPoints[i]; } } typename PointDataImageType::Pointer RefinedLattice = PointDataImageType::New(); RefinedLattice->SetRegions( size ); RefinedLattice->Allocate(); PointDataType data; data.Fill( 0.0 ); RefinedLattice->FillBuffer( data ); typename PointDataImageType::IndexType idx; typename PointDataImageType::IndexType idx_Psi; typename PointDataImageType::IndexType tmp; typename PointDataImageType::IndexType tmp_Psi; typename PointDataImageType::IndexType off; typename PointDataImageType::IndexType off_Psi; typename PointDataImageType::RegionType::SizeType size_Psi; size.Fill( 2 ); unsigned int N = 1; for( unsigned int i = 0; i < ImageDimension; i++ ) { N *= ( this->m_SplineOrder[i] + 1 ); size_Psi[i] = this->m_SplineOrder[i] + 1; } ImageRegionIteratorWithIndex It( RefinedLattice, RefinedLattice->GetLargestPossibleRegion() ); It.GoToBegin(); while( !It.IsAtEnd() ) { idx = It.GetIndex(); for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_CurrentLevel < this->m_NumberOfLevels[i] ) { idx_Psi[i] = static_cast( 0.5*idx[i] ); } else { idx_Psi[i] = static_cast( idx[i] ); } } for( unsigned int i = 0; i < ( 2 << (ImageDimension-1) ); i++ ) { PointDataType sum( 0.0 ); PointDataType val; off = this->NumberToIndex( i, size ); bool OutOfBoundary = false; for( unsigned int j = 0; j < ImageDimension; j++ ) { tmp[j] = idx[j] + off[j]; if( tmp[j] >= static_cast( NumberOfNewControlPoints[j] ) && !this->m_CloseDimension[j] ) { OutOfBoundary = true; break; } if( this->m_CloseDimension[j] ) { tmp[j] %= RefinedLattice->GetLargestPossibleRegion().GetSize()[j]; } } if( OutOfBoundary ) { continue; } for( unsigned int j = 0; j < N; j++ ) { off_Psi = this->NumberToIndex( j, size_Psi ); bool IsOutOfBoundary = false; for( unsigned int k = 0; k < ImageDimension; k++ ) { tmp_Psi[k] = idx_Psi[k] + off_Psi[k]; if( tmp_Psi[k] >= static_cast( this->m_CurrentNumberOfControlPoints[k] ) && !this->m_CloseDimension[k] ) { IsOutOfBoundary = true; break; } if( this->m_CloseDimension[k] ) { tmp_Psi[k] %= this->m_PsiLattice->GetLargestPossibleRegion().GetSize()[k]; } } if( IsOutOfBoundary ) { continue; } RealType coeff = 1.0; for( unsigned int k = 0; k < ImageDimension; k++ ) { coeff *= this->m_RefinedLatticeCoefficients[k]( off[k], off_Psi[k] ); } val = this->m_PsiLattice->GetPixel( tmp_Psi ); val *= coeff; sum += val; } RefinedLattice->SetPixel( tmp, sum ); } bool IsEvenIndex = false; while( !IsEvenIndex && !It.IsAtEnd() ) { ++It; idx = It.GetIndex(); IsEvenIndex = true; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( idx[i] % 2 ) { IsEvenIndex = false; } } } } typedef ImageDuplicator ImageDuplicatorType; typename ImageDuplicatorType::Pointer Duplicator = ImageDuplicatorType::New(); Duplicator->SetInputImage( RefinedLattice ); Duplicator->Update(); this->m_PsiLattice = Duplicator->GetOutput(); } template void BSplineScatteredDataPointSetToImageFilter ::GenerateControlLattice() { typename RealImageType::RegionType::SizeType size; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_CloseDimension[i] ) { size[i] = this->m_CurrentNumberOfControlPoints[i]-this->m_SplineOrder[i]; } else { size[i] = this->m_CurrentNumberOfControlPoints[i]; } } this->m_PhiLattice = PointDataImageType::New(); this->m_PhiLattice->SetRegions( size ); this->m_PhiLattice->Allocate(); this->m_PhiLattice->FillBuffer( 0.0 ); typename RealImageType::Pointer omega = RealImageType::New(); omega->SetRegions( size ); omega->Allocate(); omega->FillBuffer( 0.0 ); typename PointDataImageType::Pointer delta = PointDataImageType::New(); delta->SetRegions( size ); delta->Allocate(); delta->FillBuffer( 0.0 ); for( unsigned int i = 0; i < ImageDimension; i++ ) { size[i] = this->m_SplineOrder[i] + 1; } typename RealImageType::Pointer w = RealImageType::New(); w->SetRegions( size ); w->Allocate(); typename PointDataImageType::Pointer phi = PointDataImageType::New(); phi->SetRegions( size ); phi->Allocate(); ImageRegionIteratorWithIndex Itw( w, w->GetLargestPossibleRegion() ); ImageRegionIteratorWithIndex Itp( phi, phi->GetLargestPossibleRegion() ); vnl_vector p( ImageDimension ); vnl_vector r( ImageDimension ); for( unsigned int i = 0; i < ImageDimension; i++ ) { r[i] = static_cast( this->m_CurrentNumberOfControlPoints[i] - this->m_SplineOrder[i] ) / ( static_cast( this->m_Size[i] - 1 )*this->m_Spacing[i] + 0.001 ); } typename PointDataContainerType::ConstIterator It; for( It = this->m_InputPointData->Begin(); It != this->m_InputPointData->End(); ++It ) { PointType point; point.Fill(0.0); this->GetInput()->GetPoint( It.Index(), &point ); for( unsigned int i = 0; i < ImageDimension; i++ ) { p[i] = ( point[i] - this->m_Origin[i] ) * r[i]; } RealType w2_sum = 0.0; for( Itw.GoToBegin(); !Itw.IsAtEnd(); ++Itw ) { RealType B = 1.0; typename RealImageType::IndexType idx = Itw.GetIndex(); for( unsigned int i = 0; i < ImageDimension; i++ ) { RealType u = static_cast( p[i] - static_cast( p[i] ) - idx[i] ) + 0.5*static_cast( this->m_SplineOrder[i] - 1 ); B *= this->m_Kernel[i]->Evaluate( u ); } Itw.Set( B ); w2_sum += B*B; } for( Itp.GoToBegin(), Itw.GoToBegin(); !Itp.IsAtEnd(); ++Itp, ++Itw ) { typename RealImageType::IndexType idx = Itw.GetIndex(); for( unsigned int i = 0; i < ImageDimension; i++ ) { idx[i] += static_cast( p[i] ); if( this->m_CloseDimension[i] ) { idx[i] %= delta->GetLargestPossibleRegion().GetSize()[i]; } } RealType wc = this->m_PointWeights->GetElement( It.Index() ); RealType t = Itw.Get(); omega->SetPixel( idx, omega->GetPixel( idx ) + wc*t*t ); PointDataType data = It.Value(); data *= ( t / w2_sum ); Itp.Set( data ); data *= ( t * t * wc ); delta->SetPixel( idx, delta->GetPixel( idx ) + data ); delta->GetPixel( idx ) + data; } } ImageRegionIterator Itl( this->m_PhiLattice, this->m_PhiLattice->GetLargestPossibleRegion() ); ImageRegionIterator Itd( delta, delta->GetLargestPossibleRegion() ); ImageRegionIterator Ito( omega, omega->GetLargestPossibleRegion() ); for( Itl.GoToBegin(), Ito.GoToBegin(), Itd.GoToBegin(); !Itl.IsAtEnd(); ++Itl, ++Ito, ++Itd ) { PointDataType P; P.Fill( 0 ); if( Ito.Get() != 0 ) { P = Itd.Get() / Ito.Get(); for( unsigned int i = 0; i < P.Size(); i++ ) { if( vnl_math_isnan( P[i] ) || vnl_math_isinf( P[i] ) ) { P[i] = 0; } } Itl.Set( P ); } } } template void BSplineScatteredDataPointSetToImageFilter ::UpdatePointSet() { typename PointDataImageType::Pointer collapsedPhiLattices[ImageDimension+1]; for( unsigned int i = 0; i < ImageDimension; i++ ) { collapsedPhiLattices[i] = PointDataImageType::New(); collapsedPhiLattices[i]->SetOrigin( this->m_PhiLattice->GetOrigin() ); collapsedPhiLattices[i]->SetSpacing( this->m_PhiLattice->GetSpacing() ); typename PointDataImageType::SizeType size; size.Fill( 1 ); for( unsigned int j = 0; j < i; j++ ) { size[j] = this->m_PhiLattice->GetLargestPossibleRegion().GetSize()[j]; } collapsedPhiLattices[i]->SetRegions( size ); collapsedPhiLattices[i]->Allocate(); } collapsedPhiLattices[ImageDimension] = this->m_PhiLattice; ArrayType totalNumberOfSpans; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_CloseDimension[i] ) { totalNumberOfSpans[i] = this->m_PhiLattice->GetLargestPossibleRegion().GetSize()[i]; } else { totalNumberOfSpans[i] = this->m_PhiLattice->GetLargestPossibleRegion().GetSize()[i] - this->m_SplineOrder[i]; } } FixedArray U; FixedArray currentU; currentU.Fill( -1 ); typename PointDataImageType::IndexType startPhiIndex = this->m_PhiLattice->GetLargestPossibleRegion().GetIndex(); typename PointDataContainerType::ConstIterator ItIn; ItIn = this->m_InputPointData->Begin(); while( ItIn != this->m_InputPointData->End() ) { PointType point; point.Fill(0.0); this->GetInput()->GetPoint( ItIn.Index(), &point ); for( unsigned int i = 0; i < ImageDimension; i++ ) { U[i] = static_cast( totalNumberOfSpans[i] ) * static_cast( point[i] - this->m_Origin[i] ) / ( static_cast( this->m_Size[i] - 1 ) * this->m_Spacing[i] ); if( U[i] == static_cast( totalNumberOfSpans[i] ) ) { U[i] -= 0.001; } } for( int i = ImageDimension-1; i >= 0; i-- ) { if( U[i] != currentU[i] ) { for( int j = i; j >= 0; j-- ) { this->CollapsePhiLattice( collapsedPhiLattices[j+1], collapsedPhiLattices[j], U[j], j ); currentU[j] = U[j]; } break; } } this->m_OutputPointData->InsertElement( ItIn.Index(), collapsedPhiLattices[0]->GetPixel( startPhiIndex ) ); ++ItIn; } } template void BSplineScatteredDataPointSetToImageFilter ::GenerateOutputImage() { ImageRegionIteratorWithIndex It( this->GetOutput(), this->GetOutput()->GetBufferedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PointDataType data; this->EvaluateAtIndex( It.GetIndex(), data ); It.Set( data ); } } template void BSplineScatteredDataPointSetToImageFilter ::GenerateOutputImageFast() { typename PointDataImageType::Pointer collapsedPhiLattices[ImageDimension+1]; for( unsigned int i = 0; i < ImageDimension; i++ ) { collapsedPhiLattices[i] = PointDataImageType::New(); collapsedPhiLattices[i]->SetOrigin( this->m_PhiLattice->GetOrigin() ); collapsedPhiLattices[i]->SetSpacing( this->m_PhiLattice->GetSpacing() ); typename PointDataImageType::SizeType size; size.Fill( 1 ); for( unsigned int j = 0; j < i; j++ ) { size[j] = this->m_PhiLattice->GetLargestPossibleRegion().GetSize()[j]; } collapsedPhiLattices[i]->SetRegions( size ); collapsedPhiLattices[i]->Allocate(); } collapsedPhiLattices[ImageDimension] = this->m_PhiLattice; ArrayType totalNumberOfSpans; for( unsigned int i = 0; i < ImageDimension; i++ ) { if( this->m_CloseDimension[i] ) { totalNumberOfSpans[i] = this->m_PhiLattice->GetLargestPossibleRegion().GetSize()[i]; } else { totalNumberOfSpans[i] = this->m_PhiLattice->GetLargestPossibleRegion().GetSize()[i] - this->m_SplineOrder[i]; } } FixedArray U; FixedArray currentU; currentU.Fill( -1 ); typename ImageType::IndexType startIndex = this->GetOutput()->GetRequestedRegion().GetIndex(); typename PointDataImageType::IndexType startPhiIndex = this->m_PhiLattice->GetLargestPossibleRegion().GetIndex(); ImageRegionIteratorWithIndex It( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { typename ImageType::IndexType idx = It.GetIndex(); for( unsigned int i = 0; i < ImageDimension; i++ ) { U[i] = static_cast( totalNumberOfSpans[i] ) * static_cast( idx[i] - startIndex[i] ) / static_cast( this->m_Size[i] - 1 ); if( U[i] == static_cast( totalNumberOfSpans[i] ) ) { U[i] -= 0.001; } } for( int i = ImageDimension-1; i >= 0; i-- ) { if( U[i] != currentU[i] ) { for( int j = i; j >= 0; j-- ) { this->CollapsePhiLattice( collapsedPhiLattices[j+1], collapsedPhiLattices[j], U[j], j ); currentU[j] = U[j]; } break; } } It.Set( collapsedPhiLattices[0]->GetPixel( startPhiIndex ) ); } } template void BSplineScatteredDataPointSetToImageFilter ::CollapsePhiLattice( PointDataImageType *lattice, PointDataImageType *collapsedLattice, RealType u, unsigned int dimension ) { ImageRegionIteratorWithIndex It ( collapsedLattice, collapsedLattice->GetLargestPossibleRegion() ); for( It.GoToBegin(); !It.IsAtEnd(); ++It ) { PointDataType data; data.Fill( 0.0 ); typename PointDataImageType::IndexType idx = It.GetIndex(); for( unsigned int i = 0; i < this->m_SplineOrder[dimension] + 1; i++ ) { idx[dimension] = static_cast( u ) + i; RealType B = this->m_Kernel[dimension]->Evaluate ( u - idx[dimension] + 0.5*static_cast( this->m_SplineOrder[dimension] - 1 ) ); if( this->m_CloseDimension[dimension] ) { idx[dimension] %= lattice->GetLargestPossibleRegion().GetSize()[dimension]; } data += ( lattice->GetPixel( idx ) * B ); } It.Set( data ); } } template void BSplineScatteredDataPointSetToImageFilter ::EvaluateAtPoint( PointType point, PointDataType &data ) { for( unsigned int i = 0; i < ImageDimension; i++) { point[i] -= this->m_Origin[i]; point[i] /= ( static_cast( this->m_Size[i]-1 ) * this->m_Spacing[i] ); } this->Evaluate( point, data ); } template void BSplineScatteredDataPointSetToImageFilter ::EvaluateAtIndex( IndexType idx, PointDataType &data ) { PointType point; this->GetOutput()->TransformIndexToPhysicalPoint( idx, point ); this->EvaluateAtPoint( point, data ); } template void BSplineScatteredDataPointSetToImageFilter ::EvaluateAtContinuousIndex( ContinuousIndexType idx, PointDataType &data ) { PointType point; this->GetOutput()->TransformContinuousIndexToPhysicalPoint( idx, point ); this->EvaluateAtPoint( point, data ); } template void BSplineScatteredDataPointSetToImageFilter ::Evaluate( PointType params, PointDataType &data ) { vnl_vector p( ImageDimension ); for( unsigned int i = 0; i < ImageDimension; i++ ) { if( params[i] < 0.0 || params[i] > 1.0 ) { itkExceptionMacro( "The specified point " << params << " is outside the image domain." ); } if( params[i] == 1.0 ) { params[i] = 1.0 - vnl_math::float_eps; } p[i] = static_cast( params[i] ) * static_cast( this->m_CurrentNumberOfControlPoints[i] - this->m_SplineOrder[i] ); } typename RealImageType::RegionType::SizeType size; for( unsigned int i = 0; i < ImageDimension; i++ ) { size[i] = this->m_SplineOrder[i] + 1; } typename RealImageType::Pointer w; w = RealImageType::New(); w->SetRegions( size ); w->Allocate(); PointDataType val; data.Fill( 0.0 ); ImageRegionIteratorWithIndex Itw( w, w->GetLargestPossibleRegion() ); for( Itw.GoToBegin(); !Itw.IsAtEnd(); ++Itw ) { RealType B = 1.0; typename RealImageType::IndexType idx = Itw.GetIndex(); for( unsigned int i = 0; i < ImageDimension; i++ ) { RealType u = p[i] - static_cast( static_cast( p[i] ) + idx[i] ) + 0.5*static_cast( this->m_SplineOrder[i] - 1 ); B *= this->m_Kernel[i]->Evaluate( u ); } for( unsigned int i = 0; i < ImageDimension; i++ ) { idx[i] += static_cast( p[i] ); if( this->m_CloseDimension[i] ) { idx[i] %= this->m_PhiLattice->GetLargestPossibleRegion().GetSize()[i]; } } if( this->m_PhiLattice->GetLargestPossibleRegion().IsInside( idx ) ) { val = this->m_PhiLattice->GetPixel( idx ); val *= B; data += val; } } } template void BSplineScatteredDataPointSetToImageFilter ::EvaluateGradientAtPoint( PointType point, GradientType &gradient ) { for( unsigned int i = 0; i < ImageDimension; i++) { point[i] -= this->m_Origin[i]; point[i] /= ( static_cast( this->m_Size[i] - 1 ) * this->m_Spacing[i] ); } this->EvaluateGradient( point, gradient ); } template void BSplineScatteredDataPointSetToImageFilter ::EvaluateGradientAtIndex( IndexType idx, GradientType &gradient ) { PointType point; this->GetOutput()->TransformIndexToPhysicalPoint( idx, point ); this->EvaluateGradientAtPoint( point, gradient ); } template void BSplineScatteredDataPointSetToImageFilter ::EvaluateGradientAtContinuousIndex( ContinuousIndexType idx, GradientType &gradient ) { PointType point; this->GetOutput()->TransformContinuousIndexToPhysicalPoint( idx, gradient ); this->EvaluateGradientAtPoint( point, gradient ); } template void BSplineScatteredDataPointSetToImageFilter ::EvaluateGradient( PointType params, GradientType &gradient ) { vnl_vector p( ImageDimension ); for( unsigned int i = 0; i < ImageDimension; i++ ) { if( params[i] < 0.0 || params[i] > 1.0 ) { itkExceptionMacro( "The specifed point is outside the image domain." ); } if( params[i] == 1.0 ) { params[i] = 1.0 - vnl_math::float_eps; } p[i] = static_cast( params[i] ) * static_cast( this->m_CurrentNumberOfControlPoints[i] - this->m_SplineOrder[i] ); } typename RealImageType::RegionType::SizeType size; for( unsigned int i = 0; i < ImageDimension; i++ ) { size[i] = this->m_SplineOrder[i] + 1; } typename RealImageType::Pointer w; w = RealImageType::New(); w->SetRegions( size ); w->Allocate(); PointDataType val; gradient.SetSize( val.Size(), ImageDimension ); gradient.Fill( 0.0 ); ImageRegionIteratorWithIndex Itw( w, w->GetLargestPossibleRegion() ); for( unsigned int j = 0; j < gradient.Cols(); j++ ) { for( Itw.GoToBegin(); !Itw.IsAtEnd(); ++Itw ) { RealType B = 1.0; typename RealImageType::IndexType idx = Itw.GetIndex(); for( unsigned int i = 0; i < ImageDimension; i++ ) { RealType u = p[i] - static_cast( static_cast( p[i] ) + idx[i] ) + 0.5*static_cast( this->m_SplineOrder[i] - 1 ); if( j == i ) { B *= this->m_Kernel[i]->EvaluateDerivative( u ); } else { B *= this->m_Kernel[i]->Evaluate( u ); } } for( unsigned int i = 0; i < ImageDimension; i++ ) { idx[i] += static_cast( p[i] ); if( this->m_CloseDimension[i] ) { idx[i] %= this->m_PhiLattice->GetLargestPossibleRegion().GetSize()[i]; } } if( this->m_PhiLattice->GetLargestPossibleRegion().IsInside( idx ) ) { val = this->m_PhiLattice->GetPixel( idx ); val *= B; for( unsigned int i = 0; i < val.Size(); i++ ) { gradient( i, j ) += val[i]; } } } } } /** * Standard "PrintSelf" method */ template void BSplineScatteredDataPointSetToImageFilter ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); for( unsigned int i = 0; i < ImageDimension; i++ ) { this->m_Kernel[i]->Print( os, indent ); } os << indent << "B-spline order: " << this->m_SplineOrder << std::endl; os << indent << "Number Of control points: " << this->m_NumberOfControlPoints << std::endl; os << indent << "Close dimension: " << this->m_CloseDimension << std::endl; os << indent << "Number of levels " << this->m_NumberOfLevels << std::endl; } } // end namespace itk #endif itksnap-3.4.0/Common/ITKExtras/itkBinaryDiamondStructuringElement.h000066400000000000000000000074001263013355200254230ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkBinaryDiamondStructuringElement.h,v $ Language: C++ Date: $Date: 2008/11/26 03:10:15 $ Version: $Revision: 1.1 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkBinaryDiamondStructuringElement_h #define __itkBinaryDiamondStructuringElement_h #include "itkNeighborhood.h" namespace itk { /** \class BinaryDiamondStructuringElement * \brief A Neighborhood that represents a box structuring element * with binary elements. * * * \sa Neighborhood * \sa MorphologyImageFilter * \sa BinaryDilateImageFilter * \sa BinaryErodeImageFilter * * \ingroup Operators * \ingroup ImageIterators */ template > class ITK_EXPORT BinaryDiamondStructuringElement : public Neighborhood { public: /** Standard class typedefs. */ typedef BinaryDiamondStructuringElement Self; typedef Neighborhood Superclass; /** External support for allocator type. */ typedef TAllocator AllocatorType; /** External support for dimensionality. */ itkStaticConstMacro(NeighborhoodDimension, unsigned int, VDimension); /** External support for pixel type. */ typedef TPixel PixelType; /** Iterator typedef support. Note the naming is intentional, i.e., * ::iterator and ::const_iterator, because the allocator may be a * vnl object or other type, which uses this form. */ typedef typename AllocatorType::iterator Iterator; typedef typename AllocatorType::const_iterator ConstIterator; /** Size and value typedef support. */ typedef typename Superclass::SizeType SizeType; typedef typename Superclass::SizeValueType SizeValueType; /** Offset and value typedef support. */ typedef typename Superclass::OffsetType OffsetType; typedef typename OffsetType::OffsetValueType OffsetValueType; /** Radius typedef support. */ typedef typename Superclass::RadiusType RadiusType; /** External slice iterator type typedef support. */ typedef SliceIterator SliceIteratorType; /** Default constructor. */ BinaryDiamondStructuringElement() {} /** Default destructor. */ virtual ~BinaryDiamondStructuringElement() {} /** Copy constructor. */ BinaryDiamondStructuringElement(const Self& other) : Neighborhood(other) { } /** Assignment operator. */ Self &operator=(const Self& other) { Superclass::operator=(other); return *this; } /** Build the structuring element */ void CreateStructuringElement(); protected: private: }; } // namespace itk // Define instantiation macro for this template. #define ITK_TEMPLATE_BinaryDiamondStructuringElement(_, EXPORT, x, y) namespace itk { \ _(2(class EXPORT BinaryDiamondStructuringElement< ITK_TEMPLATE_2 x >)) \ namespace Templates { typedef BinaryDiamondStructuringElement< ITK_TEMPLATE_2 x > \ BinaryDiamondStructuringElement##y; } \ } #if ITK_TEMPLATE_EXPLICIT # include "Templates/itkBinaryDiamondStructuringElement+-.h" #endif #if ITK_TEMPLATE_TXX # include "itkBinaryDiamondStructuringElement.txx" #endif #endif itksnap-3.4.0/Common/ITKExtras/itkBinaryDiamondStructuringElement.txx000066400000000000000000000035051263013355200260210ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkBinaryDiamondStructuringElement.txx,v $ Language: C++ Date: $Date: 2008/12/10 18:42:15 $ Version: $Revision: 1.2 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkBinaryDiamondStructuringElement_txx #define __itkBinaryDiamondStructuringElement_txx #include "itkBinaryDiamondStructuringElement.h" #include "itkNumericTraits.h" namespace itk { // Create the structuring element template void BinaryDiamondStructuringElement ::CreateStructuringElement() { unsigned int minRadius = this->GetRadius( 0 ); for ( unsigned d = 1; d < NeighborhoodDimension; d++ ) { if ( minRadius > this->GetRadius( d ) ) { minRadius = this->GetRadius( d ); } } for ( unsigned int n = 0; n < this->Size(); n++ ) { OffsetType offset = this->GetOffset( n ); unsigned int manhattanDistance = 0; for ( unsigned int d = 0; d < NeighborhoodDimension; d++ ) { manhattanDistance += vnl_math_abs( offset[d] ); } if ( manhattanDistance <= minRadius ) { this->operator[]( n ) = NumericTraits::One; } else { this->operator[]( n ) = NumericTraits::Zero; } } } } // namespace itk #endif itksnap-3.4.0/Common/ITKExtras/itkCoxDeBoorBSplineKernelFunction.h000066400000000000000000000126731263013355200250770ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkCoxDeBoorBSplineKernelFunction.h,v $ Language: C++ Date: $Date: 2008/10/24 12:52:08 $ Version: $Revision: 1.1 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkCoxDeBoorBSplineKernelFunction_h #define __itkCoxDeBoorBSplineKernelFunction_h #include "itkKernelFunction.h" #include "vnl/vnl_math.h" #include "vnl/vnl_real_polynomial.h" #include "vnl/vnl_matrix.h" namespace itk { /** \class CoxDeBoorBSplineKernelFunction * \brief BSpline kernel used for density estimation and nonparameteric * regression. * * This class enscapsulates BSpline kernel for * density estimation or nonparameteric regression. * See documentation for KernelFunction for more details. * * This class is templated over the spline order to cohere with * the previous incarnation of this class. One can change the * order during an instantiation's existence. Note that * other authors have defined the B-spline order as being the * degree of spline + 1. In the ITK context (e.g. in this * class), the spline order is equivalent to the degree of * the spline. * * \author Nicholas J. Tustison * * Contributed by Nicholas J. Tustison, James C. Gee * in the Insight Journal paper: * http://hdl.handle.net/1926/140 * * * \sa KernelFunction * * \ingroup Functions */ template class ITK_EXPORT CoxDeBoorBSplineKernelFunction : public KernelFunction { public: /** Standard class typedefs. */ typedef CoxDeBoorBSplineKernelFunction Self; typedef KernelFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro( CoxDeBoorBSplineKernelFunction, KernelFunction ); typedef double RealType; typedef vnl_vector VectorType; typedef vnl_real_polynomial PolynomialType; typedef vnl_matrix MatrixType; /** Get/Sets the Spline Order */ void SetSplineOrder( unsigned int ); itkGetMacro( SplineOrder, unsigned int ); /** Evaluate the function. */ inline RealType Evaluate( const RealType & u ) const { RealType absValue = vnl_math_abs( u ); unsigned int which; if ( this->m_SplineOrder % 2 == 0 ) { which = static_cast( absValue+0.5 ); } else { which = static_cast( absValue ); } if ( which < this->m_BSplineShapeFunctions.rows() ) { return PolynomialType( this->m_BSplineShapeFunctions.get_row( which ) ).evaluate( absValue ); } else { return NumericTraits::Zero; } } /** Evaluate the derivative. */ inline RealType EvaluateDerivative( const double & u ) const { RealType absValue = vnl_math_abs( u ); unsigned int which; if ( this->m_SplineOrder % 2 == 0 ) { which = static_cast( absValue+0.5 ); } else { which = static_cast( absValue ); } if( which < static_cast( this->m_BSplineShapeFunctions.rows() ) ) { RealType der = PolynomialType( this->m_BSplineShapeFunctions.get_row( which ) ).devaluate( absValue ); if ( u < NumericTraits::Zero ) { return -der; } else { return der; } } else { return NumericTraits::Zero; } } /** * For a specific order, return the (this->m_SplineOrder+1) pieces of * the single basis function centered at zero. */ MatrixType GetShapeFunctions(); /** * For a specific order, generate and return the (this->m_SplineOrder+1) * pieces of the different basis functions in the [0, 1] interval. */ MatrixType GetShapeFunctionsInZeroToOneInterval(); protected: CoxDeBoorBSplineKernelFunction(); ~CoxDeBoorBSplineKernelFunction(); void PrintSelf( std::ostream& os, Indent indent ) const; private: CoxDeBoorBSplineKernelFunction( const Self& ); //purposely not implemented void operator=( const Self& ); //purposely not implemented /** * For a specific order, generate the (this->m_SplineOrder+1) pieces of * the single basis function centered at zero. */ void GenerateBSplineShapeFunctions( unsigned int ); /** * Use the CoxDeBoor recursion relation to generate the piecewise * polynomials which compose the basis function. * See, for example, L. Piegl, L. Tiller, "The NURBS Book," * Springer 1997, p. 50. */ PolynomialType CoxDeBoor( unsigned short, VectorType, unsigned int, unsigned int ); MatrixType m_BSplineShapeFunctions; unsigned int m_SplineOrder; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkCoxDeBoorBSplineKernelFunction.txx" #endif #endif itksnap-3.4.0/Common/ITKExtras/itkCoxDeBoorBSplineKernelFunction.txx000066400000000000000000000122131263013355200254610ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkCoxDeBoorBSplineKernelFunction.txx,v $ Language: C++ Date: $Date: 2008/10/24 12:52:08 $ Version: $Revision: 1.1 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkCoxDeBoorBSplineKernelFunction_txx #define __itkCoxDeBoorBSplineKernelFunction_txx #include "itkCoxDeBoorBSplineKernelFunction.h" namespace itk { /** * \author Nicholas J. Tustison * * Contributed by Nicholas J. Tustison, James C. Gee * in the Insight Journal paper: * http://hdl.handle.net/1926/140 * */ template CoxDeBoorBSplineKernelFunction ::CoxDeBoorBSplineKernelFunction() { this->m_SplineOrder = VSplineOrder; this->GenerateBSplineShapeFunctions( this->m_SplineOrder+1 ); } template CoxDeBoorBSplineKernelFunction ::~CoxDeBoorBSplineKernelFunction() { } template void CoxDeBoorBSplineKernelFunction ::SetSplineOrder( unsigned int order ) { if ( order != this->m_SplineOrder ) { this->m_SplineOrder = order; this->GenerateBSplineShapeFunctions( this->m_SplineOrder+1 ); this->Modified(); } } template void CoxDeBoorBSplineKernelFunction ::GenerateBSplineShapeFunctions( unsigned int order ) { unsigned int NumberOfPieces = static_cast( 0.5*( order+1 ) ); this->m_BSplineShapeFunctions.set_size( NumberOfPieces, order ); VectorType knots( order+1 ); for( unsigned int i = 0; i < knots.size(); i++) { knots[i] = -0.5*static_cast( order ) + static_cast( i ); } for ( unsigned int i = 0; i < NumberOfPieces; i++ ) { PolynomialType poly = this->CoxDeBoor(order, knots, 0, static_cast( 0.5*( order ) ) + i ); this->m_BSplineShapeFunctions.set_row( i, poly.coefficients() ); } } template typename CoxDeBoorBSplineKernelFunction::PolynomialType CoxDeBoorBSplineKernelFunction ::CoxDeBoor( unsigned short order, VectorType knots, unsigned int whichBasisFunction, unsigned int whichPiece ) { VectorType tmp(2); PolynomialType poly1(0.0), poly2(0.0); RealType den; unsigned short p = order-1; unsigned short i = whichBasisFunction; if( p == 0 && whichBasisFunction == whichPiece ) { PolynomialType poly(1.0); return poly; } // Term 1 den = knots(i+p)-knots(i); if ( den == NumericTraits::Zero ) { PolynomialType poly(0.0); poly1 = poly; } else { tmp(0) = 1.0; tmp(1) = -knots(i); tmp /= den; poly1 = PolynomialType(tmp) * this->CoxDeBoor( order-1, knots, i, whichPiece ); } // Term 2 den = knots(i+p+1)-knots(i+1); if ( den == NumericTraits::Zero ) { PolynomialType poly(0.0); poly2 = poly; } else { tmp(0) = -1.0; tmp(1) = knots(i+p+1); tmp /= den; poly2 = PolynomialType(tmp) * this->CoxDeBoor( order-1, knots, i+1, whichPiece ); } return ( poly1 + poly2 ); } template typename CoxDeBoorBSplineKernelFunction::MatrixType CoxDeBoorBSplineKernelFunction ::GetShapeFunctionsInZeroToOneInterval() { int order = this->m_SplineOrder+1; unsigned int NumberOfPieces = static_cast( order ); MatrixType ShapeFunctions( NumberOfPieces, order ); VectorType knots( 2*order ); for( unsigned int i = 0; i < knots.size(); i++ ) { knots[i] = -static_cast( this->m_SplineOrder ) + static_cast( i ); } for( unsigned int i = 0; i < NumberOfPieces; i++ ) { PolynomialType poly = this->CoxDeBoor( order, knots, i, order-1 ); ShapeFunctions.set_row( i, poly.coefficients() ); } return ShapeFunctions; } template void CoxDeBoorBSplineKernelFunction ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os, indent ); os << indent << "Spline Order: " << this->m_SplineOrder << std::endl; os << indent << "Piecewise Polynomial Pieces: " << std::endl; for ( unsigned int i = 0; i < this->m_BSplineShapeFunctions.rows(); i++ ) { RealType a = 0.0; RealType b = 0.0;; os << indent << indent; PolynomialType( this->m_BSplineShapeFunctions.get_row( i ) ).print( os ); if( i == 0 ) { if( this->m_SplineOrder % 2 == 0 ) { b = 0.5; } else { b = 1.0; } } else { a = b; b += 1.0; } os << ", X \\in [" << a << ", " << b << "]" << std::endl; } } } // namespace itk #endif itksnap-3.4.0/Common/ITKExtras/itkMaximumGradientMagnitudeImageFilter.h000066400000000000000000000122571263013355200261670ustar00rootroot00000000000000#ifndef ITKMAXIMUMGRADIENTMAGNITUDEIMAGEFILTER_H #define ITKMAXIMUMGRADIENTMAGNITUDEIMAGEFILTER_H #include "itkImageToImageFilter.h" #include "itkSimpleDataObjectDecorator.h" #include #include "itkNumericTraits.h" namespace itk { /** \class MaximumGradientMagnitudeImageFilter * \brief Computes the minimum and the maximum gradient magnitude of * an image. * * It is templated over input image type only. * This filter just copies the input image through this output to * be included within the pipeline. The implementation uses the * StatisticsImageFilter. * * \ingroup Operators * \sa StatisticsImageFilter * \ingroup ITKImageStatistics */ template< typename TInputImage > class MaximumGradientMagnitudeImageFilter: public ImageToImageFilter< TInputImage, TInputImage > { public: /** Extract dimension from input image. */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); itkStaticConstMacro(OutputImageDimension, unsigned int, TInputImage::ImageDimension); /** Standard class typedefs. */ typedef MaximumGradientMagnitudeImageFilter Self; typedef ImageToImageFilter< TInputImage, TInputImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Image related typedefs. */ typedef typename TInputImage::Pointer InputImagePointer; typedef typename TInputImage::RegionType RegionType; typedef typename TInputImage::SizeType SizeType; typedef typename TInputImage::IndexType IndexType; typedef typename TInputImage::PixelType PixelType; /** Smart Pointer type to a DataObject. */ typedef typename DataObject::Pointer DataObjectPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(MaximumGradientMagnitudeImageFilter, ImageToImageFilter); /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); /** Image typedef support. */ typedef TInputImage InputImageType; /** Type of DataObjects used for scalar outputs */ typedef SimpleDataObjectDecorator< double > DoubleObjectType; /** Return the computed Maximum. */ double GetMaximum() const { return this->GetMaximumOutput()->Get(); } DoubleObjectType * GetMaximumOutput(); const DoubleObjectType * GetMaximumOutput() const; /** Make a DataObject of the correct type to be used as the specified * output. */ typedef ProcessObject::DataObjectPointerArraySizeType DataObjectPointerArraySizeType; using Superclass::MakeOutput; virtual DataObjectPointer MakeOutput(DataObjectPointerArraySizeType idx); /** Use the image spacing information in calculations. Use this option if you * want derivatives in physical space. Default is UseImageSpacingOn. */ void SetUseImageSpacingOn() { this->SetUseImageSpacing(true); } /** Ignore the image spacing. Use this option if you want derivatives in isotropic pixel space. Default is UseImageSpacingOn. */ void SetUseImageSpacingOff() { this->SetUseImageSpacing(false); } /** Set/Get whether or not the filter will use the spacing of the input image in its calculations */ itkSetMacro(UseImageSpacing, bool); itkGetConstMacro(UseImageSpacing, bool); #ifdef ITK_USE_CONCEPT_CHECKING // Begin concept checking itkConceptMacro( LessThanComparableCheck, ( Concept::LessThanComparable< PixelType > ) ); itkConceptMacro( GreaterThanComparableCheck, ( Concept::GreaterThanComparable< PixelType > ) ); itkConceptMacro( OStreamWritableCheck, ( Concept::OStreamWritable< PixelType > ) ); // End concept checking #endif protected: MaximumGradientMagnitudeImageFilter(); virtual ~MaximumGradientMagnitudeImageFilter() {} void PrintSelf(std::ostream & os, Indent indent) const; /** Pass the input through unmodified. Do this by Grafting in the AllocateOutputs method. */ void AllocateOutputs(); /** Initialize some accumulators before the threads run. */ void BeforeThreadedGenerateData(); /** Do final mean and variance computation from data accumulated in threads. */ void AfterThreadedGenerateData(); /** Multi-thread version GenerateData. */ void ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId); // Override since the filter needs all the data for the algorithm void GenerateInputRequestedRegion(); // Override since the filter produces all of its output void EnlargeOutputRequestedRegion(DataObject *data); private: MaximumGradientMagnitudeImageFilter(const Self &); //purposely not implemented void operator=(const Self &); //purposely not implemented std::vector< double > m_ThreadMax; bool m_UseImageSpacing; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkMaximumGradientMagnitudeImageFilter.hxx" #endif #endif // ITKMAXIMUMGRADIENTMAGNITUDEIMAGEFILTER_H itksnap-3.4.0/Common/ITKExtras/itkMaximumGradientMagnitudeImageFilter.hxx000066400000000000000000000157261263013355200265530ustar00rootroot00000000000000#ifndef ITKMAXIMUMGRADIENTMAGNITUDEIMAGEFILTER_HXX #define ITKMAXIMUMGRADIENTMAGNITUDEIMAGEFILTER_HXX #include "itkMaximumGradientMagnitudeImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkNeighborhoodInnerProduct.h" #include "itkImageRegionIterator.h" #include "itkDerivativeOperator.h" #include "itkNeighborhoodAlgorithm.h" #include "itkProgressReporter.h" #include namespace itk { template< typename TInputImage > MaximumGradientMagnitudeImageFilter< TInputImage > ::MaximumGradientMagnitudeImageFilter() { this->SetNumberOfRequiredOutputs(2); // first output is a copy of the image, DataObject created by // superclass // // allocate the data objects for the remaining outputs which are // just decorators around floating point types typename DoubleObjectType::Pointer output = static_cast(this->MakeOutput(1).GetPointer()); this->ProcessObject::SetNthOutput(1, output.GetPointer()); this->GetMaximumOutput()->Set(0.0); m_UseImageSpacing = true; } template< typename TInputImage > DataObject::Pointer MaximumGradientMagnitudeImageFilter< TInputImage > ::MakeOutput(DataObjectPointerArraySizeType output) { switch ( output ) { case 0: return TInputImage::New().GetPointer(); break; case 1: return DoubleObjectType::New().GetPointer(); break; default: // might as well make an image return TInputImage::New().GetPointer(); break; } } template< typename TInputImage > typename MaximumGradientMagnitudeImageFilter< TInputImage >::DoubleObjectType * MaximumGradientMagnitudeImageFilter< TInputImage > ::GetMaximumOutput() { return static_cast< DoubleObjectType * >( this->ProcessObject::GetOutput(1) ); } template< typename TInputImage > const typename MaximumGradientMagnitudeImageFilter< TInputImage >::DoubleObjectType * MaximumGradientMagnitudeImageFilter< TInputImage > ::GetMaximumOutput() const { return static_cast< const DoubleObjectType * >( this->ProcessObject::GetOutput(1) ); } template< typename TInputImage > void MaximumGradientMagnitudeImageFilter< TInputImage > ::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); if ( this->GetInput() ) { InputImagePointer image = const_cast< typename Superclass::InputImageType * >( this->GetInput() ); image->SetRequestedRegionToLargestPossibleRegion(); } } template< typename TInputImage > void MaximumGradientMagnitudeImageFilter< TInputImage > ::EnlargeOutputRequestedRegion(DataObject *data) { Superclass::EnlargeOutputRequestedRegion(data); data->SetRequestedRegionToLargestPossibleRegion(); } template< typename TInputImage > void MaximumGradientMagnitudeImageFilter< TInputImage > ::AllocateOutputs() { // Pass the input through as the output InputImagePointer image = const_cast< TInputImage * >( this->GetInput() ); this->GraftOutput(image); // Nothing that needs to be allocated for the remaining outputs } template< typename TInputImage > void MaximumGradientMagnitudeImageFilter< TInputImage > ::BeforeThreadedGenerateData() { ThreadIdType numberOfThreads = this->GetNumberOfThreads(); // Create the thread temporaries m_ThreadMax = std::vector< double >(numberOfThreads, 0.0); } template< typename TInputImage > void MaximumGradientMagnitudeImageFilter< TInputImage > ::AfterThreadedGenerateData() { ThreadIdType i; ThreadIdType numberOfThreads = this->GetNumberOfThreads(); // Find the min/max over all threads double maximum = 0.0; for ( i = 0; i < numberOfThreads; i++ ) { if ( m_ThreadMax[i] > maximum ) { maximum = m_ThreadMax[i]; } } // Set the outputs this->GetMaximumOutput()->Set(maximum); } template< typename TInputImage > void MaximumGradientMagnitudeImageFilter< TInputImage > ::ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId) { if ( outputRegionForThread.GetNumberOfPixels() == 0 ) return; typedef float RealType; // Local maximum double localMax = 0.0; unsigned int i; ZeroFluxNeumannBoundaryCondition< TInputImage > nbc; ConstNeighborhoodIterator< TInputImage > nit; ConstNeighborhoodIterator< TInputImage > bit; NeighborhoodInnerProduct< TInputImage, RealType > SIP; // Allocate output typename InputImageType::ConstPointer input = this->GetInput(); // Set up operators DerivativeOperator< RealType, ImageDimension > op[ImageDimension]; for ( i = 0; i < ImageDimension; i++ ) { op[i].SetDirection(0); op[i].SetOrder(1); op[i].CreateDirectional(); if ( m_UseImageSpacing == true ) { if ( this->GetInput()->GetSpacing()[i] == 0.0 ) { itkExceptionMacro(<< "Image spacing cannot be zero."); } else { op[i].ScaleCoefficients(1.0 / this->GetInput()->GetSpacing()[i]); } } } // Calculate iterator radius Size< ImageDimension > radius; for ( i = 0; i < ImageDimension; ++i ) { radius[i] = op[0].GetRadius()[0]; } // Find the data-set boundary "faces" typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< TInputImage >:: FaceListType faceList; NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< TInputImage > bC; faceList = bC(input, outputRegionForThread, radius); typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< TInputImage >:: FaceListType::iterator fit; fit = faceList.begin(); // support progress methods/callbacks ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); // Process non-boundary face nit = ConstNeighborhoodIterator< TInputImage >(radius, input, *fit); std::slice x_slice[ImageDimension]; const SizeValueType center = nit.Size() / 2; for ( i = 0; i < ImageDimension; ++i ) { x_slice[i] = std::slice( center - nit.GetStride(i) * radius[i], op[i].GetSize()[0], nit.GetStride(i) ); } // Process each of the boundary faces. These are N-d regions which border // the edge of the buffer. for ( fit = faceList.begin(); fit != faceList.end(); ++fit ) { bit = ConstNeighborhoodIterator< InputImageType >(radius, input, *fit); bit.OverrideBoundaryCondition(&nbc); bit.GoToBegin(); while ( !bit.IsAtEnd() ) { RealType a = NumericTraits< RealType >::Zero; for ( i = 0; i < ImageDimension; ++i ) { const RealType g = SIP(x_slice[i], bit, op[i]); a += g * g; } if(localMax < a) localMax = a; ++bit; progress.CompletedPixel(); } } m_ThreadMax[threadId] = vcl_sqrt(localMax); } template< typename TImage > void MaximumGradientMagnitudeImageFilter< TImage > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Maximum: " << static_cast< typename NumericTraits< PixelType >::PrintType >( this->GetMaximum() ) << std::endl; } } // end namespace itk #endif itksnap-3.4.0/Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.h000066400000000000000000001003211263013355200276530ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkParallelSparseFieldLevelSetImageFilterBugFix.h,v $ Language: C++ Date: $Date: 2008/10/24 12:52:08 $ Version: $Revision: 1.1 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkParallelSparseFieldLevelSetImageFilterBugFix_h_ #define __itkParallelSparseFieldLevelSetImageFilterBugFix_h_ #include #include "itkFiniteDifferenceImageFilter.h" #include "itkSparseFieldLayer.h" #include "itkObjectStore.h" #include "itkNeighborhoodIterator.h" #include "itkConstNeighborhoodIterator.h" #include "itkMultiThreader.h" #include "itkBarrier.h" #include "itkSemaphore.h" namespace itk { /** * A data structure used in the ParallelSparsefieldlevelsetimagefilter to construct * lists of indicies and other values. */ template class ParallelSparseFieldLevelSetNode { public: TNodeIndexType m_Index; float m_Value; ParallelSparseFieldLevelSetNode *Next; ParallelSparseFieldLevelSetNode *Previous; }; /** * \class ParallelSparseFieldCityBlockNeighborList * * \brief A convenience class for storing indicies which reference neighbor * pixels within a neighborhood. * * \par * This class creates and stores indicies for use in finding neighbors within * an itk::NeighborhoodIterator object. Both an array of unsigned integer * indicies and an array of N dimensional offsets (from the center of the * neighborhood) are created and stored. The indicies and offsets correspond * to the "city-block" neighbors, that is, 4-neighbors in 2d, 6-neighbors in * 3d, etc. * * \par * Order of reference is lowest index to highest index in the neighborhood. * For example, for 4 connectivity, the indicies refer to the following * neighbors: * \code * * * 1 * * 2 * 3 * * 4 * * * \endcode * */ template class ParallelSparseFieldCityBlockNeighborList { public: typedef TNeighborhoodType NeighborhoodType; typedef typename NeighborhoodType::OffsetType OffsetType; typedef typename NeighborhoodType::RadiusType RadiusType; itkStaticConstMacro(Dimension, unsigned int, NeighborhoodType::Dimension); const RadiusType &GetRadius() const { return m_Radius; } const unsigned int &GetArrayIndex(unsigned int i) const { return m_ArrayIndex[i]; } const OffsetType &GetNeighborhoodOffset(unsigned int i) const { return m_NeighborhoodOffset[i]; } const unsigned int &GetSize() const { return m_Size; } unsigned int GetStride(unsigned int i) { return m_StrideTable[i]; } ParallelSparseFieldCityBlockNeighborList(); ~ParallelSparseFieldCityBlockNeighborList() { m_ArrayIndex.clear(); m_NeighborhoodOffset.clear(); } void Print(std::ostream &os) const; private: char pad1[128]; unsigned int m_Size; RadiusType m_Radius; std::vector m_ArrayIndex; std::vector m_NeighborhoodOffset; /** An internal table for keeping track of stride lengths in a neighborhood, * i.e. the memory offsets between pixels along each dimensional axis. */ unsigned int m_StrideTable[Dimension]; char pad2[128]; }; /** * \class ParallelSparseFieldLevelSetImageFilterBugFix * * \brief This class implements a finite difference partial differential * equation solver for evolving surfaces embedded in volumes as level-sets. * * \par * The ``sparse field'' approach to the level-set model is a logical extension * of the classical narrow band technique, which seeks to minimize * computational effort by restricting calculations to those pixels in a * region of interest around the moving surface (the \f$k\f$-level curve). The * sparse field method uses a narrow band that is exactly the width needed to * calculate changes on the level curve for the next time step. Because the * band of grid points under consideration is so sparse, this approach has * several advantages: the algorithm does exactly the number of calculations * needed to determine the next position of the \f$k\f$-level curve, and the * distance transform around the level curve can be recomputed at each * iteration. * * \par * The sparse field algorithm works by constructing a linked list of indicies * that are adjacent to the \f$k\f$-level set. These indicies are called the * ``active set''. The values at these active set indicies define the * position of the \f$k\f$-level curve. The active set indicies are shifted * to follow the distance transform embedding of the \f$k\f$-level curve as * their values move in and out of a fixed numerical range about \f$k\f$. In * this way, the active set is maintained as only those pixels adjacent to the * evolving surface. Calculations are then done only at indicies contained in * the active set. * * \par * The city-block neighborhoods of the active set indicies are maintained as * separate lists called ``layers''. At each iteration, the values at the * layers are reinitialized as the distance transform from the active set. * The number of layers can be adjusted according to the footprint needed for * the calculations on the level curve. * * \par * Briefly, the sparse field solver algorithm is as follows: * * \par * 1. For each active layer index \f$x_j\f$: Compute the change at * \f$u_{x_j}\f$, the grid point in the embedding, based on local * geometry and external forces and using a stable numerical scheme. * * 2. For each active layer index \f$x_j\f$, add the change to the grid point * value and redefine the active set indicies and those of its layers based on * any value changes which have moved outside of the numerical range allowed * for the active set. * * 3. Starting with the first layers adjacent to the active set and moving * outwards, reconstruct the distance transform by setting values in the * layers according to their neighbors. At the very outer layers, add or * remove indicies which have come into or moved out of the sparse field. * * \par HOW TO USE THIS CLASS * Typically, this class should be subclassed with additional functionality * for specific applications. It is possible, however to use this solver as a * filter directly by instantiating it and supplying it with an appropriate * LevelSetFunction object via the SetDifferenceFunction method. See the * subclasses and their associated documentation for more information on using * this class. Also see the FiniteDifferenceImageFilter documentation for a * general overview of this class of solvers. * * \par INPUTS * This filter takes an itk::Image as input. The appropriate type of input * image is entirely determined by the application. As a rule, however, the * input type is immediately converted to the output type before processing. * This is because the input is not assumed to be a real value type and must be * converted to signed, real values for the calculations. The input values * will also be shifted by the \f$k\f$ isosurface value so that the algorithm * only needs to consider the zero level set. * * \par OUTPUTS * The output of the filter is the distance transform embedding of the * isosurface as the zero level set. Values outside the surface will be * negative and values inside the surface will be positive. The distance * transform only holds for those indicies in layers around the active layer. * Elsewhere, the values are a fixed positive or negative that is one greater * than the layer of greatest magnitude. In other words, if there are three * layers, then inside values increase only to 4.0 and outside values only to * -4.0. * * \par PARAMETERS * The NumberOfLayers parameter controls the number of layers inside and * outside of the active set (see description above). The sparse field will * contain 2*NumberOfLayers+1 lists of indices: the active set and city block * neighbors inside and outside the active set. It is important to * specify enough layers to cover the footprint of your calculations. * Curvature calculations in three dimensions, for example, require 3 layers. * In two dimensions, a minimum of 2 layers is probably required. Higher order * derivatives and other geometrical measures may require more layers. If too * few layers are specified, then the calculations will pull values from the * background, which may consist of arbitrary or random values. * * \par * The IsoSurfaceValue indicates which value in the input represents the * interface of interest. By default, this value is zero. When the solver * initializes, it will subtract the IsoSurfaceValue from all values, in the * input, shifting the isosurface of interest to zero in the output. * * \par IMPORTANT! * Read the documentation for FiniteDifferenceImageFilter before attempting to * use this filter. The solver requires that you specify a * FiniteDifferenceFunction to use for calculations. This is set using the * method SetDifferenceFunction in the parent class. * * \par REFERENCES * Whitaker, Ross. A Level-Set Approach to 3D Reconstruction from Range Data. * International Journal of Computer Vision. V. 29 No. 3, 203-231. 1998. * * \par * Sethian, J.A. Level Set Methods. Cambridge University Press. 1996. * */ template class ITK_EXPORT ParallelSparseFieldLevelSetImageFilterBugFix : public FiniteDifferenceImageFilter { public: /** Standard class typedefs */ typedef ParallelSparseFieldLevelSetImageFilterBugFix Self; typedef FiniteDifferenceImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /**Typedefs from the superclass */ typedef typename Superclass::TimeStepType TimeStepType; typedef typename Superclass::FiniteDifferenceFunctionType FiniteDifferenceFunctionType; typedef typename Superclass::RadiusType RadiusType; typedef typename Superclass::NeighborhoodScalesType NeighborhoodScalesType; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(ParallelSparseFieldLevelSetImageFilterBugFix, FiniteDifferenceImageFilter); /** Information derived from the image types. */ typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename OutputImageType::IndexType IndexType; itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension); typedef typename OutputImageType::PixelType PixelType; typedef typename OutputImageType::RegionType ThreadRegionType; /** The data type used in numerical computations. Derived from the output * image type. */ typedef typename OutputImageType::ValueType ValueType; /** Node type used in parallel sparse field layer lists. */ typedef ParallelSparseFieldLevelSetNode LayerNodeType; /** A list type used in the algorithm. */ typedef SparseFieldLayer LayerType; typedef typename LayerType::Pointer LayerPointerType; /** A type for a list of LayerPointerTypes */ typedef std::vector LayerListType; /** Type used for storing status information */ typedef signed char StatusType; /** The type of the image used to index status information. Necessary for * the internals of the algorithm. */ typedef Image StatusImageType; /** Memory pre-allocator used to manage layer nodes in a multithreaded * environment. */ typedef ObjectStore LayerNodeStorageType; typedef Offset OffsetType; /** Set/Get the number of layers to use in the sparse field. Argument is the * number of layers on ONE side of the active layer, so the total layers in * the sparse field is 2 * NumberOfLayers + 1 */ itkSetMacro(NumberOfLayers, StatusType); itkGetMacro(NumberOfLayers, StatusType); /** Set/Get the value of the isosurface to use in the input image. */ itkSetMacro(IsoSurfaceValue, ValueType); itkGetMacro(IsoSurfaceValue, ValueType); LayerPointerType GetActiveListForIndex (const IndexType index) { // get the 'z' value for the index const unsigned int indexZ= index[m_SplitAxis]; // get the thread in whose region the index lies const unsigned int ThreadNum= this->GetThreadNumber (indexZ); // get the active list for that thread return m_Data[ThreadNum].m_Layers[0]; } #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(OutputEqualityComparableCheck, (Concept::EqualityComparable)); itkConceptMacro(DoubleConvertibleToOutputCheck, (Concept::Convertible)); itkConceptMacro(OutputOStreamWritableCheck, (Concept::OStreamWritable)); /** End concept checking */ #endif protected: ParallelSparseFieldLevelSetImageFilterBugFix(); ~ParallelSparseFieldLevelSetImageFilterBugFix() {} virtual void PrintSelf(std::ostream& os, Indent indent) const; /** Connectivity information for examining neighbor pixels. */ ParallelSparseFieldCityBlockNeighborList < NeighborhoodIterator > m_NeighborList; /** The constant gradient to maintain between isosurfaces in the spare-field of the level-set image. This value defaults to 1.0 */ double m_ConstantGradientValue; /** Multiplicative identity of the ValueType. */ static ValueType m_ValueOne; /** Additive identity of the ValueType. */ static ValueType m_ValueZero; /** Special status value which indicates a pending change to a more positive * sparse field. */ static StatusType m_StatusActiveChangingUp; /** Special status value which indicates a pending change to a more negative * sparse field. */ static StatusType m_StatusActiveChangingDown; /** Special status value used as a default for indicies which have no meaningful status. */ static StatusType m_StatusNull; /** Special status value which indicates pending change to another sparse * field layer. */ static StatusType m_StatusChanging; /** Special status value which indicates a pixel is on the boundary of the * image */ static StatusType m_StatusBoundaryPixel; /** This image is a copy of the input with m_IsoSurfaceValue subtracted from * each pixel. This way we only need to consider the zero level set in our * calculations. Makes the implementation easier and more efficient. * This is used only during the initialization of the level set. */ typename OutputImageType::Pointer m_ShiftedImage; /** An array which contains all of the layers needed in the sparse * field. Layers are organized as follows: m_Layer[0] = active layer, * m_Layer[i:odd] = inside layer (i+1)/2, m_Layer[i:even] = outside layer i/2. * This is used only during the initialization of the level set. */ LayerListType m_Layers; /** The number of layers to use in the sparse field. Sparse field will * consist of m_NumberOfLayers layers on both sides of a single active layer. * This active layer is the interface of interest, i.e. the zero level set.*/ StatusType m_NumberOfLayers; /** An image of status values used internally by the algorithm. */ typename StatusImageType::Pointer m_StatusImage; typename OutputImageType::Pointer m_OutputImage; /** Images used temporarily during the initialization of the thread data structures. */ typename StatusImageType::Pointer m_StatusImageTemp; typename OutputImageType::Pointer m_OutputImageTemp; /** Storage for layer node objects. */ typename LayerNodeStorageType::Pointer m_LayerNodeStore; /** The value in the input which represents the isosurface of interest. */ ValueType m_IsoSurfaceValue; /** The RMS change calculated from each update. Can be used by a subclass to * determine halting criteria. Valid only for the previous iteration, not * during the current iteration. Calculated in ApplyUpdate. */ // ValueType m_RMSChange; /** Reimplement the GenerateData() function from FiniteDifferenceImageFilter * for more effective multithreading */ virtual void GenerateData(); /** Copies the input to the output image. Processing occurs on the output * image, so the data type of the output image determines the precision of * the calculations (i.e. double or float). This method overrides the * parent class method to do some additional processing. */ void CopyInputToOutput(); /** Reserves memory in the update buffer */ void AllocateUpdateBuffer() {} /** Constructs the sparse field layers and initializes their values. Also * creates data structures that are NOT local to a thread. */ void Initialize(); /** Constructs the active layer and initialize the first layers inside and * outside of the active layer. The active layer defines the position of the * zero level set by its values, which are constrained within a range around * zero. */ void ConstructActiveLayer(); /** Initializes the values of the active layer set. */ void InitializeActiveLayerValues(); /** Initializes a layer of the sparse field using a previously initialized * layer. Builds the list of nodes in m_Layer[to] using m_Layer[from]. * Marks values in the m_StatusImage. */ void ConstructLayer(StatusType from, StatusType to); /** */ void ProcessStatusList(LayerType *InputList, StatusType ChangeToStatus, StatusType SearchForStatus, unsigned int ThreadId); /** Adjusts the values associated with all the index layers of the sparse * field by propagating out one layer at a time from the active set. This * method also takes care of deleting nodes from the layers which have been * marked in the status image as having been moved to other layers.*/ void PropagateAllLayerValues(); /** Adjusts the values in a single layer "to" using values in a neighboring * layer "from". The list of indicies in "to" are traversed and assigned * new values appropriately. Any indicies in "to" without neighbors in * "from" are moved into the "promote" layer (or deleted if "promote" is * greater than the number of layers). "InOrOut" == 1 indicates this * propagation is inwards (more negative). "InOrOut" == 0 indicates this * propagation is outwards (more positive). */ void PropagateLayerValues(StatusType from, StatusType to, StatusType promote, unsigned int InOrOut); /**This method pre-processes pixels inside and outside the sparse field * layers. The default is to set them to positive and negative values, * respectively. This is not necessary as part of the calculations, but * produces a more intuitive output for the user. */ virtual void InitializeBackgroundPixels(); /** Each thread allocates and initializes the data it will use by itself. * This maintains the memory locality of data w.r.t. the thread in a shared * memory environment.*/ void ThreadedAllocateData(unsigned int ThreadId); void ThreadedInitializeData(unsigned int ThreadId, const ThreadRegionType & ThreadRegion); /** This performs the initial load distribution among the threads. Every * thread gets a slab of the data to work on. The slabs created along a specific * dimension. Load balancing is performed along the greatest numbered dimension * (i.e. the 3rd dimension in the 3D case and the 2nd dimension in the 2D case). * During the initializing of the sparse field layer an histogram is computed * that stores the number of nodes in the active set for each index along the * chosen dimension. This histogram is used to divide the work "equally" among * threads so that each thread approximately get the same number of nodes to * process. */ void ComputeInitialThreadBoundaries(); /** Find the thread to which a pixel belongs */ unsigned int GetThreadNumber(unsigned int splitAxisValue); /** Obtain a thread's region split as per the load balancing is done. */ void GetThreadRegionSplitByBoundary(unsigned int ThreadId, ThreadRegionType& ThreadRegion); /** Delete the data and synchronization primitives used by the threads during * iterations. */ void DeallocateData(); /** Structure for managing thread-specific data */ struct ParallelSparseFieldLevelSetThreadStruct { ParallelSparseFieldLevelSetImageFilterBugFix* Filter; TimeStepType* TimeStepList; bool* ValidTimeStepList; TimeStepType TimeStep; }; /** This method calculates the change and does the update, i.e. one iteration * of this iterative solver. A barrier class is used to synchronize * execution and keep the CalculateChange and ApplyUpdate sections from * executing simultaneously. */ void Iterate(); static ITK_THREAD_RETURN_TYPE IterateThreaderCallback(void * arg); /** This method allows a subclass to override the way in which updates to * output values are applied during each iteration. The default simply * follows the standard finite difference scheme of scaling the change by the * timestep and adding to the value of the previous iteration.*/ inline virtual ValueType ThreadedCalculateUpdateValue(const unsigned int itkNotUsed(ThreadId), const IndexType itkNotUsed(index), const TimeStepType &dt, const ValueType &value, const ValueType &change) { return (value + dt * change); } // This method can be overridden in derived classes. // The pixel at 'index' is entering the active layer for thread 'ThreadId'. // The outputimage at 'index' will have the value as given by the 'value' parameter. virtual void ThreadedProcessPixelEnteringActiveLayer (const IndexType itkNotUsed(index), const ValueType itkNotUsed(value), const unsigned int itkNotUsed(ThreadId)); /** This method is not implemented or necessary for this solver */ void ApplyUpdate(TimeStepType) {} /** Does the actual work of updating the output from the UpdateContainer over * an output region supplied by the multithreading mechanism. */ virtual void ThreadedApplyUpdate(TimeStepType dt, unsigned int ThreadId); /** This method is not implemented or necessary for this solver */ TimeStepType CalculateChange() { return NumericTraits::Zero; } /** This method does the actual work of calculating change over a region * supplied by the multithreading mechanism. */ virtual TimeStepType ThreadedCalculateChange(unsigned int ThreadId); /** 1. Updates the values (in the output-image) of the nodes in the active layer * that are moving OUT of the active layer. These values are used in the * ThreadedProcessFirstLayerStatusLists() method to assign values for new nodes * that are moving IN the active layer. * 2. This function also constructs the up/down lists for nodes that are moving * out of the active layer. */ void ThreadedUpdateActiveLayerValues(TimeStepType dt, LayerType *StatusUpList, LayerType *StatusDownList, unsigned int ThreadId); /** Make a copy of the nodes in the FromList and insert them into the ToList. */ void CopyInsertList(unsigned int ThreadId, LayerPointerType FromListPtr, LayerPointerType ToListPtr); /** Delete all nodes in the List */ void ClearList(unsigned int ThreadId, LayerPointerType ListPtr); /** Make a copy of the nodes given to one thread by its neighbors to process * and insert them into the thread's own list. */ void CopyInsertInterNeighborNodeTransferBufferLayers(unsigned int ThreadId, LayerPointerType InputList, unsigned int InOrOut, unsigned int BufferLayerNumber); /** Delete all nodes in a thread's own lists which its used to transfer nodes * to neighboring threads. */ void ClearInterNeighborNodeTransferBufferLayers(unsigned int ThreadId, unsigned int InOrOut, unsigned int BufferLayerNumber); /** Performs two tasks. The situation here is that ThreadedProcessStatusList * has been called just once after the active layer values have been updated and the * UpLists and DownLists formed. Some nodes are now moving into the active layer. * The two tasks performed are: * 1. modify the status-image like it is performed by the ThreadedProcessStatusList. * 2. Update the values in the output-image for those nodes that are moving IN the * active layer. */ void ThreadedProcessFirstLayerStatusLists(unsigned int InputLayerNumber, unsigned int OutputLayerNumber, StatusType SearchForStatus, unsigned int InOrOut, unsigned int BufferLayerNumber, unsigned int ThreadId); /** Push each index in the input list into its appropriate status layer * (ChangeToStatus) and update the status image value at that index. * Also examine the neighbors of the index, (with status SearchForStatus) to determine * which need to go onto the output list. */ void ThreadedProcessStatusList(unsigned int InputLayerNumber, unsigned int OutputLayerNumber, StatusType ChangeToStatus, StatusType SearchForStatus, unsigned int InOrOut, unsigned int BufferLayerNumber, unsigned int ThreadId); /** Push each index in the input list into its appropriate status layer * (ChangeToStatus) and ... ... update the status image value at that index */ void ThreadedProcessOutsideList(unsigned int InputLayerNumber, StatusType ChangeToStatus, unsigned int InOrOut, unsigned int BufferLayerNumber, unsigned int ThreadId); /** */ void ThreadedPropagateLayerValues(StatusType from, StatusType to, StatusType promote, unsigned int InorOut, unsigned int ThreadId); /** Split the volume uniformly along the chosen dimension for post processing * the output. */ void GetThreadRegionSplitUniformly(unsigned int ThreadId, ThreadRegionType& ThreadRegion); /** Assign background pixels INSIDE the sparse field layers to a new level set * with value less than the innermost layer. Assign background pixels * OUTSIDE the sparse field layers to a new level set with value greater than * the outermost layer. */ void ThreadedPostProcessOutput(const ThreadRegionType & regionToProcess); /** Check if the load is fairly balanced among the threads. * This is performed by just one thread while all other threads wait. * This need NOT be performed every iteration because the level-set surface moves slowly * and it is correct to believe that during an iteration the movement is small enough that * the small gain obtained by load balancing (if any) does not warrant the overhead for * calling this method. * How often this is done is controlled by a parameter LOAD_BALANCE_ITERATION_FREQUENCY * which is defined in the IterateThreaderCallback() function. * A parameter that defines a degree of unbalancedness of the load among threads is * MAX_PIXEL_DIFFERENCE_PERCENT which is defined in CheckLoadBalance(). */ virtual void CheckLoadBalance(); /** Redistribute an load among the threads to obtain a more balanced load distribution. * This is performed in parallel by all the threads. */ virtual void ThreadedLoadBalance(unsigned int ThreadId); /** Thread synchronization methods. */ void WaitForAll(); void SignalNeighborsAndWait (unsigned int ThreadId); void SignalNeighbor (unsigned int SemaphoreArrayNumber, unsigned int ThreadId); void WaitForNeighbor (unsigned int SemaphoreArrayNumber, unsigned int ThreadId); /** If child classes need an entry point to the start of every iteration step * they can override this method. This method is defined but empty in this class. */ virtual void ThreadedInitializeIteration (unsigned int ThreadId); /** For debugging. Writes the active layer set (grid-points closest to evolving * interface) to a file. */ // void WriteActivePointsToFile (); /** The number of threads to use. */ unsigned int m_NumOfThreads; /** The dimension along which to distribute the load. */ unsigned int m_SplitAxis; /** The length of the dimension along which to distribute the load. */ unsigned int m_ZSize; /** A boolean variable stating if the boundaries had been changed during * CheckLoadBalance() */ bool m_BoundaryChanged; /** The boundaries defining thread regions */ unsigned int * m_Boundary; /** Histogram of number of pixels in each Z plane for the entire 3D volume */ int * m_GlobalZHistogram; /** The mapping from a z-value to the thread in whose region the z-value lies */ unsigned int * m_MapZToThreadNumber; /** Cumulative frequency of number of pixels in each Z plane for the entire 3D * volume */ int * m_ZCumulativeFrequency; /** A global barrier used for synchronization between all threads. */ typename Barrier::Pointer m_Barrier; /** Local data for each individual thread. */ struct ThreadData { char pad1 [128]; TimeStepType TimeStep; ThreadRegionType ThreadRegion; ValueType m_RMSChange; unsigned int m_Count; /** The layers */ LayerListType m_Layers; /** Used to transfer data between m_Layers during load balancing */ LayerListType * m_LoadTransferBufferLayers; /** Node memory pool */ typename LayerNodeStorageType::Pointer m_LayerNodeStore; LayerPointerType UpList[2]; LayerPointerType DownList[2]; /** Used to transfer data between UpList and DownList across thread * boundaries */ LayerPointerType** m_InterNeighborNodeTransferBufferLayers[2]; /** A pointer to the GlobalData struct obtained from the difference function. * Every thread has its own copy of the struct */ void * globalData; /** Local histogram with each thread */ int * m_ZHistogram; /** Semaphores used for signalling and waiting neighbor threads. * Strictly speaking the semaphores are NOT just accessed by the thread that owns them * BUT also by the thread's neighbors. So they are NOT truly "local" data. */ typename Semaphore::Pointer m_Semaphore[2]; /** Indicates whether to use m_Semaphore[0] or m_Semaphore[1] for signalling/waiting */ unsigned int m_SemaphoreArrayNumber; char pad2 [128]; }; /** An array storing the individual (local) data structures for each thread. */ ThreadData *m_Data; /** Used to check if there are too few pixels remaining. If yes, then we can * stop iterating. */ bool m_Stop; /** This flag tells the solver whether or not to interpolate for the actual surface location when calculating change at each active layer node. By default this is turned on. Subclasses which do not sample propagation (speed), advection, or curvature terms should turn this flag off. */ bool m_InterpolateSurfaceLocation; private: ParallelSparseFieldLevelSetImageFilterBugFix(const Self&); // purposely not implemented void operator=(const Self&); // purposely not implemented /** This flag is true when methods need to check boundary conditions and * false when methods do not need to check for boundary conditions. */ bool m_BoundsCheckingActive; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkParallelSparseFieldLevelSetImageFilterBugFix.txx" #endif #endif itksnap-3.4.0/Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.txx000066400000000000000000002724311263013355200302630ustar00rootroot00000000000000/*====================================================================== Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkParallelSparseFieldLevelSetImageFilterBugFix.txx,v $ Language: C++ Date: $Date: 2008/10/24 12:52:08 $ Version: $Revision: 1.1 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. ======================================================================*/ #ifndef __itkParallelSparseFieldLevelSetImageFilterBugFix_txx_ #define __itkParallelSparseFieldLevelSetImageFilterBugFix_txx_ #include "itkParallelSparseFieldLevelSetImageFilterBugFix.h" #include "itkZeroCrossingImageFilter.h" #include "itkShiftScaleImageFilter.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIterator.h" #include "itkNumericTraits.h" #include "itkNeighborhoodAlgorithm.h" #include "itkMacro.h" #include #include namespace itk { template ParallelSparseFieldCityBlockNeighborList ::ParallelSparseFieldCityBlockNeighborList() { typedef typename NeighborhoodType::ImageType ImageType; typename ImageType::Pointer dummy_image = ImageType::New(); unsigned int i, nCenter; int d; OffsetType zero_offset; for (i = 0; i < Dimension; ++i) { m_Radius[i] = 1; zero_offset[i] = 0; } NeighborhoodType it(m_Radius, dummy_image, dummy_image->GetRequestedRegion()); nCenter = it.Size() / 2; m_Size = 2 * Dimension; m_ArrayIndex.reserve(m_Size); m_NeighborhoodOffset.reserve(m_Size); for (i = 0; i < m_Size; ++i) { m_NeighborhoodOffset.push_back(zero_offset); } for (d = Dimension - 1, i = 0; d >= 0; --d, ++i) { m_ArrayIndex.push_back( nCenter - it.GetStride(d) ); m_NeighborhoodOffset[i][d] = -1; } for (d = 0; d < static_cast (Dimension); ++d, ++i) { m_ArrayIndex.push_back( nCenter + it.GetStride(d) ); m_NeighborhoodOffset[i][d] = 1; } for (i = 0; i < Dimension; ++i) { m_StrideTable[i] = it.GetStride(i); } } template void ParallelSparseFieldCityBlockNeighborList ::Print(std::ostream &os) const { os << "ParallelSparseFieldCityBlockNeighborList: " << std::endl; for (unsigned i = 0; i < this->GetSize(); ++i) { os << "m_ArrayIndex[" << i << "]: " << m_ArrayIndex[i] << std::endl << "m_NeighborhoodOffset[" << i << "]: " << m_NeighborhoodOffset[i] << std::endl; } } //template //double ParallelSparseFieldLevelSetImageFilterBugFix //::m_ConstantGradientValue = 1.0; template ITK_TYPENAME ParallelSparseFieldLevelSetImageFilterBugFix::ValueType ParallelSparseFieldLevelSetImageFilterBugFix ::m_ValueOne = NumericTraits::ValueType >::One; template ITK_TYPENAME ParallelSparseFieldLevelSetImageFilterBugFix::ValueType ParallelSparseFieldLevelSetImageFilterBugFix ::m_ValueZero = NumericTraits::ValueType >::Zero; template ITK_TYPENAME ParallelSparseFieldLevelSetImageFilterBugFix::StatusType ParallelSparseFieldLevelSetImageFilterBugFix ::m_StatusNull = NumericTraits::StatusType >::NonpositiveMin(); template ITK_TYPENAME ParallelSparseFieldLevelSetImageFilterBugFix::StatusType ParallelSparseFieldLevelSetImageFilterBugFix ::m_StatusChanging = -1; template ITK_TYPENAME ParallelSparseFieldLevelSetImageFilterBugFix::StatusType ParallelSparseFieldLevelSetImageFilterBugFix ::m_StatusActiveChangingUp = -2; template ITK_TYPENAME ParallelSparseFieldLevelSetImageFilterBugFix::StatusType ParallelSparseFieldLevelSetImageFilterBugFix ::m_StatusActiveChangingDown = -3; template ITK_TYPENAME ParallelSparseFieldLevelSetImageFilterBugFix::StatusType ParallelSparseFieldLevelSetImageFilterBugFix ::m_StatusBoundaryPixel = -4; template ParallelSparseFieldLevelSetImageFilterBugFix ::ParallelSparseFieldLevelSetImageFilterBugFix() { m_IsoSurfaceValue = m_ValueZero; m_NumberOfLayers = ImageDimension; this->SetRMSChange( static_cast( m_ValueOne ) ); m_InterpolateSurfaceLocation = true; m_BoundsCheckingActive = false; m_ConstantGradientValue = 1.0; m_GlobalZHistogram = 0; m_ZCumulativeFrequency = 0; m_MapZToThreadNumber = 0; m_Boundary = 0; m_Data = 0; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::GenerateData() { if (this->GetState() == Superclass::UNINITIALIZED) { // Clean up any memory from any aborted previous filter executions. this->DeallocateData(); // Allocate the output image m_OutputImage= this->GetOutput(); m_OutputImage->SetBufferedRegion(m_OutputImage->GetRequestedRegion()); m_OutputImage->Allocate(); // Copy the input image to the output image. Algorithms will operate // directly on the output image this->CopyInputToOutput(); // Perform any other necessary pre-iteration initialization. this->Initialize(); this->SetElapsedIterations(0); //NOTE: Cannot set state to initialized yet since more initialization is //done in the Iterate method. } // Evolve the surface this->Iterate(); // Clean up if (this->GetManualReinitialization() == false) { this->DeallocateData(); this->SetStateToUninitialized(); // Reset the state once execution is // completed } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::CopyInputToOutput() { // This method is the first step in initializing the level-set image, which // is also the output of the filter. The input is passed through a // zero crossing filter, which produces zero's at pixels closest to the zero // level set and one's elsewhere. The actual zero level set values will be // adjusted in the Initialize() step to more accurately represent the // position of the zero level set. // First need to subtract the iso-surface value from the input image. typedef ShiftScaleImageFilter ShiftScaleFilterType; typename ShiftScaleFilterType::Pointer shiftScaleFilter = ShiftScaleFilterType::New(); shiftScaleFilter->SetInput( this->GetInput() ); shiftScaleFilter->SetShift( - m_IsoSurfaceValue ); // keep a handle to the shifted output m_ShiftedImage = shiftScaleFilter->GetOutput(); typename ZeroCrossingImageFilter::Pointer zeroCrossingFilter = ZeroCrossingImageFilter::New(); zeroCrossingFilter->SetInput(m_ShiftedImage); zeroCrossingFilter->GraftOutput(m_OutputImage); zeroCrossingFilter->SetBackgroundValue(m_ValueOne); zeroCrossingFilter->SetForegroundValue(m_ValueZero); zeroCrossingFilter->SetNumberOfThreads(1); zeroCrossingFilter->Update(); // Here the output is the result of zerocrossings this->GraftOutput(zeroCrossingFilter->GetOutput()); } template void ParallelSparseFieldLevelSetImageFilterBugFix ::Initialize() { unsigned int i; // A node pool used during initialization of the level set. m_LayerNodeStore = LayerNodeStorageType::New(); m_LayerNodeStore->SetGrowthStrategyToExponential(); // Allocate the status image. m_StatusImage = StatusImageType::New(); m_StatusImage->SetRegions(m_OutputImage->GetRequestedRegion()); m_StatusImage->Allocate(); // Initialize the status image to contain all m_StatusNull values. ImageRegionIterator statusIt(m_StatusImage, m_StatusImage->GetRequestedRegion()); for (statusIt = statusIt.Begin(); ! statusIt.IsAtEnd(); ++statusIt) { statusIt.Set( m_StatusNull ); } // Initialize the boundary pixels in the status images to // m_StatusBoundaryPixel values. Uses the face calculator to find all of the // region faces. typedef NeighborhoodAlgorithm::ImageBoundaryFacesCalculator BFCType; BFCType faceCalculator; typename BFCType::FaceListType faceList; typename BFCType::SizeType sz; typename BFCType::FaceListType::iterator fit; sz.Fill(1); faceList = faceCalculator(m_StatusImage, m_StatusImage->GetRequestedRegion(), sz); fit = faceList.begin(); for (++fit; fit != faceList.end(); ++fit) // skip the first (nonboundary) region { statusIt = ImageRegionIterator(m_StatusImage, *fit); for (statusIt.GoToBegin(); ! statusIt.IsAtEnd(); ++statusIt) { statusIt.Set( m_StatusBoundaryPixel ); } } // Allocate the layers of the sparse field. m_Layers.reserve(2 * m_NumberOfLayers + 1); for (i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; ++i) { m_Layers.push_back( LayerType::New() ); } m_SplitAxis = m_OutputImage->GetImageDimension() - 1; // always the "Z" dimension if (m_OutputImage->GetImageDimension() < 1) { // cannot split itkDebugMacro ("Unable to choose an axis for workload distribution among threads"); return; } typename OutputImageType::SizeType requestedRegionSize = m_OutputImage->GetRequestedRegion().GetSize(); m_ZSize = requestedRegionSize[m_SplitAxis]; // Histogram of number of pixels in each Z plane for the entire 3D volume m_GlobalZHistogram = new int[m_ZSize]; for (i = 0; i < m_ZSize; i++) { m_GlobalZHistogram[i] = 0; } // Construct the active layer and initialize the first layers inside and // outside of the active layer this->ConstructActiveLayer(); // Construct the rest of the non active set layers using the first two // layers. Inside layers are odd numbers, outside layers are even numbers. for (i = 1; i < m_Layers.size() - 2; ++i) { this->ConstructLayer(i, i+2); } // Set the values in the output image for the active layer. this->InitializeActiveLayerValues(); // Initialize layer values using the active layer as seeds. this->PropagateAllLayerValues(); // Initialize pixels inside and outside the sparse field layers to positive // and negative values, respectively. This is not necessary for the // calculations, but is useful for presenting a more intuitive output to the // filter. See PostProcessOutput method for more information. this->InitializeBackgroundPixels(); m_NumOfThreads = this->GetNumberOfThreads(); // Cumulative frequency of number of pixels in each Z plane for the entire 3D // volume m_ZCumulativeFrequency = new int[m_ZSize]; for (i = 0; i < m_ZSize; i++) { m_ZCumulativeFrequency[i] = 0; } // The mapping from a z-value to the thread in whose region the z-value lies m_MapZToThreadNumber = new unsigned int[m_ZSize]; for (i = 0; i < m_ZSize; i++) { m_MapZToThreadNumber[i] = 0; } // The boundaries defining thread regions m_Boundary = new unsigned int[m_NumOfThreads]; for (i = 0; i < m_NumOfThreads; i++) { m_Boundary[i] = 0; } // A boolean variable stating if the boundaries had been changed during // CheckLoadBalance() m_BoundaryChanged = false; // A global barrier for all threads. m_Barrier = Barrier::New(); m_Barrier->Initialize(m_NumOfThreads); // Allocate data for each thread. m_Data = new ThreadData[m_NumOfThreads]; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ConstructActiveLayer() { // We find the active layer by searching for 0's in the zero crossing image // (output image). The first inside and outside layers are also constructed // by searching the neighbors of the active layer in the (shifted) input image. // Negative neighbors not in the active set are assigned to the inside, // positive neighbors are assigned to the outside. NeighborhoodIterator shiftedIt(m_NeighborList.GetRadius(), m_ShiftedImage, m_OutputImage->GetRequestedRegion()); NeighborhoodIterator outputIt (m_NeighborList.GetRadius(), m_OutputImage, m_OutputImage->GetRequestedRegion()); NeighborhoodIterator statusIt (m_NeighborList.GetRadius(), m_StatusImage, m_OutputImage->GetRequestedRegion()); IndexType center_index, offset_index; LayerNodeType *node; bool bounds_status = true; ValueType value; StatusType layer_number; typename OutputImageType::SizeType regionSize = m_OutputImage->GetRequestedRegion().GetSize(); typename OutputImageType::IndexType startIndex = m_OutputImage->GetRequestedRegion().GetIndex();; typedef typename OutputImageType::IndexType::IndexValueType StartIndexValueType; for (outputIt.GoToBegin(); !outputIt.IsAtEnd(); ++outputIt) { bounds_status = true; if ( outputIt.GetCenterPixel() == m_ValueZero ) { // Grab the neighborhood in the status image. center_index = outputIt.GetIndex(); statusIt.SetLocation( center_index ); for(unsigned int j = 0; j < ImageDimension; j++) { if ( (center_index[j]) <= (startIndex[j]) || (center_index[j]) >= startIndex[j] + static_cast(regionSize[j]-1)) { bounds_status = false; break; } } if(bounds_status == true) { // Here record the hisgram information m_GlobalZHistogram[ center_index[m_SplitAxis] ]++; // Borrow a node from the store and set its value. node = m_LayerNodeStore->Borrow(); node->m_Index = center_index; // Add the node to the active list and set the status in the status // image. m_Layers[0]->PushFront( node ); statusIt.SetCenterPixel( 0 ); // Grab the neighborhood in the image of shifted input values. shiftedIt.SetLocation( center_index ); // Search the neighborhood pixels for first inside & outside layer // members. Construct these lists and set status list values. for (unsigned int i = 0; i < m_NeighborList.GetSize(); ++i) { offset_index = center_index + m_NeighborList.GetNeighborhoodOffset(i); if ( outputIt.GetPixel(m_NeighborList.GetArrayIndex(i)) != m_ValueZero && statusIt.GetPixel(m_NeighborList.GetArrayIndex(i)) == m_StatusNull) { value = shiftedIt.GetPixel(m_NeighborList.GetArrayIndex(i)); if ( value < m_ValueZero ) // Assign to first outside layer. { layer_number = 1; } else // Assign to first inside layer { layer_number = 2; } statusIt.SetPixel( m_NeighborList.GetArrayIndex(i), layer_number, bounds_status ); if ( bounds_status == true ) // In bounds { node = m_LayerNodeStore->Borrow(); node->m_Index = offset_index; m_Layers[layer_number]->PushFront( node ); } // else do nothing. } } } } } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ConstructLayer(StatusType from, StatusType to) { LayerNodeType *node; bool boundary_status; typename LayerType::ConstIterator fromIt; NeighborhoodIterator statusIt(m_NeighborList.GetRadius(), m_StatusImage, m_OutputImage->GetRequestedRegion() ); // For all indicies in the "from" layer... for (fromIt = m_Layers[from]->Begin(); fromIt != m_Layers[from]->End(); ++fromIt) { // Search the neighborhood of this index in the status image for // unassigned indicies. Push those indicies onto the "to" layer and // assign them values in the status image. Status pixels outside the // boundary will be ignored. statusIt.SetLocation( fromIt->m_Index ); for (unsigned int i = 0; i < m_NeighborList.GetSize(); ++i) { if ( statusIt.GetPixel( m_NeighborList.GetArrayIndex(i) ) == m_StatusNull ) { statusIt.SetPixel(m_NeighborList.GetArrayIndex(i), to, boundary_status); if (boundary_status == true) // in bounds { node = m_LayerNodeStore->Borrow(); node->m_Index = statusIt.GetIndex() + m_NeighborList.GetNeighborhoodOffset(i); m_Layers[to]->PushFront( node ); } } } } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::InitializeActiveLayerValues() { const ValueType CHANGE_FACTOR = m_ConstantGradientValue / 2.0; ValueType MIN_NORM = 1.0e-6; if (this->GetUseImageSpacing()) { double minSpacing = NumericTraits::max(); for (unsigned int i=0; iGetInput()->GetSpacing()[i]); } MIN_NORM *= minSpacing; } typename LayerType::ConstIterator activeIt; ConstNeighborhoodIteratorshiftedIt (m_NeighborList.GetRadius(), m_ShiftedImage, m_OutputImage->GetRequestedRegion()); unsigned int center = shiftedIt.Size() /2; unsigned int stride; const NeighborhoodScalesType neighborhoodScales = this->GetDifferenceFunction()->ComputeNeighborhoodScales(); ValueType dx_forward, dx_backward, length, distance; // For all indicies in the active layer... for (activeIt = m_Layers[0]->Begin(); activeIt != m_Layers[0]->End(); ++activeIt) { // Interpolate on the (shifted) input image values at this index to // assign an active layer value in the output image. shiftedIt.SetLocation( activeIt->m_Index ); length = m_ValueZero; for (unsigned int i = 0; i < static_cast(ImageDimension); ++i) { stride = shiftedIt.GetStride(i); dx_forward = ( shiftedIt.GetPixel(center + stride) - shiftedIt.GetCenterPixel() ) * neighborhoodScales[i]; dx_backward = ( shiftedIt.GetCenterPixel() - shiftedIt.GetPixel(center - stride) ) * neighborhoodScales[i]; if ( vnl_math_abs(dx_forward) > vnl_math_abs(dx_backward) ) { length += dx_forward * dx_forward; } else { length += dx_backward * dx_backward; } } length = vcl_sqrt(length) + MIN_NORM; distance = shiftedIt.GetCenterPixel() / length; m_OutputImage->SetPixel( activeIt->m_Index , vnl_math_min(vnl_math_max(-CHANGE_FACTOR, distance), CHANGE_FACTOR)); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::PropagateAllLayerValues() { // Update values in the first inside and first outside layers using the // active layer as a seed. Inside layers are odd numbers, outside layers are // even numbers. this->PropagateLayerValues (0, 1, 3, 1); // first inside this->PropagateLayerValues (0, 2, 4, 0); // first outside // Update the rest of the layers. for (unsigned int i = 1; i < m_Layers.size() - 2; ++i) { this->PropagateLayerValues (i, i+2, i+4, (i+2)%2); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::PropagateLayerValues(StatusType from, StatusType to, StatusType promote, unsigned int InOrOut) { unsigned int i; ValueType value, value_temp, delta; bool found_neighbor_flag; LayerNodeType* node; StatusType past_end = static_cast( m_Layers.size() ) - 1; // Are we propagating values inward (more negative) or outward (more positive)? if (InOrOut == 1) { delta = - m_ConstantGradientValue; // inward } else { delta = m_ConstantGradientValue; } NeighborhoodIterator outputIt (m_NeighborList.GetRadius(), m_OutputImage, m_OutputImage->GetRequestedRegion()); NeighborhoodIterator statusIt (m_NeighborList.GetRadius(), m_StatusImage, m_OutputImage->GetRequestedRegion()); typename LayerType::Iterator toIt = m_Layers[to]->Begin(); while ( toIt != m_Layers[to]->End() ) { statusIt.SetLocation( toIt->m_Index ); // Is this index marked for deletion? If the status image has // been marked with another layer's value, we need to delete this node // from the current list then skip to the next iteration. if (statusIt.GetCenterPixel() != to) { node = toIt.GetPointer(); ++toIt; m_Layers[to]->Unlink( node ); m_LayerNodeStore->Return( node ); continue; } outputIt.SetLocation( toIt->m_Index ); value = m_ValueZero; found_neighbor_flag = false; for (i = 0; i < m_NeighborList.GetSize(); ++i) { // If this neighbor is in the "from" list, compare its absolute value // to any previous values found in the "from" list. Keep the value // that will cause the next layer to be closest to the zero level set. if ( statusIt.GetPixel( m_NeighborList.GetArrayIndex(i) ) == from ) { value_temp = outputIt.GetPixel( m_NeighborList.GetArrayIndex(i) ); if (found_neighbor_flag == false) { value = value_temp; } else { if (vnl_math_abs(value_temp+delta) < vnl_math_abs(value+delta)) { // take the value closest to zero value= value_temp; } } found_neighbor_flag = true; } } if (found_neighbor_flag == true) { // Set the new value using the smallest magnitude // found in our "from" neighbors. outputIt.SetCenterPixel( value + delta ); ++toIt; } else { // Did not find any neighbors on the "from" list, then promote this // node. A "promote" value past the end of my sparse field size // means delete the node instead. Change the status value in the // status image accordingly. node = toIt.GetPointer(); ++toIt; m_Layers[to]->Unlink( node ); if ( promote > past_end ) { m_LayerNodeStore->Return( node ); statusIt.SetCenterPixel(m_StatusNull); } else { m_Layers[promote]->PushFront( node ); statusIt.SetCenterPixel(promote); } } } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::InitializeBackgroundPixels() { // Assign background pixels INSIDE the sparse field layers to a new level set // with value less than the innermost layer. Assign background pixels // OUTSIDE the sparse field layers to a new level set with value greater than // the outermost layer. const ValueType max_layer = static_cast(m_NumberOfLayers); const ValueType outside_value = (max_layer+1) * m_ConstantGradientValue; const ValueType inside_value = -(max_layer+1) * m_ConstantGradientValue; ImageRegionConstIterator statusIt(m_StatusImage, this->GetOutput()->GetRequestedRegion()); ImageRegionIterator outputIt(this->GetOutput(), this->GetOutput()->GetRequestedRegion()); ImageRegionConstIterator shiftedIt(m_ShiftedImage, this->GetOutput()->GetRequestedRegion()); for (outputIt = outputIt.Begin(), statusIt = statusIt.Begin(), shiftedIt = shiftedIt.Begin(); ! outputIt.IsAtEnd(); ++outputIt, ++statusIt, ++shiftedIt) { if (statusIt.Get() == m_StatusNull || statusIt.Get() == m_StatusBoundaryPixel) { if (shiftedIt.Get() > m_ValueZero) { outputIt.Set(outside_value); } else { outputIt.Set(inside_value); } } } // deallocate the shifted-image m_ShiftedImage = 0; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ComputeInitialThreadBoundaries() { // NOTE: Properties of the boundary computation algorithm // 1. Thread-0 always has something to work on. // 2. If a particular thread numbered i has the m_Boundary = (mZSize - // 1) then ALL threads numbered > i do NOT have anything to work on. // Compute the cumulative frequency distribution using the global histogram. unsigned int i, j; m_ZCumulativeFrequency[0] = m_GlobalZHistogram[0]; for (i= 1; i < m_ZSize; i++) { m_ZCumulativeFrequency[i] = m_ZCumulativeFrequency[i-1] + m_GlobalZHistogram[i]; } // Now define the regions that each thread will process and the corresponding // boundaries. m_Boundary[m_NumOfThreads - 1] = m_ZSize - 1; // special case: the upper // bound for the last thread for (i= 0; i < m_NumOfThreads - 1; i++) { // compute m_Boundary[i] float cutOff = 1.0 * (i+1) * m_ZCumulativeFrequency[m_ZSize-1] / m_NumOfThreads; // find the position in the cumulative freq dist where this cutoff is met for (j = (i == 0 ? 0 : m_Boundary[i-1]); j < m_ZSize; j++) { if (cutOff > m_ZCumulativeFrequency[j]) { continue; } else { // Optimize a little. // Go further FORWARD and find the first index (k) in the cumulative // freq distribution s.t. m_ZCumulativeFrequency[k] != // m_ZCumulativeFrequency[j] This is to be done because if we have a // flat patch in the cumulative freq. dist. then we can choose // a bound midway in that flat patch . unsigned int k; for (k= 1; j+k < m_ZSize; k++) { if (m_ZCumulativeFrequency[j+k] != m_ZCumulativeFrequency[j]) { break; } } // m_Boundary[i]= static_cast( (j + k / 2) ); break; } } } // Initialize the local histograms using the global one and the boundaries // Also initialize the mapping from the Z value --> the thread number // i.e. m_MapZToThreadNumber[] // Also divide the lists up according to the boundaries for (i = 0; i <= m_Boundary[0]; i++) { // this Z belongs to the region associated with thread-0 m_MapZToThreadNumber[i]= 0; } for (unsigned int t= 1; t < m_NumOfThreads; t++) { for (i = m_Boundary[t-1]+1; i <= m_Boundary[t]; i++) { // this Z belongs to the region associated with thread-0 m_MapZToThreadNumber[i]= t; } } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedAllocateData (unsigned int ThreadId) { static const float SAFETY_FACTOR = 4.0; unsigned int i, j; // create semaphores m_Data[ThreadId].m_Semaphore[0] = Semaphore::New (); m_Data[ThreadId].m_Semaphore[1] = Semaphore::New (); m_Data[ThreadId].m_Semaphore[0]->Initialize(0); m_Data[ThreadId].m_Semaphore[1]->Initialize(0); // Allocate the layers for the sparse field. m_Data[ThreadId].m_Layers.reserve(2 * m_NumberOfLayers + 1); for (i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; ++i) { m_Data[ThreadId].m_Layers.push_back( LayerType::New() ); } // Throw an exception if we don't have enough layers. if (m_Data[ThreadId].m_Layers.size() < 3) { itkExceptionMacro( << "Not enough layers have been allocated for the sparse" << "field. Requires at least one layer." ); } // Layers used as buffers for transfering pixels during load balancing m_Data[ThreadId].m_LoadTransferBufferLayers = new LayerListType[2*m_NumberOfLayers+1]; for (i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; i++) { m_Data[ThreadId].m_LoadTransferBufferLayers[i].reserve( m_NumOfThreads ); for (j = 0; j < m_NumOfThreads; j++) { m_Data[ThreadId].m_LoadTransferBufferLayers[i].push_back(LayerType::New()); } } // Every thread allocates a local node pool (improving memory locality) m_Data[ThreadId].m_LayerNodeStore = LayerNodeStorageType::New(); m_Data[ThreadId].m_LayerNodeStore->SetGrowthStrategyToExponential(); // The SAFETY_FACTOR simple ensures that the number of nodes created // is larger than those required to start with for each thread. unsigned int nodeNum= static_cast(SAFETY_FACTOR * m_Layers[0]->Size() * (2*m_NumberOfLayers+1) / m_NumOfThreads); m_Data[ThreadId].m_LayerNodeStore->Reserve(nodeNum); m_Data[ThreadId].m_RMSChange = m_ValueZero; // UpLists and Downlists for (i = 0; i < 2; ++i) { m_Data[ThreadId].UpList[i] = LayerType::New(); m_Data[ThreadId].DownList[i] = LayerType::New(); } // Used during the time when status lists are being processed (in ThreadedApplyUpdate() ) // for the Uplists m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[0] = new LayerPointerType * [m_NumberOfLayers + 1]; // for the Downlists m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[1] = new LayerPointerType * [m_NumberOfLayers + 1]; for (i= 0; i < static_cast(m_NumberOfLayers) + 1; i++) { m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[0][i] = new LayerPointerType[m_NumOfThreads]; m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[1][i] = new LayerPointerType[m_NumOfThreads]; } for (i= 0; i < static_cast(m_NumberOfLayers) + 1; i++) { for (j= 0; j < m_NumOfThreads; j++) { m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[0][i][j] = LayerType::New(); m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[1][i][j] = LayerType::New(); } } // Local histogram for every thread (used during Iterate() ) m_Data[ThreadId].m_ZHistogram = new int[m_ZSize]; for (i = 0; i < m_ZSize; i++) { m_Data[ThreadId].m_ZHistogram[i] = 0; } // Every thread must have its own copy of the the GlobalData struct. m_Data[ThreadId].globalData = this->GetDifferenceFunction()->GetGlobalDataPointer(); // m_Data[ThreadId].m_SemaphoreArrayNumber = 0; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedInitializeData(unsigned int ThreadId, const ThreadRegionType & ThreadRegion) { // divide the lists based on the boundaries LayerNodeType * nodePtr, * nodeTempPtr; for (unsigned int i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; i++) { typename LayerType::Iterator layerIt = m_Layers[i]->Begin(); typename LayerType::Iterator layerEnd= m_Layers[i]->End(); while (layerIt != layerEnd) { nodePtr = layerIt.GetPointer(); ++layerIt; unsigned int k = this->GetThreadNumber(nodePtr->m_Index[m_SplitAxis]); if (k != ThreadId) { continue; // some other thread's node => ignore } // Borrow a node from the specific thread's layer so that MEMORY LOCALITY // is maintained. // NOTE : We already pre-allocated more than enough // nodes for each thread implying no new nodes are created here. nodeTempPtr= m_Data[ThreadId].m_LayerNodeStore->Borrow (); nodeTempPtr->m_Index= nodePtr->m_Index; // push the node on the approproate layer m_Data[ThreadId].m_Layers[i]->PushFront(nodeTempPtr); // for the active layer (layer-0) build the histogram for each thread if (i == 0) { // this Z histogram value should be given to thread-0 m_Data[ThreadId].m_ZHistogram[ (nodePtr->m_Index)[m_SplitAxis] ] = m_Data[ThreadId].m_ZHistogram[ (nodePtr->m_Index)[m_SplitAxis] ] + 1; } } } // Make use of the SGI default "first-touch" memory placement policy // Copy from the current status/output images to the new ones and let each // thread do the copy of its own region. // This will make each thread be the FIRST to write to "it's" data in the new // images and hence the memory will get allocated // in the corresponding thread's memory-node. ImageRegionConstIterator statusIt(m_StatusImage, ThreadRegion); ImageRegionIterator statusItNew (m_StatusImageTemp, ThreadRegion); ImageRegionConstIterator outputIt(m_OutputImage, ThreadRegion); ImageRegionIterator outputItNew(m_OutputImageTemp, ThreadRegion); for (outputIt = outputIt.Begin(), statusIt = statusIt.Begin(), outputItNew = outputItNew.Begin(), statusItNew = statusItNew.Begin(); ! outputIt.IsAtEnd(); ++outputIt, ++statusIt, ++outputItNew, ++statusItNew) { statusItNew.Set (statusIt.Get()); outputItNew.Set (outputIt.Get()); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::DeallocateData() { unsigned int i, j; // Delete data structures used for load distribution and balancing. if ( m_GlobalZHistogram != 0 ) { delete [] m_GlobalZHistogram; m_GlobalZHistogram = 0; } if ( m_ZCumulativeFrequency != 0 ) { delete [] m_ZCumulativeFrequency; m_ZCumulativeFrequency = 0; } if ( m_MapZToThreadNumber != 0 ) { delete [] m_MapZToThreadNumber; m_MapZToThreadNumber = 0; } if (m_Boundary != 0) { delete [] m_Boundary; m_Boundary = 0; } // Deallocate the status image. m_StatusImage= 0; // Remove the barrier from the system. // m_Barrier->Remove (); // Delete initial nodes, the node pool, the layers. if (! m_Layers.empty()) { for (i = 0; i < 2* static_cast(m_NumberOfLayers)+1; i++) { // return all the nodes in layer i to the main node pool LayerNodeType * nodePtr= 0; LayerPointerType layerPtr= m_Layers[i]; while (! layerPtr->Empty()) { nodePtr= layerPtr->Front(); layerPtr->PopFront(); m_LayerNodeStore->Return (nodePtr); } } } if (m_LayerNodeStore) { m_LayerNodeStore->Clear(); m_Layers.clear(); } if (m_Data != 0) { // Deallocate the thread local data structures. for (unsigned int ThreadId= 0; ThreadId < m_NumOfThreads; ThreadId++) { // Remove semaphores from the system. m_Data[ThreadId].m_Semaphore[0]->Remove(); m_Data[ThreadId].m_Semaphore[1]->Remove(); delete [] m_Data[ThreadId].m_ZHistogram; if (m_Data[ThreadId].globalData != 0) { this->GetDifferenceFunction()->ReleaseGlobalDataPointer (m_Data[ThreadId].globalData); m_Data[ThreadId].globalData= 0; } // 1. delete nodes on the thread layers for (i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; i++) { // return all the nodes in layer i to thread-i's node pool LayerNodeType * nodePtr; LayerPointerType layerPtr= m_Data[ThreadId].m_Layers[i]; while (! layerPtr->Empty()) { nodePtr= layerPtr->Front(); layerPtr->PopFront(); m_Data[ThreadId].m_LayerNodeStore->Return(nodePtr); } } m_Data[ThreadId].m_Layers.clear(); // 2. cleanup the LoadTransferBufferLayers: empty all and return the nodes // to the pool for (i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; i++) { for (j= 0; j < m_NumOfThreads; j++) { if (j == ThreadId) { // a thread does NOT pass nodes to istelf continue; } LayerNodeType * nodePtr; LayerPointerType layerPtr= m_Data[ThreadId].m_LoadTransferBufferLayers[i][j]; while (! layerPtr->Empty()) { nodePtr= layerPtr->Front(); layerPtr->PopFront(); m_Data[ThreadId].m_LayerNodeStore->Return (nodePtr); } } m_Data[ThreadId].m_LoadTransferBufferLayers[i].clear(); } delete [] m_Data[ThreadId].m_LoadTransferBufferLayers; // 3. clear up the nodes in the last layer of m_InterNeighborNodeTransferBufferLayers (if any) for (i= 0; i < m_NumOfThreads; i++) { LayerNodeType* nodePtr; for (unsigned int InOrOut= 0; InOrOut < 2; InOrOut++) { LayerPointerType layerPtr = m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[InOrOut][m_NumberOfLayers][i]; while (! layerPtr->Empty()) { nodePtr= layerPtr->Front(); layerPtr->PopFront(); m_Data[ThreadId].m_LayerNodeStore->Return(nodePtr); } } } // check if all last layers are empty and then delete them for (i = 0; i < static_cast(m_NumberOfLayers) + 1; i++) { delete [] m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[0][i]; delete [] m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[1][i]; } delete [] m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[0]; delete [] m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[1]; // 4. check if all the uplists and downlists are empty // 5. delete all nodes in the node pool m_Data[ThreadId].m_LayerNodeStore->Clear(); } delete [] m_Data; } // if m_data != 0 m_Data= 0; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedInitializeIteration (unsigned int itkNotUsed(ThreadId)) { // If child classes need an entry point to the start of every iteration step // they can override this method. return; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::Iterate() { // Set up for multithreaded processing ParallelSparseFieldLevelSetThreadStruct str; str.Filter = this; str.TimeStep = NumericTraits::Zero; this->GetMultiThreader()->SetNumberOfThreads (m_NumOfThreads); // Initialize the list of time step values that will be generated by the // various threads. There is one distinct slot for each possible thread, // so this data structure is thread-safe. str.TimeStepList = new TimeStepType[m_NumOfThreads]; str.ValidTimeStepList = new bool [m_NumOfThreads]; for (unsigned int i =0; i < m_NumOfThreads; ++i) { str.ValidTimeStepList[i] = true; } // Multithread the execution this->GetMultiThreader()->SetSingleMethod(this->IterateThreaderCallback, &str); // It is this method that will results in the creation of the threads this->GetMultiThreader()->SingleMethodExecute (); delete [] str.TimeStepList; delete [] str.ValidTimeStepList; } template ITK_THREAD_RETURN_TYPE ParallelSparseFieldLevelSetImageFilterBugFix ::IterateThreaderCallback(void * arg) { // Controls how often we check for balance of the load among the threads and perform // load balancing (if needed) by redistributing the load. const unsigned int LOAD_BALANCE_ITERATION_FREQUENCY = 30; unsigned int i; unsigned int ThreadId = ((MultiThreader::ThreadInfoStruct *)(arg))->ThreadID; ParallelSparseFieldLevelSetThreadStruct * str = (ParallelSparseFieldLevelSetThreadStruct *) (((MultiThreader::ThreadInfoStruct *)(arg))->UserData); #ifdef ITK_USE_SPROC // Every thread should 'usadd' itself to the arena as the very first thing so // as to detect errors (if any) early. if (str->Filter->GetState() == Superclass::UNINITIALIZED) { if (MultiThreader::GetThreadArena() != 0) { int code= usadd (MultiThreader::GetThreadArena()); if (code != 0) { OStringStream message; message << "Thread failed to join SGI arena: error"; throw ::itk::ExceptionObject(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION); } } } #endif // allocate thread data: every thread allocates its own data // We do NOT assume here that malloc is thread safe: hence make threads // allocate data serially if (str->Filter->GetState() == Superclass::UNINITIALIZED) { if (ThreadId == 0) { str->Filter->ComputeInitialThreadBoundaries (); // Create the temporary status image str->Filter->m_StatusImageTemp = StatusImageType::New(); str->Filter->m_StatusImageTemp->SetRegions(str->Filter->m_OutputImage->GetRequestedRegion()); str->Filter->m_StatusImageTemp->Allocate(); // Create the temporary output image str->Filter->m_OutputImageTemp = OutputImageType::New(); str->Filter->m_OutputImageTemp->SetRegions(str->Filter->m_OutputImage->GetRequestedRegion()); str->Filter->m_OutputImageTemp->Allocate(); } str->Filter->WaitForAll(); // Data allocation performed serially. for (i= 0; i < str->Filter->m_NumOfThreads; i++) { if (ThreadId == i) { str->Filter->ThreadedAllocateData (ThreadId); } str->Filter->WaitForAll(); } // Data initialization performed in parallel. // Make use of the SGI default first-touch memory placement policy str->Filter->GetThreadRegionSplitByBoundary(ThreadId, str->Filter->m_Data[ThreadId].ThreadRegion); str->Filter->ThreadedInitializeData(ThreadId, str->Filter->m_Data[ThreadId].ThreadRegion); str->Filter->WaitForAll(); if (ThreadId == 0) { str->Filter->m_StatusImage = 0; str->Filter->m_StatusImage = str->Filter->m_StatusImageTemp; str->Filter->m_StatusImageTemp = 0; str->Filter->m_OutputImage = 0; str->Filter->m_OutputImage = str->Filter->m_OutputImageTemp; str->Filter->m_OutputImageTemp = 0; // str->Filter->GraftOutput(str->Filter->m_OutputImage); } str->Filter->WaitForAll(); str->Filter->SetStateToInitialized(); } unsigned int iter = str->Filter->GetElapsedIterations(); while (! (str->Filter->ThreadedHalt(arg)) ) { str->Filter->ThreadedInitializeIteration(ThreadId); // Threaded Calculate Change str->Filter->m_Data[ThreadId].TimeStep = str->Filter->ThreadedCalculateChange(ThreadId); str->Filter->WaitForAll(); // Handle AbortGenerateData() if (str->Filter->m_NumOfThreads == 1 || ThreadId == 0) { if( str->Filter->GetAbortGenerateData() ) { str->Filter->InvokeEvent( IterationEvent() ); str->Filter->ResetPipeline(); ProcessAborted e(__FILE__,__LINE__); e.SetDescription("Process aborted."); e.SetLocation(ITK_LOCATION); throw e; } } // Calculate the timestep (no need to do this when there is just 1 thread) if (str->Filter->m_NumOfThreads == 1) { if (iter != 0) { // Update the RMS difference here str->Filter->SetRMSChange( static_cast(str->Filter->m_Data[0].m_RMSChange)); unsigned int count = str->Filter->m_Data[0].m_Count; if (count != 0) { str->Filter->SetRMSChange( static_cast(vcl_sqrt( (static_cast (str->Filter->GetRMSChange())) / count))); } } // this is done by the thread0 str->Filter->InvokeEvent( IterationEvent() ); str->Filter->InvokeEvent( ProgressEvent () ); str->Filter->SetElapsedIterations(++iter); str->TimeStep = str->Filter->m_Data[0].TimeStep; // (works for the 1-thread // case else redefined below) } else { if (ThreadId == 0) { if (iter != 0) { // Update the RMS difference here unsigned int count = 0; str->Filter->SetRMSChange(static_cast( m_ValueZero )); for (i = 0; i < str->Filter->m_NumOfThreads; i++) { str->Filter->SetRMSChange(str->Filter->GetRMSChange() + str->Filter->m_Data[i].m_RMSChange); count += str->Filter->m_Data[i].m_Count; } if (count != 0) { str->Filter->SetRMSChange( static_cast( vcl_sqrt((static_cast (str->Filter->m_RMSChange)) / count) )); } } // Should we stop iterating ? (in case there are too few pixels to // process for every thread) str->Filter->m_Stop= true; for (i= 0; i < str->Filter->m_NumOfThreads; i++) { if (str->Filter->m_Data[i].m_Layers[0]->Size() > 10) { str->Filter->m_Stop= false; break; } } str->Filter->InvokeEvent ( IterationEvent() ); str->Filter->InvokeEvent ( ProgressEvent () ); str->Filter->SetElapsedIterations (++iter); } if (ThreadId == 0) { for (i= 0; i < str->Filter->m_NumOfThreads; i++) { str->TimeStepList[i]= str->Filter->m_Data[i].TimeStep; } str->TimeStep = str->Filter->ResolveTimeStep(str->TimeStepList, str->ValidTimeStepList, str->Filter->m_NumOfThreads); } } str->Filter->WaitForAll(); // The active layer is too small => stop iterating if (str->Filter->m_Stop == true) { return ITK_THREAD_RETURN_VALUE; } // Threaded Apply Update str->Filter->ThreadedApplyUpdate(str->TimeStep, ThreadId); // We only need to wait for neighbors because ThreadedCalculateChange // requires information only from the neighbors. str->Filter->SignalNeighborsAndWait(ThreadId); if (str->Filter->GetElapsedIterations() % LOAD_BALANCE_ITERATION_FREQUENCY == 0) { str->Filter->WaitForAll(); // change boundaries if needed if (ThreadId == 0) { str->Filter->CheckLoadBalance(); } str->Filter->WaitForAll(); if (str->Filter->m_BoundaryChanged == true) { str->Filter->ThreadedLoadBalance (ThreadId); str->Filter->WaitForAll(); } } } // post-process output str->Filter->GetThreadRegionSplitUniformly(ThreadId, str->Filter->m_Data[ThreadId].ThreadRegion); str->Filter->ThreadedPostProcessOutput( str->Filter->m_Data[ThreadId].ThreadRegion); return ITK_THREAD_RETURN_VALUE; } template typename ParallelSparseFieldLevelSetImageFilterBugFix::TimeStepType ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedCalculateChange(unsigned int ThreadId) { typename FiniteDifferenceFunctionType::Pointer df = this->GetDifferenceFunction(); typename FiniteDifferenceFunctionType::FloatOffsetType offset; ValueType norm_grad_phi_squared, dx_forward, dx_backward; ValueType centerValue, forwardValue, backwardValue; ValueType MIN_NORM = 1.0e-6; if (this->GetUseImageSpacing()) { double minSpacing = NumericTraits::max(); for (unsigned int i=0; iGetInput()->GetSpacing()[i]); } MIN_NORM *= minSpacing; } ConstNeighborhoodIterator outputIt (df->GetRadius(), m_OutputImage, m_OutputImage->GetRequestedRegion()); if ( m_BoundsCheckingActive == false ) { outputIt.NeedToUseBoundaryConditionOff(); } unsigned int i, center = outputIt.Size() /2; const NeighborhoodScalesType neighborhoodScales = this->GetDifferenceFunction()->ComputeNeighborhoodScales(); // Calculates the update values for the active layer indicies in this // iteration. Iterates through the active layer index list, applying // the level set function to the output image (level set image) at each // index. typename LayerType::Iterator layerIt = m_Data[ThreadId].m_Layers[0]->Begin(); typename LayerType::Iterator layerEnd = m_Data[ThreadId].m_Layers[0]->End(); for ( ; layerIt != layerEnd; ++layerIt) { outputIt.SetLocation(layerIt->m_Index); // Calculate the offset to the surface from the center of this // neighborhood. This is used by some level set functions in sampling a // speed, advection, or curvature term. if (this->m_InterpolateSurfaceLocation && (centerValue=outputIt.GetCenterPixel()) != NumericTraits::Zero) { // Surface is at the zero crossing, so distance to surface is: // phi(x) / norm(grad(phi)), where phi(x) is the center of the // neighborhood. The location is therefore // (i,j,k) - ( phi(x) * grad(phi(x)) ) / norm(grad(phi))^2 norm_grad_phi_squared = 0.0; for (i = 0; i < static_cast(ImageDimension); ++i) { forwardValue = outputIt.GetPixel(center + m_NeighborList.GetStride(i)); backwardValue= outputIt.GetPixel(center - m_NeighborList.GetStride(i)); if (forwardValue * backwardValue >= 0) { // 1. both neighbors have the same sign OR at least one of them is ZERO dx_forward = forwardValue - centerValue; dx_backward = centerValue - backwardValue; // take the one-sided derivative with the larger magnitude if (vnl_math_abs(dx_forward) > vnl_math_abs(dx_backward)) { offset[i]= dx_forward; } else { offset[i]= dx_backward; } } else { // 2. neighbors have opposite sign // take the one-sided derivative using the neighbor that has the opposite sign w.r.t. oneself if (centerValue * forwardValue < 0) { offset[i]= forwardValue - centerValue; } else { offset[i]= centerValue - backwardValue; } } norm_grad_phi_squared += offset[i] * offset[i]; } for (i = 0; i < static_cast(ImageDimension); ++i) { offset[i] = ( offset[i] * outputIt.GetCenterPixel() ) / (norm_grad_phi_squared + MIN_NORM); } layerIt->m_Value = df->ComputeUpdate (outputIt, (void *) m_Data[ThreadId].globalData, offset); } else // Don't do interpolation { layerIt->m_Value = df->ComputeUpdate (outputIt, (void *) m_Data[ThreadId].globalData); } } TimeStepType timeStep= df->ComputeGlobalTimeStep ((void*) m_Data[ThreadId].globalData); return timeStep; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedApplyUpdate (TimeStepType dt, unsigned int ThreadId) { this->ThreadedUpdateActiveLayerValues(dt, m_Data[ThreadId].UpList[0], m_Data[ThreadId].DownList[0], ThreadId); // We need to update histogram information (because some pixels are LEAVING // layer-0 (the active layer) this->SignalNeighborsAndWait(ThreadId); // Process status lists and update value for first inside/outside layers this->ThreadedProcessStatusList( 0, 1, 2, 1, 1, 0, ThreadId); this->ThreadedProcessStatusList( 0, 1, 1, 2, 0, 0, ThreadId); this->SignalNeighborsAndWait(ThreadId); // Update first layer value, process first layer this->ThreadedProcessFirstLayerStatusLists( 1, 0, 3, 1, 1, ThreadId); this->ThreadedProcessFirstLayerStatusLists( 1, 0, 4, 0, 1, ThreadId); // We need to update histogram information (because some pixels are ENTERING // layer-0 this->SignalNeighborsAndWait(ThreadId); StatusType up_to= 1, up_search= 5; StatusType down_to= 2, down_search= 6; unsigned char j= 0, k= 1; // The 3D case: this loop is executed at least once while ( down_search < 2 * m_NumberOfLayers + 1 ) { this->ThreadedProcessStatusList(j, k, up_to, up_search, 1, (up_search - 1) / 2, ThreadId); this->ThreadedProcessStatusList(j, k, down_to, down_search, 0, (up_search - 1) / 2, ThreadId); this->SignalNeighborsAndWait(ThreadId); up_to += 2; down_to += 2; up_search += 2; down_search += 2; // Swap the lists so we can re-use the empty one. j= k; k= 1 - j; } // now up_search = 2 * m_NumberOfLayers + 1 (= 7 if m_NumberOfLayers = 3) // now down_search = 2 * m_NumberOfLayers + 2 (= 8 if m_NumberOfLayers = 3) // Process the outermost inside/outside layers in the sparse field this->ThreadedProcessStatusList(j, k, up_to, m_StatusNull, 1, (up_search - 1) / 2, ThreadId); this->ThreadedProcessStatusList(j, k, down_to, m_StatusNull, 0, (up_search - 1) / 2, ThreadId); this->SignalNeighborsAndWait(ThreadId); this->ThreadedProcessOutsideList(k,(2 * m_NumberOfLayers + 1) - 2, 1, (up_search + 1) / 2, ThreadId); this->ThreadedProcessOutsideList(k,(2 * m_NumberOfLayers + 1) - 1, 0, (up_search + 1) / 2, ThreadId); if (m_OutputImage->GetImageDimension() < 3) { this->SignalNeighborsAndWait(ThreadId); } // A synchronize is NOT required here because in 3D case we have at least 7 // layers, thus ThreadedProcessOutsideList() works on layers 5 & 6 while // ThreadedPropagateLayerValues() works on 0, 1, 2, 3, 4 only. => There can // NOT be any dependencies amoing different threads. // Finally, we update all of the layer VALUES (excluding the active layer, // which has already been updated) this->ThreadedPropagateLayerValues(0, 1, 3, 1, ThreadId); // first inside this->ThreadedPropagateLayerValues(0, 2, 4, 0, ThreadId); // first outside this->SignalNeighborsAndWait (ThreadId); // Update the rest of the layer values unsigned int i; for (i = 1; i < (2 * static_cast(m_NumberOfLayers) + 1) - 2; i += 2) { j = i+1; this->ThreadedPropagateLayerValues(i, i+2, i+4, 1, ThreadId); this->ThreadedPropagateLayerValues(j, j+2, j+4, 0, ThreadId); this->SignalNeighborsAndWait (ThreadId); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedUpdateActiveLayerValues (TimeStepType dt, LayerType * UpList, LayerType * DownList, unsigned int ThreadId) { // This method scales the update buffer values by the time step and adds // them to the active layer pixels. New values at an index which fall // outside of the active layer range trigger that index to be placed on the // "up" or "down" status list. The neighbors of any such index are then // assigned new values if they are determined to be part of the active list // for the next iteration (i.e. their values will be raised or lowered into // the active range). ValueType LOWER_ACTIVE_THRESHOLD = - (m_ConstantGradientValue / 2.0); ValueType UPPER_ACTIVE_THRESHOLD = m_ConstantGradientValue / 2.0 ; LayerNodeType *release_node; bool flag; IndexType centerIndex; PixelType centerValue; unsigned long int counter =0; float new_value; float rms_change_accumulator = m_ValueZero; unsigned int Neighbor_Size = m_NeighborList.GetSize(); typename LayerType::Iterator layerIt = m_Data[ThreadId].m_Layers[0]->Begin(); typename LayerType::Iterator layerEnd = m_Data[ThreadId].m_Layers[0]->End(); while (layerIt != layerEnd ) { centerIndex = layerIt->m_Index; centerValue = m_OutputImage->GetPixel(centerIndex); new_value = this->ThreadedCalculateUpdateValue(ThreadId, centerIndex, dt, centerValue, layerIt->m_Value); // If this index needs to be moved to another layer, then search its // neighborhood for indicies that need to be pulled up/down into the // active layer. Set those new active layer values appropriately, // checking first to make sure they have not been set by a more // influential neighbor. // ...But first make sure any neighbors in the active layer are not // moving to a layer in the opposite direction. This step is necessary // to avoid the creation of holes in the active layer. The fix is simply // to not change this value and leave the index in the active set. if (new_value > UPPER_ACTIVE_THRESHOLD) { // This index will move UP into a positive (outside) layer // First check for active layer neighbors moving in the opposite // direction flag = false; for (unsigned int i = 0; i < Neighbor_Size; ++i) { if (m_StatusImage->GetPixel(centerIndex + m_NeighborList.GetNeighborhoodOffset(i)) == m_StatusActiveChangingDown) { flag = true; break; } } if (flag == true) { ++layerIt; continue; } rms_change_accumulator += vnl_math_sqr(static_cast(new_value - centerValue )); // update the value of the pixel m_OutputImage->SetPixel (centerIndex, new_value); // Now remove this index from the active list. release_node = layerIt.GetPointer(); ++layerIt; m_Data[ThreadId].m_Layers[0]->Unlink(release_node); m_Data[ThreadId].m_ZHistogram[ release_node->m_Index[m_SplitAxis] ] = m_Data[ThreadId].m_ZHistogram[ release_node->m_Index[m_SplitAxis] ] - 1; // add the release_node to status up list UpList->PushFront(release_node); // m_StatusImage->SetPixel(centerIndex, m_StatusActiveChangingUp); } else if (new_value < LOWER_ACTIVE_THRESHOLD) { // This index will move DOWN into a negative (inside) layer. // First check for active layer neighbors moving in the opposite direction flag = false; for (unsigned int i = 0; i < Neighbor_Size; ++i) { if (m_StatusImage->GetPixel(centerIndex+m_NeighborList.GetNeighborhoodOffset(i)) == m_StatusActiveChangingUp) { flag = true; break; } } if (flag == true) { ++layerIt; continue; } rms_change_accumulator += vnl_math_sqr(static_cast(new_value - centerValue)); // update the value of the pixel m_OutputImage->SetPixel(centerIndex, new_value ); // Now remove this index from the active list. release_node = layerIt.GetPointer(); ++layerIt; m_Data[ThreadId].m_Layers[0]->Unlink(release_node); m_Data[ThreadId].m_ZHistogram[ release_node->m_Index[m_SplitAxis] ] = m_Data[ThreadId].m_ZHistogram[ release_node->m_Index[m_SplitAxis] ] - 1; // now add release_node to status down list DownList->PushFront(release_node); m_StatusImage->SetPixel(centerIndex, m_StatusActiveChangingDown); } else { rms_change_accumulator += vnl_math_sqr(static_cast(new_value - centerValue)); // update the value of the pixel m_OutputImage->SetPixel(centerIndex, new_value ); ++layerIt; } ++counter; } // Determine the average change during this iteration if (counter == 0) { m_Data[ThreadId].m_RMSChange = m_ValueZero; } else { m_Data[ThreadId].m_RMSChange = rms_change_accumulator; } m_Data[ThreadId].m_Count = counter; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::CopyInsertList (unsigned int ThreadId, LayerPointerType FromListPtr, LayerPointerType ToListPtr) { typename LayerType::Iterator layerIt = FromListPtr->Begin(); typename LayerType::Iterator layerEnd= FromListPtr->End(); LayerNodeType * nodePtr; LayerNodeType * nodeTempPtr; while (layerIt != layerEnd) { // copy the node nodePtr= layerIt.GetPointer(); ++layerIt; nodeTempPtr= m_Data[ThreadId].m_LayerNodeStore->Borrow(); nodeTempPtr->m_Index= nodePtr->m_Index; // insert ToListPtr->PushFront (nodeTempPtr); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ClearList (unsigned int ThreadId, LayerPointerType ListPtr) { LayerNodeType * nodePtr; while (! ListPtr->Empty()) { nodePtr= ListPtr->Front(); // remove node from layer ListPtr->PopFront(); // return node to node-pool m_Data[ThreadId].m_LayerNodeStore->Return (nodePtr); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::CopyInsertInterNeighborNodeTransferBufferLayers (unsigned int ThreadId, LayerPointerType List, unsigned int InOrOut, unsigned int BufferLayerNumber) { if (ThreadId != 0) { CopyInsertList(ThreadId, m_Data[this->GetThreadNumber(m_Boundary[ThreadId-1])].m_InterNeighborNodeTransferBufferLayers[InOrOut][BufferLayerNumber][ThreadId], List); } if (m_Boundary[ThreadId] != m_ZSize - 1) { CopyInsertList(ThreadId, m_Data[this->GetThreadNumber (m_Boundary[ThreadId] + 1)].m_InterNeighborNodeTransferBufferLayers[InOrOut][BufferLayerNumber][ThreadId], List); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ClearInterNeighborNodeTransferBufferLayers (unsigned int ThreadId, unsigned int InOrOut, unsigned int BufferLayerNumber) { for (unsigned int i= 0; i < m_NumOfThreads; i++) { ClearList(ThreadId, m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[InOrOut][BufferLayerNumber][i]); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedProcessFirstLayerStatusLists (unsigned int InputLayerNumber, unsigned int OutputLayerNumber, StatusType SearchForStatus, unsigned int InOrOut, unsigned int BufferLayerNumber, unsigned int ThreadId) { LayerNodeType* nodePtr; StatusType from, neighbor_status; ValueType value, value_temp, delta; bool found_neighbor_flag; IndexType center_index, n_index; unsigned int neighbor_Size = m_NeighborList.GetSize(); LayerPointerType InputList, OutputList; //InOrOut == 1, inside, more negative, uplist //InOrOut == 0, outside if (InOrOut == 1) { delta = - m_ConstantGradientValue; from = 2; InputList = m_Data[ThreadId].UpList[InputLayerNumber]; OutputList = m_Data[ThreadId].UpList[OutputLayerNumber]; } else { delta = m_ConstantGradientValue; from = 1; InputList = m_Data[ThreadId].DownList[InputLayerNumber]; OutputList = m_Data[ThreadId].DownList[OutputLayerNumber]; } // 1. nothing to clear // 2. make a copy of the node on the // m_InterNeighborNodeTransferBufferLayers[InOrOut][BufferLayerNumber - 1][i] // for all neighbors i ... and insert it in one's own InputList CopyInsertInterNeighborNodeTransferBufferLayers(ThreadId, InputList, InOrOut, BufferLayerNumber - 1); typename LayerType::Iterator layerIt = InputList->Begin(); typename LayerType::Iterator layerEnd = InputList->End(); while (layerIt != layerEnd) { nodePtr = layerIt.GetPointer(); ++layerIt; center_index = nodePtr->m_Index; // remove node from InputList InputList->Unlink(nodePtr); // check if this is not a duplicate pixel in the InputList // In the case when the thread boundaries differ by just 1 pixel some // nodes may get added twice in the InputLists Even if the boundaries are // more than 1 pixel wide the *_shape_* of the layer may allow this to // happen. Solution: If a pixel comes multiple times than we would find // that the Status image would already be reflecting the new status after // the pixel was encountered the first time if (m_StatusImage->GetPixel(center_index) == 0) { // duplicate node => return it to the node pool m_Data[ThreadId].m_LayerNodeStore->Return (nodePtr); continue; } // set status to zero m_StatusImage->SetPixel(center_index, 0); // add node to the layer-0 m_Data[ThreadId].m_Layers[0]->PushFront(nodePtr); m_Data[ThreadId].m_ZHistogram[ nodePtr->m_Index[m_SplitAxis] ] = m_Data[ThreadId].m_ZHistogram[ nodePtr->m_Index[m_SplitAxis] ] + 1; value = m_OutputImage->GetPixel(center_index); found_neighbor_flag = false; for (unsigned int i = 0; i < neighbor_Size; ++i) { n_index = center_index + m_NeighborList.GetNeighborhoodOffset(i); neighbor_status = m_StatusImage->GetPixel(n_index); // Have we bumped up against the boundary? If so, turn on bounds checking. if ( neighbor_status == m_StatusBoundaryPixel ) { m_BoundsCheckingActive = true; } if (neighbor_status == from) { value_temp = m_OutputImage->GetPixel(n_index); if (found_neighbor_flag == false) { value = value_temp; } else { if (vnl_math_abs(value_temp+delta) < vnl_math_abs(value+delta)) { // take the value closest to zero value= value_temp; } } found_neighbor_flag = true; } if (neighbor_status == SearchForStatus) { // mark this pixel so we MAY NOT add it twice // This STILL DOES NOT GUARANTEE RACE CONDITIONS BETWEEN THREADS. This // is handled at the next stage m_StatusImage->SetPixel(n_index, m_StatusChanging); unsigned int tmpId = this->GetThreadNumber(n_index[m_SplitAxis]); nodePtr = m_Data[ThreadId].m_LayerNodeStore->Borrow(); nodePtr->m_Index = n_index; if (tmpId != ThreadId) { m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[InOrOut][BufferLayerNumber][tmpId]->PushFront(nodePtr); } else { OutputList->PushFront(nodePtr); } } } m_OutputImage->SetPixel(center_index, value + delta ); // This function can be overridden in the derived classes to process pixels entering the active layer. this->ThreadedProcessPixelEnteringActiveLayer (center_index, value + delta, ThreadId); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedProcessPixelEnteringActiveLayer (const IndexType itkNotUsed(index), const ValueType itkNotUsed(value), const unsigned int itkNotUsed(ThreadId)) { // This function can be overridden in the derived classes to process pixels entering the active layer. return; } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedProcessStatusList (unsigned int InputLayerNumber, unsigned int OutputLayerNumber, StatusType ChangeToStatus, StatusType SearchForStatus, unsigned int InOrOut, unsigned int BufferLayerNumber, unsigned int ThreadId) { unsigned int i; LayerNodeType* nodePtr; StatusType neighbor_status; IndexType center_index, n_index; LayerPointerType InputList, OutputList; // Push each index in the input list into its appropriate status layer // (ChangeToStatus) and update the status image value at that index. // Also examine the neighbors of the index to determine which need to go onto // the output list (search for SearchForStatus). if (InOrOut == 1) { InputList = m_Data[ThreadId].UpList[InputLayerNumber]; OutputList = m_Data[ThreadId].UpList[OutputLayerNumber]; } else { InputList = m_Data[ThreadId].DownList[InputLayerNumber]; OutputList = m_Data[ThreadId].DownList[OutputLayerNumber]; } // 1. clear one's own // m_InterNeighborNodeTransferBufferLayers[InOrOut][BufferLayerNumber - 2][i] // for all threads i. if (BufferLayerNumber >= 2) { ClearInterNeighborNodeTransferBufferLayers(ThreadId, InOrOut, BufferLayerNumber - 2); } // SPECIAL CASE: clear one's own // m_InterNeighborNodeTransferBufferLayers[InOrOut][m_NumberOfLayers][i] for // all threads i if (BufferLayerNumber == 0) { ClearInterNeighborNodeTransferBufferLayers(ThreadId, InOrOut, m_NumberOfLayers); } // obtain the pixels (from last iteration) that were given to you from other // (neighboring) threads 2. make a copy of the node on the // m_InterNeighborNodeTransferBufferLayers[InOrOut][LastLayer - 1][i] for all // thread neighbors i ... ... and insert it in one's own InputList if (BufferLayerNumber > 0) { CopyInsertInterNeighborNodeTransferBufferLayers(ThreadId, InputList, InOrOut, BufferLayerNumber - 1); } unsigned int neighbor_size = m_NeighborList.GetSize(); while ( ! InputList->Empty() ) { nodePtr = InputList->Front(); center_index = nodePtr->m_Index; InputList->PopFront(); // Check if this is not a duplicate pixel in the InputList. // Solution: If a pixel comes multiple times than we would find that the // Status image would already be reflecting // the new status after the pixel was encountered the first time if ((BufferLayerNumber != 0) && (m_StatusImage->GetPixel(center_index) == ChangeToStatus)) { // duplicate node => return it to the node pool m_Data[ThreadId].m_LayerNodeStore->Return (nodePtr); continue; } // add to layer m_Data[ThreadId].m_Layers[ChangeToStatus]->PushFront (nodePtr); // change the status m_StatusImage->SetPixel(center_index, ChangeToStatus); for (i = 0; i < neighbor_size; ++i) { n_index = center_index + m_NeighborList.GetNeighborhoodOffset(i); neighbor_status = m_StatusImage->GetPixel(n_index); // Have we bumped up against the boundary? If so, turn on bounds checking. if ( neighbor_status == m_StatusBoundaryPixel ) { m_BoundsCheckingActive = true; } if (neighbor_status == SearchForStatus) { // mark this pixel so we MAY NOT add it twice // This STILL DOES NOT AVOID RACE CONDITIONS BETWEEN THREADS (This is // handled at the next stage) m_StatusImage->SetPixel(n_index, m_StatusChanging); unsigned int tmpId = this->GetThreadNumber (n_index[m_SplitAxis]); nodePtr = m_Data[ThreadId].m_LayerNodeStore->Borrow(); nodePtr->m_Index = n_index; if (tmpId != ThreadId) { m_Data[ThreadId].m_InterNeighborNodeTransferBufferLayers[InOrOut][BufferLayerNumber][tmpId]->PushFront(nodePtr); } else { OutputList->PushFront(nodePtr); } } } } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedProcessOutsideList (unsigned int InputLayerNumber, StatusType ChangeToStatus, unsigned int InOrOut, unsigned int BufferLayerNumber, unsigned int ThreadId) { LayerPointerType OutsideList; if (InOrOut == 1) { OutsideList= m_Data[ThreadId].UpList [InputLayerNumber]; } else { OutsideList= m_Data[ThreadId].DownList[InputLayerNumber]; } // obtain the pixels (from last iteration of ThreadedProcessStatusList() ) // that were given to you from other (neighboring) threads // 1. clear one's own // m_InterNeighborNodeTransferBufferLayers[InOrOut][BufferLayerNumber - 2][i] // for all threads i. ClearInterNeighborNodeTransferBufferLayers(ThreadId, InOrOut, BufferLayerNumber - 2); // 2. make a copy of the node on the // m_InterNeighborNodeTransferBufferLayers[InOrOut][LastLayer - 1][i] for // all thread neighbors i ... ... and insert it in one's own InoutList CopyInsertInterNeighborNodeTransferBufferLayers(ThreadId, OutsideList, InOrOut, BufferLayerNumber - 1); // Push each index in the input list into its appropriate status layer // (ChangeToStatus) and ... ... update the status image value at that index LayerNodeType* nodePtr; while ( ! OutsideList->Empty() ) { nodePtr = OutsideList->Front(); OutsideList->PopFront(); m_StatusImage->SetPixel(nodePtr->m_Index, ChangeToStatus); m_Data[ThreadId].m_Layers[ChangeToStatus]->PushFront (nodePtr); } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedPropagateLayerValues(StatusType from, StatusType to, StatusType promote, unsigned int InOrOut, unsigned int ThreadId) { ValueType value, value_temp, delta; bool found_neighbor_flag; typename LayerType::Iterator toIt; typename LayerType::Iterator toEnd; LayerNodeType* nodePtr; StatusType past_end = static_cast( m_Layers.size() ) - 1; // Are we propagating values inward (more negative) or outward (more positive)? if (InOrOut == 1) { delta = - m_ConstantGradientValue; } else { delta = m_ConstantGradientValue; } unsigned int Neighbor_Size = m_NeighborList.GetSize(); toIt = m_Data[ThreadId].m_Layers[to]->Begin(); toEnd = m_Data[ThreadId].m_Layers[to]->End(); IndexType centerIndex, nIndex; StatusType centerStatus, nStatus; while ( toIt != toEnd ) { centerIndex = toIt->m_Index; centerStatus = m_StatusImage->GetPixel(centerIndex); if (centerStatus != to) { // delete nodes NOT deleted earlier nodePtr = toIt.GetPointer(); ++toIt; // remove the node from the layer m_Data[ThreadId].m_Layers[to]->Unlink( nodePtr ); m_Data[ThreadId].m_LayerNodeStore->Return( nodePtr ); continue; } value = m_ValueZero; found_neighbor_flag = false; for (unsigned int i = 0; i < Neighbor_Size ; ++i) { nIndex = centerIndex + m_NeighborList.GetNeighborhoodOffset(i); nStatus = m_StatusImage->GetPixel(nIndex); // If this neighbor is in the "from" list, compare its absolute value // to any previous values found in the "from" list. Keep only the // value with the smallest magnitude. if (nStatus == from) { value_temp = m_OutputImage->GetPixel(nIndex); if (found_neighbor_flag == false) { value = value_temp; } else { if (vnl_math_abs(value_temp+delta) < vnl_math_abs(value+delta)) { // take the value closest to zero value= value_temp; } } found_neighbor_flag = true; } } if (found_neighbor_flag == true) { // Set the new value using the smallest magnitude found in our "from" // neighbors m_OutputImage->SetPixel (centerIndex, value + delta); ++toIt; } else { // Did not find any neighbors on the "from" list, then promote this // node. A "promote" value past the end of my sparse field size // means delete the node instead. Change the status value in the // status image accordingly. nodePtr = toIt.GetPointer(); ++toIt; m_Data[ThreadId].m_Layers[to]->Unlink( nodePtr ); if ( promote > past_end ) { m_Data[ThreadId].m_LayerNodeStore->Return( nodePtr ); m_StatusImage->SetPixel(centerIndex, m_StatusNull); } else { m_Data[ThreadId].m_Layers[promote]->PushFront( nodePtr ); m_StatusImage->SetPixel(centerIndex, promote); } } } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::CheckLoadBalance() { unsigned int i, j; // This parameter defines a degree of unbalancedness of the load among threads. const float MAX_PIXEL_DIFFERENCE_PERCENT = 0.025; m_BoundaryChanged = false; // work load division based on the nodes on the active layer (layer-0) long int min = NumericTraits::max(); long int max = 0; long int total= 0; // the total nodes in the active layer of the surface for (i = 0; i < m_NumOfThreads; i++) { long int count = m_Data[i].m_Layers[0]->Size(); total += count; if (min > count) min = count; if (max < count) max = count; } if (max - min < MAX_PIXEL_DIFFERENCE_PERCENT * total / m_NumOfThreads) { // if the difference between max and min is NOT even x% of the average // nodes in the thread layers then no need to change the boundaries next return; } // Change the boundaries -------------------------- // compute the global histogram from the individual histograms for (i= 0; i < m_NumOfThreads; i++) { for (j= (i == 0 ? 0 : m_Boundary[i-1] + 1); j <= m_Boundary[i]; j++) { m_GlobalZHistogram[j] = m_Data[i].m_ZHistogram[j]; } } // compute the cumulative frequency distribution using the histogram m_ZCumulativeFrequency[0] = m_GlobalZHistogram[0]; for (i= 1; i < m_ZSize; i++) { m_ZCumulativeFrequency[i] = m_ZCumulativeFrequency[i-1] + m_GlobalZHistogram[i]; } // now define the boundaries m_Boundary[m_NumOfThreads - 1] = m_ZSize - 1; // special case: the last bound for (i= 0; i < m_NumOfThreads - 1; i++) { // compute m_Boundary[i] float cutOff= 1.0f * (i+1) * m_ZCumulativeFrequency[m_ZSize-1] / m_NumOfThreads; // find the position in the cumulative freq dist where this cutoff is met for (j= (i == 0 ? 0 : m_Boundary[i-1]); j < m_ZSize; j++) { if (cutOff > m_ZCumulativeFrequency[j]) { continue; } else { // do some optimization ! // go further FORWARD and find the first index (k) in the cumulative // freq distribution s.t. m_ZCumulativeFrequency[k] != // m_ZCumulativeFrequency[j]. This is to be done because if we have a // flat patch in the cum freq dist then ... . we can choose a bound // midway in that flat patch unsigned int k; for (k= 1; j+k < m_ZSize; k++) { if (m_ZCumulativeFrequency[j+k] != m_ZCumulativeFrequency[j]) { break; } } // if ALL new boundaries same as the original then NO NEED TO DO // ThreadedLoadBalance() next !!! unsigned int newBoundary= static_cast ((j + (j+k)) / 2); if (newBoundary != m_Boundary[i]) { // m_BoundaryChanged= true; m_Boundary[i]= newBoundary; } break; } } } if (m_BoundaryChanged == false) { return; } // Reset the individual histograms to reflect the new distrbution // Also reset the mapping from the Z value --> the thread number i.e. m_MapZToThreadNumber[] for (i= 0; i < m_NumOfThreads; i++) { if (i != 0) { for (j= 0; j <= m_Boundary[i-1]; j++) { m_Data[i].m_ZHistogram[j] = 0; } } for (j= (i == 0 ? 0 : m_Boundary[i-1] + 1); j <= m_Boundary[i]; j++) { // this Z histogram value should be given to thread-i m_Data[i].m_ZHistogram[j] = m_GlobalZHistogram[j]; // this Z belongs to the region associated with thread-i m_MapZToThreadNumber[j]= i; } for (j= m_Boundary[i] + 1; j < m_ZSize; j++) { m_Data[i].m_ZHistogram[j] = 0; } } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedLoadBalance(unsigned int ThreadId) { // the situation at this point in time:: // the OPTIMAL boundaries (that divide work equally) have changed but ... // the thread data lags behind the boundaries (it is still following the old // boundaries) the m_ZHistogram[], however, reflects the latest optimal // boundaries // The task: // 1. Every thread checks for pixels with itself that should NOT be with // itself anymore (because of the changed boundaries). // These pixels are now put in extra "buckets" for other threads to grab // 2. WaitForAll (). // 3. Every thread grabs those pixels, from every other thread, that come // within its boundaries (from the extra buckets). //////////////////////////////////////////////////// // 1. unsigned int i, j; // cleanup the layers first for (i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; i++) { for (j= 0; j < m_NumOfThreads; j++) { if (j == ThreadId) { // a thread does NOT pass nodes to istelf continue; } ClearList(ThreadId, m_Data[ThreadId].m_LoadTransferBufferLayers[i][j]); } } LayerNodeType * nodePtr; for (i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; i++) // for all layers { typename LayerType::Iterator layerIt = m_Data[ThreadId].m_Layers[i]->Begin(); typename LayerType::Iterator layerEnd = m_Data[ThreadId].m_Layers[i]->End(); while (layerIt != layerEnd) { nodePtr= layerIt.GetPointer(); ++layerIt; // use the latest (just updated in CheckLoadBalance) boundaries to // determine to which thread region does the pixel now belong unsigned int tmpId = this->GetThreadNumber(nodePtr->m_Index[m_SplitAxis]); if (tmpId != ThreadId) // this pixel no longer belongs to this thread { // remove from the layer m_Data[ThreadId].m_Layers[i]->Unlink(nodePtr); // insert temporarily into the special-layers TO BE LATER taken by the // other thread // NOTE: What is pushed is a node belonging to the LayerNodeStore of // ThreadId. This is deleted later (during the start of the next // SpecialIteration). What is taken by the other thread is NOT this // node BUT a copy of it. m_Data[ThreadId].m_LoadTransferBufferLayers[i][tmpId]->PushFront(nodePtr); } } } //////////////////////////////////////////////////// // 2. this->WaitForAll(); //////////////////////////////////////////////////// // 3. for (i = 0; i < 2 * static_cast(m_NumberOfLayers) + 1; i++) { // check all other threads for (j= 0; j < m_NumOfThreads; j++) { if (j == ThreadId) { continue; // exclude oneself } CopyInsertList(ThreadId, m_Data[j].m_LoadTransferBufferLayers[i][ThreadId], m_Data[ThreadId].m_Layers[i]); } } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::GetThreadRegionSplitByBoundary(unsigned int ThreadId, ThreadRegionType & ThreadRegion) { // Initialize the ThreadRegion to the output's requested region ThreadRegion = m_OutputImage->GetRequestedRegion(); // compute lower bound on the index typename TOutputImage::IndexType threadRegionIndex = ThreadRegion.GetIndex(); if (ThreadId != 0) { if (m_Boundary[ThreadId-1] < m_Boundary[m_NumOfThreads -1]) { threadRegionIndex[m_SplitAxis] += m_Boundary[ThreadId-1] + 1; } else { threadRegionIndex[m_SplitAxis] += m_Boundary[ThreadId-1]; } } ThreadRegion.SetIndex (threadRegionIndex); // compute the size of the region typename TOutputImage::SizeType threadRegionSize = ThreadRegion.GetSize(); threadRegionSize[m_SplitAxis] = (ThreadId == 0 ? (m_Boundary[0] + 1) : m_Boundary[ThreadId] - m_Boundary[ThreadId-1]); ThreadRegion.SetSize(threadRegionSize); } template void ParallelSparseFieldLevelSetImageFilterBugFix ::GetThreadRegionSplitUniformly(unsigned int ThreadId, ThreadRegionType & ThreadRegion) { // Initialize the ThreadRegion to the output's requested region ThreadRegion = m_OutputImage->GetRequestedRegion(); typename TOutputImage::IndexType threadRegionIndex = ThreadRegion.GetIndex(); threadRegionIndex[m_SplitAxis] += static_cast (1.0 * ThreadId * m_ZSize / m_NumOfThreads); ThreadRegion.SetIndex(threadRegionIndex); typename TOutputImage::SizeType threadRegionSize = ThreadRegion.GetSize(); // compute lower bound on the index and the size of the region if (ThreadId < m_NumOfThreads - 1) // this is NOT the last thread { threadRegionSize [m_SplitAxis] = static_cast (1.0 * (ThreadId+1) * m_ZSize / m_NumOfThreads) - static_cast (1.0 * ThreadId * m_ZSize / m_NumOfThreads); } else { threadRegionSize [m_SplitAxis] = m_ZSize - static_cast (1.0 * ThreadId * m_ZSize / m_NumOfThreads); } ThreadRegion.SetSize(threadRegionSize); } template void ParallelSparseFieldLevelSetImageFilterBugFix ::ThreadedPostProcessOutput(const ThreadRegionType & regionToProcess) { // Assign background pixels INSIDE the sparse field layers to a new level set // with value less than the innermost layer. Assign background pixels // OUTSIDE the sparse field layers to a new level set with value greater than // the outermost layer. const ValueType max_layer = static_cast(m_NumberOfLayers); const ValueType outside_value = (max_layer+1) * m_ConstantGradientValue; const ValueType inside_value = -(max_layer+1) * m_ConstantGradientValue; ImageRegionConstIterator statusIt(m_StatusImage, regionToProcess); ImageRegionIterator outputIt(m_OutputImage, regionToProcess); for (outputIt = outputIt.Begin(), statusIt = statusIt.Begin(); ! outputIt.IsAtEnd(); ++outputIt, ++statusIt) { if (statusIt.Get() == m_StatusNull || statusIt.Get() == m_StatusBoundaryPixel) { if (outputIt.Get() > m_ValueZero) { outputIt.Set (outside_value); } else { outputIt.Set (inside_value); } } } } template unsigned int ParallelSparseFieldLevelSetImageFilterBugFix ::GetThreadNumber (unsigned int splitAxisValue) { return ( m_MapZToThreadNumber[splitAxisValue] ); } template void ParallelSparseFieldLevelSetImageFilterBugFix ::SignalNeighborsAndWait (unsigned int ThreadId) { // This is the case when a thread has no pixels to process // This case is analogous to NOT using that thread at all // Hence this thread does not need to signal / wait for any other neighbor // thread during the iteration if (ThreadId != 0) { if (m_Boundary[ThreadId-1] == m_Boundary[ThreadId]) { m_Data[ThreadId].m_SemaphoreArrayNumber= 1 - m_Data[ThreadId].m_SemaphoreArrayNumber; return; } } unsigned int lastThreadId = m_NumOfThreads - 1; if (lastThreadId == 0) { return; // only 1 thread => no need to wait } // signal neighbors that work is done if (ThreadId != 0) // not the first thread { this->SignalNeighbor(m_Data[ThreadId].m_SemaphoreArrayNumber, this->GetThreadNumber ( m_Boundary[ThreadId-1] )); } if (m_Boundary[ThreadId] != m_ZSize - 1) // not the last thread { this->SignalNeighbor (m_Data[ThreadId].m_SemaphoreArrayNumber, this->GetThreadNumber ( m_Boundary[ThreadId] + 1 )); } // wait for signal from neighbors signifying that their work is done if ((ThreadId == 0) || (m_Boundary[ThreadId] == m_ZSize - 1)) { // do it just once for the first and the last threads because they share // just 1 boundary (just 1 neighbor) this->WaitForNeighbor (m_Data[ThreadId].m_SemaphoreArrayNumber, ThreadId); m_Data[ThreadId].m_SemaphoreArrayNumber= 1 - m_Data[ThreadId].m_SemaphoreArrayNumber; } else { // do twice because share 2 boundaries with neighbors this->WaitForNeighbor (m_Data[ThreadId].m_SemaphoreArrayNumber, ThreadId); this->WaitForNeighbor (m_Data[ThreadId].m_SemaphoreArrayNumber, ThreadId); m_Data[ThreadId].m_SemaphoreArrayNumber= 1 - m_Data[ThreadId].m_SemaphoreArrayNumber; } } template void ParallelSparseFieldLevelSetImageFilterBugFix ::SignalNeighbor(unsigned int SemaphoreArrayNumber, unsigned int ThreadId) { m_Data[ThreadId].m_Semaphore[SemaphoreArrayNumber]->Up(); } template void ParallelSparseFieldLevelSetImageFilterBugFix ::WaitForNeighbor(unsigned int SemaphoreArrayNumber, unsigned int ThreadId) { m_Data[ThreadId].m_Semaphore[SemaphoreArrayNumber]->Down(); } template void ParallelSparseFieldLevelSetImageFilterBugFix ::WaitForAll () { m_Barrier->Wait(); } template void ParallelSparseFieldLevelSetImageFilterBugFix ::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); unsigned int i; os << indent << "m_IsoSurfaceValue: " << m_IsoSurfaceValue << std::endl; os << indent << "m_LayerNodeStore: " << m_LayerNodeStore; unsigned int ThreadId; for (ThreadId=0; ThreadId < m_NumOfThreads; ThreadId++) { os << indent << "ThreadId: " << ThreadId << std::endl; if (m_Data != 0) { for (i=0; i < m_Data[ThreadId].m_Layers.size(); i++) { os << indent << "m_Layers[" << i << "]: size=" << m_Data[ThreadId].m_Layers[i]->Size() << std::endl; os << indent << m_Data[ThreadId].m_Layers[i]; } } } } /* template void ParallelSparseFieldLevelSetImageFilterBugFix ::WriteActivePointsToFile() { std::cout << "WriteActivePointsToFile called ..." << std::endl << std::flush; typename LayerType::Iterator layerIt, end; FILE* out; char filename[100]; sprintf (filename, "activeLayerPoints_%d.pts", this->GetElapsedIterations()); out= fopen(filename, "wt"); if(!out) std::cout<<"Can not open "<Begin(); end = m_Data[i].m_Layers[0]->End(); while(layerIt != end) { for(unsigned int j = 0; j < static_cast(ImageDimension); j++) { fprintf (out,"%d ", static_cast (layerIt->m_Index[j])); } fprintf (out, "\n"); ++layerIt; } } fclose(out); } */ } // end namespace itk #endif itksnap-3.4.0/Common/ITKExtras/itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.h000066400000000000000000000101441263013355200316240ustar00rootroot00000000000000#ifndef __itkTopologyPreservingDigitalSurfaceEvolutionImageFilter_h #define __itkTopologyPreservingDigitalSurfaceEvolutionImageFilter_h #include "itkArray.h" #include "itkInPlaceImageFilter.h" #include "itkNeighborhoodIterator.h" #include /** \class TopologyPreservingDigitalSurfaceEvolutionImageFilter * */ namespace itk { template class TopologyPreservingDigitalSurfaceEvolutionImageFilter : public InPlaceImageFilter { public: /** Extract dimension from input image. */ itkStaticConstMacro( ImageDimension, unsigned int, TImage::ImageDimension ); /** Convenient typedefs for simplifying declarations. */ typedef TImage ImageType; /** Standard class typedefs. */ typedef TopologyPreservingDigitalSurfaceEvolutionImageFilter Self; typedef InPlaceImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Run-time type information (and related methods) */ itkTypeMacro( TopologyPreservingDigitalSurfaceEvolutionImageFilter, InPlaceImageFilter ); /** Method for creation through the object factory. */ itkNewMacro( Self ); /** Image typedef support. */ typedef typename ImageType::PixelType PixelType; typedef typename ImageType::IndexType IndexType; typedef std::deque IndexContainerType; typedef NeighborhoodIterator NeighborhoodIteratorType; typedef float RealType; itkSetObjectMacro( TargetImage, ImageType ); itkGetObjectMacro( TargetImage, ImageType ); itkSetMacro( NumberOfIterations, unsigned int ); itkGetConstMacro( NumberOfIterations, unsigned int ); itkSetMacro( ForegroundValue, PixelType ); itkGetConstMacro( ForegroundValue, PixelType ); itkSetMacro( UseInversionMode, bool ); itkGetConstMacro( UseInversionMode, bool ); protected: TopologyPreservingDigitalSurfaceEvolutionImageFilter(); virtual ~TopologyPreservingDigitalSurfaceEvolutionImageFilter(); void PrintSelf( std::ostream& os, Indent indent ) const; void GenerateData(); private: //purposely not implemented TopologyPreservingDigitalSurfaceEvolutionImageFilter( const Self& ); void operator=( const Self& ); typename ImageType::Pointer m_TargetImage; typename ImageType::Pointer m_LabelSurfaceImage; /** * user-selected parameters */ bool m_UseInversionMode; unsigned int m_NumberOfIterations; RealType m_ThresholdValue; PixelType m_ForegroundValue; PixelType m_BackgroundValue; /** * variables for internal use */ PixelType m_SurfaceLabel; IndexContainerType m_CriticalConfigurationIndices; /** * Functions/data for the 2-D case */ void InitializeIndices2D(); bool IsChangeSafe2D( IndexType ); bool IsCriticalC1Configuration2D( Array ); bool IsCriticalC2Configuration2D( Array ); bool IsCriticalC3Configuration2D( Array ); bool IsCriticalC4Configuration2D( Array ); bool IsSpecialCaseOfC4Configuration2D( PixelType, IndexType, IndexType, IndexType ); Array m_RotationIndices[4]; Array m_ReflectionIndices[2]; void CreateLabelSurfaceImage(); void InitializeIndices3D(); bool IsCriticalC1Configuration3D( Array ); unsigned int IsCriticalC2Configuration3D( Array ); bool IsChangeSafe3D( IndexType ); bool IsCriticalTopologicalConfiguration( IndexType ); Array m_C1Indices[12]; Array m_C2Indices[8]; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.txx" #endif #endif itksnap-3.4.0/Common/ITKExtras/itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.txx000066400000000000000000000474131263013355200322310ustar00rootroot00000000000000#ifndef __itkTopologyPreservingDigitalSurfaceEvolutionImageFilter_txx #define __itkTopologyPreservingDigitalSurfaceEvolutionImageFilter_txx #include "itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.h" #include "itkBinaryDilateImageFilter.h" #include "itkBinaryDiamondStructuringElement.h" #include "itkBinaryThresholdImageFilter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkSubtractImageFilter.h" #include "itkTimeProbe.h" namespace itk { template TopologyPreservingDigitalSurfaceEvolutionImageFilter ::TopologyPreservingDigitalSurfaceEvolutionImageFilter() { this->SetNumberOfRequiredInputs( 1 ); this->m_NumberOfIterations = 5; this->m_ThresholdValue = 0.5; this->m_BackgroundValue = NumericTraits::Zero; this->m_ForegroundValue = NumericTraits::One; this->m_UseInversionMode = false; this->m_TargetImage = NULL; if( ImageDimension == 2 ) { this->InitializeIndices2D(); } else if( ImageDimension == 3 ) { this->InitializeIndices3D(); } else { itkExceptionMacro( "Image dimension must be equal to 2 or 3." ); } } template TopologyPreservingDigitalSurfaceEvolutionImageFilter ::~TopologyPreservingDigitalSurfaceEvolutionImageFilter() { } template void TopologyPreservingDigitalSurfaceEvolutionImageFilter ::GenerateData() { this->AllocateOutputs(); if( !this->m_TargetImage ) { itkExceptionMacro( "TargetImage not specified." ); } RealType totalDifference = 0.0; ImageRegionIterator It( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); ImageRegionIterator ItT( this->m_TargetImage, this->m_TargetImage->GetRequestedRegion() ); for( It.GoToBegin(), ItT.GoToBegin(); !It.IsAtEnd(); ++It, ++ItT ) { if( It.Get() != ItT.Get() ) { totalDifference += 1.0; } if( this->m_UseInversionMode ) { if( It.Get() == this->m_ForegroundValue ) { It.Set( this->m_BackgroundValue ); } else { It.Set( this->m_ForegroundValue ); } if( ItT.Get() == this->m_ForegroundValue ) { ItT.Set( this->m_BackgroundValue ); } else { ItT.Set( this->m_ForegroundValue ); } } } this->m_SurfaceLabel = this->m_ForegroundValue + 1; unsigned int iterations = 0; bool changeDetected = true; RealType totalNumberOfChanges = 0.0; // std::cout << "/" << std::flush; // // TimeProbe timer; // timer.Start(); this->UpdateProgress( 0.0 ); // RealType oldProgress = this->GetProgress() * 100.0; while( iterations++ < this->m_NumberOfIterations && changeDetected ) { changeDetected = false; this->CreateLabelSurfaceImage(); ImageRegionIteratorWithIndex ItI( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); ImageRegionIterator ItT( this->m_TargetImage, this->m_TargetImage->GetRequestedRegion() ); ImageRegionIterator ItL( this->m_LabelSurfaceImage, this->m_LabelSurfaceImage->GetRequestedRegion() ); ItI.GoToBegin(); ItT.GoToBegin(); ItL.GoToBegin(); while( !ItI.IsAtEnd() ) { RealType absoluteDifference = vnl_math_abs( static_cast( ItT.Get() - ItI.Get() ) ); if( ItL.Get() == this->m_SurfaceLabel ) { if( absoluteDifference > this->m_ThresholdValue ) { bool isChangeSafe = false; if( ImageDimension == 2 ) { isChangeSafe = this->IsChangeSafe2D( ItI.GetIndex() ); } else { isChangeSafe = this->IsChangeSafe3D( ItI.GetIndex() ); } if( isChangeSafe ) { ItI.Set( this->m_ForegroundValue ); changeDetected = true; totalNumberOfChanges += 1.0; this->UpdateProgress( totalNumberOfChanges / totalDifference ); // RealType newProgress = this->GetProgress() * 100.0; // // if( newProgress - oldProgress >= 1.0 ) // { // oldProgress = newProgress; // std::cout << "*" << std::flush; // } } } } ++ItI; ++ItT; ++ItL; } } // timer.Stop(); // std::cout << "/ -> " << this->GetProgress() << " (" // << timer.GetMeanTime() << " seconds)" << std::endl; if( this->m_UseInversionMode ) { ImageRegionIterator It( this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); ImageRegionIterator ItT( this->m_TargetImage, this->m_TargetImage->GetRequestedRegion() ); for( It.GoToBegin(), ItT.GoToBegin(); !It.IsAtEnd(); ++It, ++ItT ) { if( It.Get() == this->m_ForegroundValue ) { It.Set( this->m_BackgroundValue ); } else { It.Set( this->m_ForegroundValue ); } if( ItT.Get() == this->m_ForegroundValue ) { ItT.Set( this->m_BackgroundValue ); } else { ItT.Set( this->m_ForegroundValue ); } } } this->UpdateProgress( 1.0 ); } template void TopologyPreservingDigitalSurfaceEvolutionImageFilter ::CreateLabelSurfaceImage() { typedef BinaryDiamondStructuringElement StructuringElementType; StructuringElementType element; element.SetRadius( 1 ); element.CreateStructuringElement(); typedef BinaryDilateImageFilter DilateFilterType; typename DilateFilterType::Pointer dilater = DilateFilterType::New(); dilater->SetKernel( element ); dilater->SetInput( this->GetOutput() ); dilater->SetBackgroundValue( this->m_BackgroundValue ); dilater->SetForegroundValue( this->m_ForegroundValue ); dilater->Update(); typedef SubtractImageFilter SubtracterType; typename SubtracterType::Pointer subtracter = SubtracterType::New(); subtracter->SetInput1( dilater->GetOutput() ); subtracter->SetInput2( this->GetOutput() ); subtracter->Update(); typedef BinaryThresholdImageFilter ThresholderType; typename ThresholderType::Pointer thresholder = ThresholderType::New(); thresholder->SetInput( subtracter->GetOutput() ); thresholder->SetLowerThreshold( this->m_ForegroundValue ); thresholder->SetUpperThreshold( this->m_ForegroundValue ); thresholder->SetInsideValue( this->m_SurfaceLabel ); thresholder->SetOutsideValue( this->m_BackgroundValue ); thresholder->Update(); this->m_LabelSurfaceImage = thresholder->GetOutput(); } /* * 2-D */ template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsChangeSafe2D( IndexType idx ) { typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType It( radius, this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); It.SetLocation( idx ); Array neighborhoodPixels( 9 ); // Check for critical configurations: 4 90-degree rotations for ( unsigned int i = 0; i < 4; i++ ) { for ( unsigned int j = 0; j < 9; j++ ) { neighborhoodPixels[j] = ( It.GetPixel( this->m_RotationIndices[i][j] ) == this->m_BackgroundValue ); if( this->m_RotationIndices[i][j] == 4 ) { neighborhoodPixels[j] = !neighborhoodPixels[j]; } } if( this->IsCriticalC1Configuration2D( neighborhoodPixels ) || this->IsCriticalC2Configuration2D( neighborhoodPixels ) || this->IsCriticalC3Configuration2D( neighborhoodPixels ) || this->IsCriticalC4Configuration2D( neighborhoodPixels ) ) { return false; } } // Check for critical configurations: 2 reflections // Note that the reflections for the C1 and C2 cases // are covered by the rotation cases above (except // in the case of FullInvariance == false. for ( unsigned int i = 0; i < 2; i++ ) { for ( unsigned int j = 0; j < 9; j++ ) { neighborhoodPixels[j] = ( It.GetPixel( this->m_ReflectionIndices[i][j] ) == this->m_BackgroundValue ); if( this->m_ReflectionIndices[i][j] == 4 ) { neighborhoodPixels[j] = !neighborhoodPixels[j]; } } // if( !this->m_FullInvariance // && ( this->IsCriticalC1Configuration2D( neighborhoodPixels ) // || this->IsCriticalC2Configuration2D( neighborhoodPixels ) ) ) // { // return false; // } if( this->IsCriticalC3Configuration2D( neighborhoodPixels ) || this->IsCriticalC4Configuration2D( neighborhoodPixels ) ) { return false; } } /** * this check is only valid after the other checks have * been performed. */ if( this->IsCriticalTopologicalConfiguration( idx ) ) { return false; } return true; } template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsCriticalTopologicalConfiguration( IndexType idx ) { typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType It( radius, this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); It.SetLocation( idx ); unsigned int numberOfCriticalC3Configurations = 0; unsigned int numberOfFaces = 0; for( unsigned int d = 0; d < ImageDimension; d++ ) { if( It.GetNext( d ) == this->m_ForegroundValue ) { numberOfFaces++; } if( It.GetPrevious( d ) == this->m_ForegroundValue ) { numberOfFaces++; } if( It.GetNext( d ) == this->m_ForegroundValue && It.GetPrevious( d ) == this->m_ForegroundValue ) { numberOfCriticalC3Configurations++; } } if( numberOfCriticalC3Configurations > 0 && numberOfFaces % 2 == 0 && numberOfCriticalC3Configurations * 2 == numberOfFaces ) { return true; } return false; } /* * 2-D */ template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsCriticalC1Configuration2D( Array neighborhood ) { return ( !neighborhood[0] && neighborhood[1] && neighborhood[3] && !neighborhood[4] && !neighborhood[8] ); } /* * 2-D */ template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsCriticalC2Configuration2D( Array neighborhood ) { return ( !neighborhood[0] && neighborhood[1] && neighborhood[3] && !neighborhood[4] && neighborhood[8] && ( neighborhood[5] || neighborhood[7] ) ); } /* * 2-D */ template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsCriticalC3Configuration2D( Array neighborhood ) { return ( !neighborhood[0] && neighborhood[1] && neighborhood[3] && !neighborhood[4] && !neighborhood[5] && neighborhood[6] && !neighborhood[7] && neighborhood[8] ); } /* * 2-D */ template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsCriticalC4Configuration2D( Array neighborhood ) { return ( !neighborhood[0] && neighborhood[1] && neighborhood[3] && !neighborhood[4] && !neighborhood[5] && !neighborhood[6] && !neighborhood[7] && neighborhood[8] ); } /* * 2-D */ template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsSpecialCaseOfC4Configuration2D( PixelType label, IndexType idx, IndexType idx6, IndexType idx7 ) { IndexType idxa; IndexType idxb; for ( unsigned int j = 0; j < 2; j++ ) { idxa[j] = idx7[j] + ( idx7[j] - idx[j] ); idxb[j] = idx6[j] + ( idx7[j] - idx[j] ); } return ( this->GetOutput()->GetRequestedRegion().IsInside( idxa ) && this->GetOutput()->GetRequestedRegion().IsInside( idxb ) && this->GetOutput()->GetPixel( idxa ) < label && this->GetOutput()->GetPixel( idxb ) >= label ); } /* * 2-D */ template void TopologyPreservingDigitalSurfaceEvolutionImageFilter ::InitializeIndices2D() { this->m_RotationIndices[0].SetSize( 9 ); this->m_RotationIndices[1].SetSize( 9 ); this->m_RotationIndices[2].SetSize( 9 ); this->m_RotationIndices[3].SetSize( 9 ); this->m_RotationIndices[0][0] = 0; this->m_RotationIndices[0][1] = 1; this->m_RotationIndices[0][2] = 2; this->m_RotationIndices[0][3] = 3; this->m_RotationIndices[0][4] = 4; this->m_RotationIndices[0][5] = 5; this->m_RotationIndices[0][6] = 6; this->m_RotationIndices[0][7] = 7; this->m_RotationIndices[0][8] = 8; this->m_RotationIndices[1][0] = 2; this->m_RotationIndices[1][1] = 5; this->m_RotationIndices[1][2] = 8; this->m_RotationIndices[1][3] = 1; this->m_RotationIndices[1][4] = 4; this->m_RotationIndices[1][5] = 7; this->m_RotationIndices[1][6] = 0; this->m_RotationIndices[1][7] = 3; this->m_RotationIndices[1][8] = 6; this->m_RotationIndices[2][0] = 8; this->m_RotationIndices[2][1] = 7; this->m_RotationIndices[2][2] = 6; this->m_RotationIndices[2][3] = 5; this->m_RotationIndices[2][4] = 4; this->m_RotationIndices[2][5] = 3; this->m_RotationIndices[2][6] = 2; this->m_RotationIndices[2][7] = 1; this->m_RotationIndices[2][8] = 0; this->m_RotationIndices[3][0] = 6; this->m_RotationIndices[3][1] = 3; this->m_RotationIndices[3][2] = 0; this->m_RotationIndices[3][3] = 7; this->m_RotationIndices[3][4] = 4; this->m_RotationIndices[3][5] = 1; this->m_RotationIndices[3][6] = 8; this->m_RotationIndices[3][7] = 5; this->m_RotationIndices[3][8] = 2; this->m_ReflectionIndices[0].SetSize( 9 ); this->m_ReflectionIndices[1].SetSize( 9 ); this->m_ReflectionIndices[0][0] = 6; this->m_ReflectionIndices[0][1] = 7; this->m_ReflectionIndices[0][2] = 8; this->m_ReflectionIndices[0][3] = 3; this->m_ReflectionIndices[0][4] = 4; this->m_ReflectionIndices[0][5] = 5; this->m_ReflectionIndices[0][6] = 0; this->m_ReflectionIndices[0][7] = 1; this->m_ReflectionIndices[0][8] = 2; this->m_ReflectionIndices[1][0] = 2; this->m_ReflectionIndices[1][1] = 1; this->m_ReflectionIndices[1][2] = 0; this->m_ReflectionIndices[1][3] = 5; this->m_ReflectionIndices[1][4] = 4; this->m_ReflectionIndices[1][5] = 3; this->m_ReflectionIndices[1][6] = 8; this->m_ReflectionIndices[1][7] = 7; this->m_ReflectionIndices[1][8] = 6; } /* * 3-D */ template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsChangeSafe3D( IndexType idx ) { Array neighborhoodPixels( 8 ); typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType It( radius, this->GetOutput(), this->GetOutput()->GetRequestedRegion() ); It.SetLocation( idx ); // Check for C1 critical configurations for ( unsigned int i = 0; i < 12; i++ ) { for ( unsigned int j = 0; j < 4; j++ ) { neighborhoodPixels[j] = ( It.GetPixel( this->m_C1Indices[i][j] ) == this->m_ForegroundValue ); if( this->m_C1Indices[i][j] == 13 ) { neighborhoodPixels[j] = !neighborhoodPixels[j]; } } if( this->IsCriticalC1Configuration3D( neighborhoodPixels ) ) { return false; } } // Check for C2 critical configurations for ( unsigned int i = 0; i < 8; i++ ) { for ( unsigned int j = 0; j < 8; j++ ) { neighborhoodPixels[j] = ( It.GetPixel( this->m_C2Indices[i][j] ) == this->m_ForegroundValue ); if( this->m_C2Indices[i][j] == 13 ) { neighborhoodPixels[j] = !neighborhoodPixels[j]; } } if( this->IsCriticalC2Configuration3D( neighborhoodPixels ) ) { return false; } } /** * this check is only valid after the other checks have * been performed. */ if( this->IsCriticalTopologicalConfiguration( idx ) ) { return false; } return true; } /* * 3-D */ template bool TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsCriticalC1Configuration3D( Array neighborhood ) { return ( ( neighborhood[0] && neighborhood[1] && !neighborhood[2] && !neighborhood[3] ) || ( !neighborhood[0] && !neighborhood[1] && neighborhood[2] && neighborhood[3] ) ); } /* * 3-D */ template unsigned int TopologyPreservingDigitalSurfaceEvolutionImageFilter ::IsCriticalC2Configuration3D( Array neighborhood ) { // Check if Type 1 or Type 2 for ( unsigned int i = 0; i < 4; i++ ) { bool isC2 = false; if( neighborhood[2*i] == neighborhood[2*i+1] ) { isC2 = true; for ( unsigned int j = 0; j < 8; j++ ) { if( neighborhood[j] == neighborhood[2*i] && j != 2*i && j != 2*i+1 ) { isC2 = false; } } } if( isC2 ) { if( neighborhood[2*i] ) { return 1; } else { return 2; } } } return 0; } /* * 3-D */ template void TopologyPreservingDigitalSurfaceEvolutionImageFilter ::InitializeIndices3D() { for ( unsigned int i = 0; i < 12; i++ ) { this->m_C1Indices[i].SetSize( 4 ); } for ( unsigned int i = 0; i < 8; i++ ) { this->m_C2Indices[i].SetSize( 8 ); } this->m_C1Indices[0][0] = 1; this->m_C1Indices[0][1] = 13; this->m_C1Indices[0][2] = 4; this->m_C1Indices[0][3] = 10; this->m_C1Indices[1][0] = 9; this->m_C1Indices[1][1] = 13; this->m_C1Indices[1][2] = 10; this->m_C1Indices[1][3] = 12; this->m_C1Indices[2][0] = 3; this->m_C1Indices[2][1] = 13; this->m_C1Indices[2][2] = 4; this->m_C1Indices[2][3] = 12; this->m_C1Indices[3][0] = 4; this->m_C1Indices[3][1] = 14; this->m_C1Indices[3][2] = 5; this->m_C1Indices[3][3] = 13; this->m_C1Indices[4][0] = 12; this->m_C1Indices[4][1] = 22; this->m_C1Indices[4][2] = 13; this->m_C1Indices[4][3] = 21; this->m_C1Indices[5][0] = 13; this->m_C1Indices[5][1] = 23; this->m_C1Indices[5][2] = 14; this->m_C1Indices[5][3] = 22; this->m_C1Indices[6][0] = 4; this->m_C1Indices[6][1] = 16; this->m_C1Indices[6][2] = 7; this->m_C1Indices[6][3] = 13; this->m_C1Indices[7][0] = 13; this->m_C1Indices[7][1] = 25; this->m_C1Indices[7][2] = 16; this->m_C1Indices[7][3] = 22; this->m_C1Indices[8][0] = 10; this->m_C1Indices[8][1] = 22; this->m_C1Indices[8][2] = 13; this->m_C1Indices[8][3] = 19; this->m_C1Indices[9][0] = 12; this->m_C1Indices[9][1] = 16; this->m_C1Indices[9][2] = 13; this->m_C1Indices[9][3] = 15; this->m_C1Indices[10][0] = 13; this->m_C1Indices[10][1] = 17; this->m_C1Indices[10][2] = 14; this->m_C1Indices[10][3] = 16; this->m_C1Indices[11][0] = 10; this->m_C1Indices[11][1] = 14; this->m_C1Indices[11][2] = 11; this->m_C1Indices[11][3] = 13; this->m_C2Indices[0][0] = 0; this->m_C2Indices[0][1] = 13; this->m_C2Indices[0][2] = 1; this->m_C2Indices[0][3] = 12; this->m_C2Indices[0][4] = 3; this->m_C2Indices[0][5] = 10; this->m_C2Indices[0][6] = 4; this->m_C2Indices[0][7] = 9; this->m_C2Indices[4][0] = 9; this->m_C2Indices[4][1] = 22; this->m_C2Indices[4][2] = 10; this->m_C2Indices[4][3] = 21; this->m_C2Indices[4][4] = 12; this->m_C2Indices[4][5] = 19; this->m_C2Indices[4][6] = 13; this->m_C2Indices[4][7] = 18; for ( unsigned int i = 1; i < 4; i++ ) { int addend; if( i == 2 ) { addend = 2; } else { addend = 1; } for ( unsigned int j = 0; j < 8; j++ ) { this->m_C2Indices[i ][j] = this->m_C2Indices[i-1][j] + addend; this->m_C2Indices[i+4][j] = this->m_C2Indices[i+3][j] + addend; } } } template void TopologyPreservingDigitalSurfaceEvolutionImageFilter ::PrintSelf( std::ostream& os, Indent indent) const { Superclass::PrintSelf( os, indent ); } } // end namespace itk #endif itksnap-3.4.0/Common/ITKExtras/itkVoxBoCUBImageIO.cxx000066400000000000000000000617261263013355200222670ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: itkVoxBoCUBImageIO.cxx,v $ Language: C++ Date: $Date: 2008/11/20 04:23:28 $ Version: $Revision: 1.4 $ Copyright (c) Insight Software Consortium. All rights reserved. This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #include "itkVoxBoCUBImageIO.h" #include "itkIOCommon.h" #include "itkExceptionObject.h" #include "itkMetaDataObject.h" #include "itkByteSwapper.h" #include #include #include #include // Commented out because zlib is not available through Insight Applications #ifdef SNAP_GZIP_SUPPORT #include #endif #include "itkSpatialOrientationAdapter.h" namespace itk { /** * A generic reader and writer object for VoxBo files. Basically it * provides uniform access to gzip and normal files */ class GenericCUBFileAdaptor { public: virtual ~GenericCUBFileAdaptor() {} virtual unsigned char ReadByte() = 0; virtual void ReadData(void *data, unsigned long bytes) = 0; virtual void WriteData(const void *data, unsigned long bytes) = 0; std::string ReadHeader() { // Read everything up to the \f symbol std::ostringstream oss; unsigned char byte = ReadByte(); while(byte != '\f') { oss << byte; byte = ReadByte(); } // Read the next byte unsigned char term = ReadByte(); if(term == '\r') term = ReadByte(); // Throw exception if term is not there if(term != '\n') { ExceptionObject exception; exception.SetDescription("Header is not terminated by newline."); throw exception; } // Return the header string return oss.str(); } }; /** * A reader for gzip files */ #ifdef SNAP_GZIP_SUPPORT class CompressedCUBFileAdaptor : public GenericCUBFileAdaptor { public: CompressedCUBFileAdaptor(const char *file, const char *mode) { m_GzFile = ::gzopen(file, mode); if(m_GzFile == NULL) { ExceptionObject exception; exception.SetDescription("File cannot be accessed"); throw exception; } } ~CompressedCUBFileAdaptor() { if(m_GzFile) ::gzclose(m_GzFile); } unsigned char ReadByte() { int byte = ::gzgetc(m_GzFile); if(byte < 0) { std::ostringstream oss; oss << "Error reading byte from file at position: " << ::gztell(m_GzFile); ExceptionObject exception; exception.SetDescription(oss.str().c_str()); throw exception; } return static_cast(byte); } void ReadData(void *data, unsigned long bytes) { if(m_GzFile == NULL) { ExceptionObject exception; exception.SetDescription("File cannot be read"); throw exception; } int bread = ::gzread(m_GzFile, data, bytes); if(bread != bytes) { std::ostringstream oss; oss << "File size does not match header: " << bytes << " bytes requested but only " << bread << " bytes available!" << std::endl << "At file position " << ::gztell(m_GzFile); ExceptionObject exception; exception.SetDescription(oss.str().c_str()); throw exception; } } void WriteData(const void *data, unsigned long bytes) { if(m_GzFile == NULL) { ExceptionObject exception; exception.SetDescription("File cannot be written"); throw exception; } int bwritten = ::gzwrite(m_GzFile, (void *) data, bytes); if(bwritten != bytes) { ExceptionObject exception; exception.SetDescription("Could not write all bytes to file"); throw exception; } } private: ::gzFile m_GzFile; }; #endif // SNAP_GZIP_SUPPORT /** * A reader for non-gzip files */ class DirectCUBFileAdaptor : public GenericCUBFileAdaptor { public: DirectCUBFileAdaptor(const char *file, const char *mode) { m_File = fopen(file, mode); if(m_File == NULL) { ExceptionObject exception; exception.SetDescription("File cannot be read"); throw exception; } } virtual ~DirectCUBFileAdaptor() { if(m_File) fclose(m_File); } unsigned char ReadByte() { int byte = fgetc(m_File); if(byte == EOF) { std::ostringstream oss; oss << "Error reading byte from file at position: " << ::ftell(m_File); ExceptionObject exception; exception.SetDescription(oss.str().c_str()); throw exception; } return static_cast(byte); } void ReadData(void *data, unsigned long bytes) { if(m_File == NULL) { ExceptionObject exception; exception.SetDescription("File cannot be read"); throw exception; } size_t bread = fread(data, 1, bytes, m_File); if(static_cast(bread) != bytes) { std::ostringstream oss; oss << "File size does not match header: " << bytes << " bytes requested but only " << bread << " bytes available!" << std::endl << "At file position " << ftell(m_File); ExceptionObject exception; exception.SetDescription(oss.str().c_str()); throw exception; } } void WriteData(const void *data, unsigned long bytes) { if(m_File == NULL) { ExceptionObject exception; exception.SetDescription("File cannot be written"); throw exception; } size_t bwritten = fwrite(data, 1, bytes, m_File); if(static_cast(bwritten) != bytes) { ExceptionObject exception; exception.SetDescription("Could not write all bytes to file"); throw exception; } } private: FILE *m_File; }; /** * A swap helper class, used to perform swapping for any input * data type. */ template class VoxBoCUBImageIOSwapHelper { public: typedef ImageIOBase::ByteOrder ByteOrder; static void SwapIfNecessary( void *buffer, unsigned long numberOfBytes, ByteOrder order) { if ( order == ImageIOBase::LittleEndian ) { ByteSwapper::SwapRangeFromSystemToLittleEndian( (TPixel*)buffer, numberOfBytes / sizeof(TPixel) ); } else if ( order == ImageIOBase::BigEndian ) { ByteSwapper::SwapRangeFromSystemToBigEndian( (TPixel *)buffer, numberOfBytes / sizeof(TPixel) ); } } }; // Strings const char *VoxBoCUBImageIO::VB_IDENTIFIER_SYSTEM = "VB98"; const char *VoxBoCUBImageIO::VB_IDENTIFIER_FILETYPE = "CUB1"; const char *VoxBoCUBImageIO::VB_DIMENSIONS = "VoxDims(XYZ)"; const char *VoxBoCUBImageIO::VB_SPACING = "VoxSizes(XYZ)"; const char *VoxBoCUBImageIO::VB_ORIGIN = "Origin(XYZ)"; const char *VoxBoCUBImageIO::VB_DATATYPE = "DataType"; const char *VoxBoCUBImageIO::VB_BYTEORDER = "Byteorder"; const char *VoxBoCUBImageIO::VB_ORIENTATION = "Orientation"; const char *VoxBoCUBImageIO::VB_BYTEORDER_MSB = "msbfirst"; const char *VoxBoCUBImageIO::VB_BYTEORDER_LSB = "lsbfirst"; const char *VoxBoCUBImageIO::VB_DATATYPE_BYTE = "Byte"; const char *VoxBoCUBImageIO::VB_DATATYPE_INT = "Integer"; const char *VoxBoCUBImageIO::VB_DATATYPE_FLOAT = "Float"; const char *VoxBoCUBImageIO::VB_DATATYPE_DOUBLE = "Double"; /** Constructor */ VoxBoCUBImageIO::VoxBoCUBImageIO() { InitializeOrientationMap(); m_ByteOrder = BigEndian; m_Reader = NULL; m_Writer = NULL; } /** Destructor */ VoxBoCUBImageIO::~VoxBoCUBImageIO() { if(m_Reader) delete m_Reader; if(m_Writer) delete m_Writer; } GenericCUBFileAdaptor * VoxBoCUBImageIO::CreateReader(const char *filename) { try { bool compressed; if(CheckExtension(filename, compressed)) if(compressed) #ifdef SNAP_GZIP_SUPPORT return new CompressedCUBFileAdaptor(filename, "rb"); #else return NULL; #endif else return new DirectCUBFileAdaptor(filename, "rb"); else return NULL; } catch(...) { return NULL; } } GenericCUBFileAdaptor * VoxBoCUBImageIO::CreateWriter(const char *filename) { try { bool compressed; if(CheckExtension(filename, compressed)) if(compressed) #ifdef SNAP_GZIP_SUPPORT return new CompressedCUBFileAdaptor(filename, "rb"); #else return NULL; #endif else return new DirectCUBFileAdaptor(filename, "wb"); else return NULL; } catch(...) { return NULL; } } bool VoxBoCUBImageIO::CanReadFile( const char* filename ) { // First check if the file can be read GenericCUBFileAdaptor *reader = CreateReader(filename); if(reader == NULL) { itkDebugMacro(<<"The file is not a valid CUB file"); return false; } // Now check the content bool iscub = true; try { // Get the header std::istringstream iss(reader->ReadHeader()); // Read the first two words std::string word; // Read the first line from the file iss >> word; if(word != VB_IDENTIFIER_SYSTEM) iscub = false; // Read the second line iss >> word; if(word != VB_IDENTIFIER_FILETYPE) iscub = false; } catch(...) { iscub = false; } delete reader; return iscub; } bool VoxBoCUBImageIO::CanWriteFile( const char * name ) { bool compressed; return CheckExtension(name, compressed); } void VoxBoCUBImageIO::Read(void* buffer) { if(m_Reader == NULL) { ExceptionObject exception(__FILE__, __LINE__); exception.SetDescription("File cannot be read"); throw exception; } m_Reader->ReadData(buffer, GetImageSizeInBytes()); this->SwapBytesIfNecessary(buffer, GetImageSizeInBytes()); } /** * Read Information about the VoxBoCUB file * and put the cursor of the stream just before the first data pixel */ void VoxBoCUBImageIO::ReadImageInformation() { // Make sure there is no other reader if(m_Reader) delete m_Reader; // Create a reader m_Reader = CreateReader(m_FileName.c_str()); if(m_Reader == NULL) { ExceptionObject exception(__FILE__, __LINE__); exception.SetDescription("File cannot be read"); throw exception; } // Set the number of dimensions to three SetNumberOfDimensions(3); // Read the file header std::istringstream issHeader(m_Reader->ReadHeader()); // Read every string in the header. Parse the strings that are special while(issHeader.good()) { // Read a line from the stream char linebuffer[512]; issHeader.getline(linebuffer, 512); // Get the key string std::istringstream iss(linebuffer); std::string key; // Read the key and strip the colon from it iss >> key; if(key.size() > 0 && key[key.size() - 1] == ':') { // Strip the colon off the key key = key.substr(0, key.size() - 1); // Check if this is a relevant key if(key == VB_DIMENSIONS) { iss >> m_Dimensions[0]; iss >> m_Dimensions[1]; iss >> m_Dimensions[2]; } else if(key == VB_SPACING) { iss >> m_Spacing[0]; iss >> m_Spacing[1]; iss >> m_Spacing[2]; } else if(key == VB_ORIGIN) { double ox, oy, oz; iss >> ox; iss >> oy; iss >> oz; m_Origin[0] = ox * m_Spacing[0]; m_Origin[1] = oy * m_Spacing[1]; m_Origin[2] = oz * m_Spacing[2]; } else if(key == VB_DATATYPE) { std::string type; iss >> type; m_PixelType = SCALAR; if(type == VB_DATATYPE_BYTE) m_ComponentType = UCHAR; else if(type == VB_DATATYPE_INT) m_ComponentType = USHORT; else if(type == VB_DATATYPE_FLOAT) m_ComponentType = FLOAT; else if(type == VB_DATATYPE_DOUBLE) m_ComponentType = DOUBLE; } else if(key == VB_BYTEORDER) { std::string type; iss >> type; if(type == VB_BYTEORDER_MSB) SetByteOrderToBigEndian(); else if(type == VB_BYTEORDER_LSB) SetByteOrderToLittleEndian(); else { ExceptionObject exception(__FILE__, __LINE__); exception.SetDescription("Unknown byte order constant"); throw exception; } } else if(key == VB_ORIENTATION) { std::string code; iss >> code; // Set the orientation code in the data dictionary OrientationMap::const_iterator it = m_OrientationMap.find(code); if(it != m_OrientationMap.end()) { //Octavian original begin //itk::MetaDataDictionary &dic =this->GetMetaDataDictionary(); //EncapsulateMetaData( //dic, ITK_CoordinateOrientation, it->second); //Octavian original end //NOTE: The itk::ImageIOBase direction is a std::vector >, and threeDDirection is a 3x3 matrix itk::SpatialOrientationAdapter soAdaptor; itk::SpatialOrientationAdapter::DirectionType threeDDirection=soAdaptor.ToDirectionCosines(it->second); this->m_Direction[0][0]=threeDDirection[0][0]; this->m_Direction[0][1]=threeDDirection[0][1]; this->m_Direction[0][2]=threeDDirection[0][2]; this->m_Direction[1][0]=threeDDirection[1][0]; this->m_Direction[1][1]=threeDDirection[1][1]; this->m_Direction[1][2]=threeDDirection[1][2]; this->m_Direction[2][0]=threeDDirection[2][0]; this->m_Direction[2][1]=threeDDirection[2][1]; this->m_Direction[2][2]=threeDDirection[2][2]; } } else { // Encode the right hand side of the string in the meta-data dic std::string word; std::ostringstream oss; while(iss >> word) { if(oss.str().size()) oss << " "; oss << word; } itk::MetaDataDictionary &dic =this->GetMetaDataDictionary(); EncapsulateMetaData(dic, key, oss.str()); } } } } void VoxBoCUBImageIO ::WriteImageInformation(void) { // See if we have a writer already if(m_Writer != NULL) delete m_Writer; // First check if the file can be written to m_Writer = CreateWriter(m_FileName.c_str()); if(m_Writer == NULL) { ExceptionObject exception(__FILE__, __LINE__); exception.SetDescription("File cannot be read"); throw exception; } // Check that the number of dimensions is correct if(GetNumberOfDimensions() != 3) { ExceptionObject exception(__FILE__, __LINE__); exception.SetDescription("Unsupported number of dimensions"); throw exception; } // Put together a header std::ostringstream header; // Write the identifiers header << VB_IDENTIFIER_SYSTEM << std::endl; header << VB_IDENTIFIER_FILETYPE << std::endl; // Write the image dimensions header << VB_DIMENSIONS << ": " << m_Dimensions[0] << " " << m_Dimensions[1] << " " << m_Dimensions[2] << std::endl; // Write the spacing header << VB_SPACING << ": " << m_Spacing[0] << " " << m_Spacing[1] << " " << m_Spacing[2] << std::endl; // Write the origin (have to convert to bytes) header << VB_ORIGIN << ": " << static_cast< int >( m_Origin[0] / m_Spacing[0] + 0.5 ) << " " << static_cast< int >( m_Origin[1] / m_Spacing[1] + 0.5 ) << " " << static_cast< int >( m_Origin[2] / m_Spacing[2] + 0.5 ) << std::endl; // Write the byte order header << VB_BYTEORDER << ": " << (ByteSwapper::SystemIsBigEndian() ? VB_BYTEORDER_MSB : VB_BYTEORDER_LSB) << std::endl; // Write the data type switch(m_ComponentType) { case CHAR: case UCHAR: header << VB_DATATYPE << ": " << VB_DATATYPE_BYTE << std::endl; break; case SHORT: case USHORT: header << VB_DATATYPE << ": " << VB_DATATYPE_INT << std::endl; break; case FLOAT: header << VB_DATATYPE << ": " << VB_DATATYPE_FLOAT << std::endl; break; case DOUBLE: header << VB_DATATYPE << ": " << VB_DATATYPE_DOUBLE << std::endl; break; default: ExceptionObject exception(__FILE__, __LINE__); exception.SetDescription("Unsupported pixel component type"); throw exception; } // Write the orientation code //Octavian original begin //MetaDataDictionary &dic = GetMetaDataDictionary(); //OrientationFlags oflag = SpatialOrientation::ITK_COORDINATE_ORIENTATION_INVALID; //if(ExposeMetaData(dic, ITK_CoordinateOrientation, oflag)) //Octavian original end //NOTE: The itk::ImageIOBase direction is a std::vector >, and threeDDirection is a 3x3 matrix itk::SpatialOrientationAdapter soAdaptor; itk::SpatialOrientationAdapter::DirectionType threeDDirection; threeDDirection[0][0]=this->m_Direction[0][0]; threeDDirection[0][1]=this->m_Direction[0][1]; threeDDirection[0][2]=this->m_Direction[0][2]; threeDDirection[1][0]=this->m_Direction[1][0]; threeDDirection[1][1]=this->m_Direction[1][1]; threeDDirection[1][2]=this->m_Direction[1][2]; threeDDirection[2][0]=this->m_Direction[2][0]; threeDDirection[2][1]=this->m_Direction[2][1]; threeDDirection[2][2]=this->m_Direction[2][2]; OrientationFlags oflag = soAdaptor.FromDirectionCosines(threeDDirection); { InverseOrientationMap::const_iterator it = m_InverseOrientationMap.find(oflag); if(it != m_InverseOrientationMap.end()) { header << VB_ORIENTATION << ": " << it->second << std::endl; } } // Write the terminating characters header << "\f\n"; // Write the header to the file as data m_Writer->WriteData(header.str().c_str(), header.str().size()); } /** The write function is not implemented */ void VoxBoCUBImageIO ::Write( const void* buffer) { WriteImageInformation(); m_Writer->WriteData(buffer, GetImageSizeInBytes()); } /** Print Self Method */ void VoxBoCUBImageIO::PrintSelf(std::ostream& os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "PixelType " << m_PixelType << "\n"; } bool VoxBoCUBImageIO::CheckExtension(const char* filename, bool &isCompressed) { std::string fname = filename; if ( fname == "" ) { itkDebugMacro(<< "No filename specified."); return false; } bool extensionFound = false; isCompressed = false; std::string::size_type giplPos = fname.rfind(".cub"); if ((giplPos != std::string::npos) && (giplPos == fname.length() - 4)) { extensionFound = true; } giplPos = fname.rfind(".cub.gz"); if ((giplPos != std::string::npos) && (giplPos == fname.length() - 7)) { extensionFound = true; isCompressed = true; } return extensionFound; } void VoxBoCUBImageIO ::InitializeOrientationMap() { m_OrientationMap["RIP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIP; m_OrientationMap["LIP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIP; m_OrientationMap["RSP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSP; m_OrientationMap["LSP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSP; m_OrientationMap["RIA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIA; m_OrientationMap["LIA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIA; m_OrientationMap["RSA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSA; m_OrientationMap["LSA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSA; m_OrientationMap["IRP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRP; m_OrientationMap["ILP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILP; m_OrientationMap["SRP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRP; m_OrientationMap["SLP"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLP; m_OrientationMap["IRA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRA; m_OrientationMap["ILA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILA; m_OrientationMap["SRA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRA; m_OrientationMap["SLA"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLA; m_OrientationMap["RPI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI; m_OrientationMap["LPI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPI; m_OrientationMap["RAI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI; m_OrientationMap["LAI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAI; m_OrientationMap["RPS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPS; m_OrientationMap["LPS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPS; m_OrientationMap["RAS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAS; m_OrientationMap["LAS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAS; m_OrientationMap["PRI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRI; m_OrientationMap["PLI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLI; m_OrientationMap["ARI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARI; m_OrientationMap["ALI"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALI; m_OrientationMap["PRS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRS; m_OrientationMap["PLS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLS; m_OrientationMap["ARS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARS; m_OrientationMap["ALS"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALS; m_OrientationMap["IPR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPR; m_OrientationMap["SPR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPR; m_OrientationMap["IAR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAR; m_OrientationMap["SAR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAR; m_OrientationMap["IPL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPL; m_OrientationMap["SPL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPL; m_OrientationMap["IAL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAL; m_OrientationMap["SAL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAL; m_OrientationMap["PIR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIR; m_OrientationMap["PSR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSR; m_OrientationMap["AIR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIR; m_OrientationMap["ASR"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASR; m_OrientationMap["PIL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIL; m_OrientationMap["PSL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSL; m_OrientationMap["AIL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIL; m_OrientationMap["ASL"] = SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASL; OrientationMap::const_iterator it; for(it = m_OrientationMap.begin(); it != m_OrientationMap.end(); ++it) m_InverseOrientationMap[it->second] = it->first; } void VoxBoCUBImageIO ::SwapBytesIfNecessary(void *buffer, unsigned long numberOfBytes) { if(m_ComponentType == CHAR) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == UCHAR) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == SHORT) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == USHORT) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == INT) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == UINT) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == LONG) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == ULONG) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == FLOAT) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else if(m_ComponentType == DOUBLE) VoxBoCUBImageIOSwapHelper::SwapIfNecessary( buffer, numberOfBytes, m_ByteOrder); else { ExceptionObject exception(__FILE__, __LINE__); exception.SetDescription("Pixel Type Unknown"); throw exception; } } } // end namespace itk itksnap-3.4.0/Common/ITKExtras/itkVoxBoCUBImageIO.h000066400000000000000000000112351263013355200217020ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: itkVoxBoCUBImageIO.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:12 $ Version: $1.0$ Copyright (c) Insight Software Consortium. All rights reserved. This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkVoxBoCUBImageIO_h #define __itkVoxBoCUBImageIO_h #ifdef _MSC_VER #pragma warning ( disable : 4786 ) #endif #include #include #include #include "itkImageIOBase.h" #include "itkSpatialOrientation.h" #include namespace itk { class GenericCUBFileAdaptor; /** \class VoxBoCUBImageIO * * \brief Read VoxBoCUBImage file format. * * \ingroup IOFilters * */ class ITK_EXPORT VoxBoCUBImageIO : public ImageIOBase { public: /** Standard class typedefs. */ typedef VoxBoCUBImageIO Self; typedef ImageIOBase Superclass; typedef SmartPointer Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(VoxBoCUBImageIO, Superclass); /*-------- This part of the interfaces deals with reading data. ----- */ /** Determine the file type. Returns true if this ImageIO can read the * file specified. */ virtual bool CanReadFile(const char*) ; /** Set the spacing and dimension information for the set filename. */ virtual void ReadImageInformation(); /** Reads the data from disk into the memory buffer provided. */ virtual void Read(void* buffer); /*-------- This part of the interfaces deals with writing data. ----- */ /** Determine the file type. Returns true if this ImageIO can write the * file specified. */ virtual bool CanWriteFile(const char*); /** Set the spacing and dimension information for the set filename. */ virtual void WriteImageInformation(); /** Writes the data to disk from the memory buffer provided. Make sure * that the IORegions has been set properly. */ virtual void Write(const void* buffer); VoxBoCUBImageIO(); ~VoxBoCUBImageIO(); void PrintSelf(std::ostream& os, Indent indent) const; private: VoxBoCUBImageIO(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented bool CheckExtension(const char*, bool &isCompressed); GenericCUBFileAdaptor *CreateReader(const char *filename); GenericCUBFileAdaptor *CreateWriter(const char *filename); GenericCUBFileAdaptor *m_Reader, *m_Writer; // Initialize the orientation map (from strings to ITK) void InitializeOrientationMap(); // Orientation stuff typedef SpatialOrientation::ValidCoordinateOrientationFlags OrientationFlags; typedef std::map OrientationMap; typedef std::map InverseOrientationMap; OrientationMap m_OrientationMap; InverseOrientationMap m_InverseOrientationMap; // Method to swap bytes in read buffer void SwapBytesIfNecessary(void *buffer, unsigned long numberOfBytes); // Strings used in VoxBo files static const char *VB_IDENTIFIER_SYSTEM; static const char *VB_IDENTIFIER_FILETYPE; static const char *VB_DIMENSIONS; static const char *VB_SPACING; static const char *VB_ORIGIN; static const char *VB_DATATYPE; static const char *VB_BYTEORDER; static const char *VB_ORIENTATION; static const char *VB_BYTEORDER_MSB; static const char *VB_BYTEORDER_LSB; static const char *VB_DATATYPE_BYTE; static const char *VB_DATATYPE_INT; static const char *VB_DATATYPE_FLOAT; static const char *VB_DATATYPE_DOUBLE; }; } // end namespace itk #endif // __itkVoxBoCUBImageIO_h itksnap-3.4.0/Common/ITKExtras/itkVoxBoCUBImageIOFactory.cxx000066400000000000000000000042311263013355200236030ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: itkVoxBoCUBImageIOFactory.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:12 $ Version: $Revision: 1.2 $ Copyright (c) Insight Software Consortium. All rights reserved. This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #include "itkVoxBoCUBImageIOFactory.h" #include "itkCreateObjectFunction.h" #include "itkVoxBoCUBImageIO.h" #include "itkVersion.h" namespace itk { VoxBoCUBImageIOFactory::VoxBoCUBImageIOFactory() { this->RegisterOverride("itkImageIOBase", "itkVoxBoCUBImageIO", "VoxBo CUB Image IO", 1, CreateObjectFunction::New()); } VoxBoCUBImageIOFactory::~VoxBoCUBImageIOFactory() { } const char* VoxBoCUBImageIOFactory::GetITKSourceVersion(void) const { return ITK_SOURCE_VERSION; } const char* VoxBoCUBImageIOFactory::GetDescription() const { return "VoxBo CUB ImageIO Factory, allows the loading of VoxBoCUB images into Insight"; } } // end namespace itk itksnap-3.4.0/Common/ITKExtras/itkVoxBoCUBImageIOFactory.h000066400000000000000000000054131263013355200232330ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: itkVoxBoCUBImageIOFactory.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:12 $ Version: $Revision: 1.2 $ Copyright (c) Insight Software Consortium. All rights reserved. This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __itkVoxBoCUBImageIOFactory_h #define __itkVoxBoCUBImageIOFactory_h #include "itkObjectFactoryBase.h" #include "itkImageIOBase.h" namespace itk { /** \class VoxBoCUBImageIOFactory * \brief Create instances of VoxBoCUBImageIO objects using an object factory. */ class ITK_EXPORT VoxBoCUBImageIOFactory : public ObjectFactoryBase { public: /** Standard class typedefs. */ typedef VoxBoCUBImageIOFactory Self; typedef ObjectFactoryBase Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Class methods used to interface with the registered factories. */ virtual const char* GetITKSourceVersion(void) const; virtual const char* GetDescription(void) const; /** Method for class instantiation. */ itkFactorylessNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(VoxBoCUBImageIOFactory, ObjectFactoryBase); /** Register one factory of this type */ static void RegisterOneFactory(void) { VoxBoCUBImageIOFactory::Pointer VoxBoCUBFactory = VoxBoCUBImageIOFactory::New(); ObjectFactoryBase::RegisterFactory(VoxBoCUBFactory); } protected: VoxBoCUBImageIOFactory(); ~VoxBoCUBImageIOFactory(); private: VoxBoCUBImageIOFactory(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented }; } // end namespace itk #endif itksnap-3.4.0/Common/PresetManager.h000066400000000000000000000066301263013355200173320ustar00rootroot00000000000000#ifndef PRESETMANAGER_H #define PRESETMANAGER_H #include "itkObject.h" #include "SNAPCommon.h" class SystemInterface; /** A class that handles system and user presets for arbitrary data structures in ITK-SNAP. An example structure is the color map. The data structure must be described by a traits object. The traits object must define the following: // The type (derived from itk::Object) that is being managed typedef ManagedType; // The iterator type for the system presets typedef SystemPresetIterator; // The list of system presets static SystemPresetIterator SystemPresetBegin(); static SystemPresetIterator SystemPresetEnd(); // The unique name for this class of presets, used for storing in registry static std::string GetPresetCategoryName(); // Apply system preset static void SetToSystemPreset(ManagedType *instance, const SystemPresetIterator &preset); static std::string GetSystemPresetName(const SystemPresetIterator &preset); // Registry io for the managed type static void ReadFromRegistry(ManagedType *instance, Registry &folder); static void WriteToRegistry(ManagedType *instance, Registry &folder); TODO: we need a good way for synchronizing presets across multiple sessions. Currently, the list of presets is read at startup, and as the user saves and deletes presets, these operations are carried out on disk, without checking what another SNAP session might have done. The object fires a itk::ModifiedEvent event when presets have been modified */ template class PresetManager : public itk::Object { public: irisITKObjectMacro(PresetManager, itk::Object) typedef typename TManagedObjectTraits::ManagedType ManagedType; typedef SmartPtr ManagedTypePtr; typedef typename TManagedObjectTraits::SystemPresetIterator SystemPresetIterator; enum PresetType { PRESET_SYSTEM, PRESET_USER, PRESET_NONE }; typedef std::pair PresetMatch; /** Load the presets from disk and initialize them */ void Initialize(SystemInterface *si); /** Get the list of user and system presets */ const std::vector &GetSystemPresets() { return m_PresetSystem; } const std::vector &GetUserPresets() { return m_PresetUser; } /** * Query if the passed in instance of the object matches one of the presets, * and if so, which type of preset (system or user) */ PresetMatch QueryPreset(ManagedType *instance); /** Set the instance passed in to a preset */ void SetToPreset(ManagedType *instance, const std::string &preset); /** Save an instance as a new preset or override an existing preset */ void SaveAsPreset(ManagedType *instance, const std::string &preset); /** Delete a user preset */ void DeletePreset(const std::string &preset); /** Access a preset */ ManagedType *GetPreset(const std::string &preset); /** Whether a string is a valid preset */ bool IsValidPreset(const std::string &preset); protected: PresetManager(); virtual ~PresetManager() {} // Pointer to the system interface object used to manage user presets SystemInterface *m_System; // The name of the category std::string m_Category; // Map of presets to instances typedef std::map PresetMap; PresetMap m_PresetMap; // List of system and user presets std::vector m_PresetSystem, m_PresetUser; }; #endif // PRESETMANAGER_H itksnap-3.4.0/Common/PresetManager.hxx000066400000000000000000000104561263013355200177130ustar00rootroot00000000000000#include "PresetManager.h" #include "SystemInterface.h" #include "IRISException.h" #include template PresetManager ::PresetManager() { m_System = NULL; } template void PresetManager ::Initialize(SystemInterface *si) { // Store the system interface pointer m_System = si; m_Category = TManagedObjectTraits::GetPresetCategoryName().c_str(); // Create all the system presets m_PresetMap.clear(); m_PresetSystem.clear(); for(SystemPresetIterator i = TManagedObjectTraits::SystemPresetBegin(); i != TManagedObjectTraits::SystemPresetEnd(); i++) { std::string name = TManagedObjectTraits::GetSystemPresetName(i); ManagedTypePtr mtp = ManagedType::New(); TManagedObjectTraits::SetToSystemPreset(mtp, i); m_PresetMap[name] = mtp; m_PresetSystem.push_back(name); } // Load all the user preset names m_PresetUser = m_System->GetSavedObjectNames(m_Category.c_str()); // Load each of the presets from the registry for(int j = 0; j < m_PresetUser.size(); j++) { Registry reg = m_System->ReadSavedObject(m_Category.c_str(), m_PresetUser[j].c_str()); ManagedTypePtr mtp = ManagedType::New(); TManagedObjectTraits::ReadFromRegistry(mtp, reg); m_PresetMap[m_PresetUser[j]] = mtp; } this->Modified(); } template typename PresetManager::PresetMatch PresetManager ::QueryPreset(ManagedType *instance) { // TODO: we currently employ brute force linear search. This is probably // just fine, but could be done more cleanly using some sort of a hash key std::vector::const_iterator it; for(it = m_PresetSystem.begin(); it != m_PresetSystem.end(); it++) { if(*m_PresetMap[*it] == *instance) return std::make_pair(PRESET_SYSTEM, *it); } for(it = m_PresetUser.begin(); it != m_PresetUser.end(); it++) { if(*m_PresetMap[*it] == *instance) return std::make_pair(PRESET_USER, *it); } return std::make_pair(PRESET_NONE, std::string()); } template void PresetManager ::SetToPreset(ManagedType *instance, const std::string &preset) { typename PresetMap::iterator it = m_PresetMap.find(preset); if(it == m_PresetMap.end()) throw IRISException("Preset %s not found in category %s", preset.c_str(), m_Category.c_str()); instance->CopyInformation(it->second); } template void PresetManager ::SaveAsPreset(ManagedType *instance, const std::string &preset) { // Check that the name is not used for a system preset if(std::find(m_PresetSystem.begin(), m_PresetSystem.end(), preset) != m_PresetSystem.end()) throw IRISException( "%s is not a valid user preset name. It conflicts with a system preset", preset.c_str()); // Assign as a user preset if(std::find(m_PresetUser.begin(), m_PresetUser.end(), preset) == m_PresetUser.end()) m_PresetUser.push_back(preset); // Store the preset in memory ManagedTypePtr mtp = ManagedType::New(); mtp->CopyInformation(instance); m_PresetMap[preset] = mtp; // Write the preset to disk Registry reg; TManagedObjectTraits::WriteToRegistry(instance, reg); m_System->UpdateSavedObject(m_Category.c_str(), preset.c_str(), reg); this->Modified(); } template void PresetManager ::DeletePreset(const std::string &preset) { // Assign as a user preset std::vector::iterator it = std::find(m_PresetUser.begin(), m_PresetUser.end(), preset); if(it != m_PresetUser.end()) { // Delete the preset from the file system m_System->DeleteSavedObject(m_Category.c_str(), preset.c_str()); // Also delete it from the list of user presets m_PresetUser.erase(it); } this->Modified(); } template typename PresetManager::ManagedType * PresetManager ::GetPreset(const std::string &preset) { return m_PresetMap[preset]; } template bool PresetManager ::IsValidPreset(const std::string &preset) { return m_PresetMap.find(preset) != m_PresetMap.end(); } itksnap-3.4.0/Common/PropertyModel.h000066400000000000000000001164571263013355200174130ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef EDITABLENUMERICVALUEMODEL_H #define EDITABLENUMERICVALUEMODEL_H #include #include #include "AbstractModel.h" #include /** This class represents the range information associated with a numeric value. This range information is used to set up the GUI controls with which the user interacts. */ template struct NumericValueRange { typedef NumericValueRange Self; // These values define a numeric value range TVal Minimum, Maximum, StepSize; NumericValueRange(TVal min, TVal max, TVal step) : Minimum(min), Maximum(max), StepSize(step) {} NumericValueRange(TVal min, TVal max) : Minimum(min), Maximum(max) { StepSize = (TVal) 0; } NumericValueRange() { Minimum = static_cast(0); Maximum = (TVal) 0; StepSize = (TVal) 0; } NumericValueRange(const NumericValueRange &ref) { Minimum = ref.Minimum; Maximum = ref.Maximum; StepSize = ref.StepSize; } void Set(TVal min, TVal max, TVal step) { Minimum = min; Maximum = max; StepSize = step; } bool operator == (const Self &comp) { return (Minimum == comp.Minimum) && (Maximum == comp.Maximum) && (StepSize == comp.StepSize); } bool operator != (const Self &comp) { return (Minimum != comp.Minimum) || (Maximum != comp.Maximum) || (StepSize != comp.StepSize); } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. virtual bool isAtomic() { return true; } }; /** This computes a step size that is a power of 10 */ inline double CalculatePowerOfTenStepSize(double min, double max, size_t minNumSteps) { double stepUB = (max - min) / minNumSteps; return pow(10, floor(log10(stepUB))); } /** An abstract parent type for models that allow random access to items of some type. This abstract class is agnostic to the actual storage type of the source container. For implementations, see RandomAccessCollectionModel TODO: reconcile this with domains! */ template class AbstractRandomAccessCollectionModel : public AbstractModel { public: typedef TItem ItemType; virtual unsigned int GetSize() = 0; virtual TItem operator[] (unsigned int n) = 0; }; /** This class represents a domain that allows all values in a data type. It can be used with the class AbstractPropertyModel when there is no need to communicate domain information to the widget */ class TrivialDomain { public: bool operator == (const TrivialDomain &cmp) { return true; } bool operator != (const TrivialDomain &cmp) { return false; } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. bool isAtomic() { return true; } }; /** Another type of a domain is a set of items/options from which the user is able to choose. Examples can be lists of strings, lists of color labels, and so on. The value is of type TVal, but this is not necessarily the information that it presented to the user. For example, in a color label chooser, the value held by a property is the ID of the label, but the user is shown the color and the description of the label. The signature for this type of domain consists of a const_iterator typedef, begin() and end() methods that return a const_iterator(), the method GetValue(it) which returns the numeric value associated with an iterator and the method GetDescription(it), which returns the information used by the GUI to present the choice to the user. The actual implementations of this domain are normally wrappers around STL structures. */ template class AbstractItemSetDomain { public: typedef TIterator const_iterator; typedef TVal ValueType; typedef TDesc DescriptorType; virtual const_iterator begin() const = 0; virtual const_iterator end() const = 0; virtual const_iterator find(const TVal &value) const = 0; virtual TVal GetValue(const const_iterator &it) const = 0; virtual TDesc GetDescription(const const_iterator &it) const = 0; virtual ~AbstractItemSetDomain() {} }; /** This is an implementation of the domain that wraps around an stl::map from values to descriptors. The map is not stored in the domain, but referenced from another object to avoid duplicating data. */ template class STLMapWrapperItemSetDomain : public AbstractItemSetDomain::const_iterator> { public: typedef STLMapWrapperItemSetDomain Self; typedef typename std::map MapType; typedef typename MapType::const_iterator const_iterator; STLMapWrapperItemSetDomain() { m_SourceMap = NULL; } STLMapWrapperItemSetDomain(const MapType *refmap) { m_SourceMap = refmap; } virtual ~STLMapWrapperItemSetDomain() {} const_iterator begin() const { assert(m_SourceMap); return m_SourceMap->begin(); } const_iterator end() const { assert(m_SourceMap); return m_SourceMap->end(); } const_iterator find(const TVal &value) const { assert(m_SourceMap); return m_SourceMap->find(value); } TVal GetValue(const const_iterator &it) const { return it->first; } TDesc GetDescription(const const_iterator &it) const { return it->second; } void SetWrappedMap(const MapType *refmap) { m_SourceMap = refmap; } virtual bool operator == (const Self &cmp) const { return m_SourceMap == cmp.m_SourceMap; } virtual bool operator != (const Self &cmp) const { return m_SourceMap != cmp.m_SourceMap; } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. virtual bool isAtomic() { return false; } protected: const MapType *m_SourceMap; }; /** This is an implementation of the domain that wraps around an stl::vector of descriptors. TVal should be an integer type that can be used as an index (int, unsigned int, enum, etc) */ template class STLVectorWrapperItemSetDomain : public AbstractItemSetDomain::const_iterator> { public: typedef STLVectorWrapperItemSetDomain Self; typedef typename std::vector VectorType; typedef typename VectorType::const_iterator const_iterator; STLVectorWrapperItemSetDomain() { m_SourceVector = NULL; } STLVectorWrapperItemSetDomain(const VectorType *refvec) { m_SourceVector = refvec; } virtual ~STLVectorWrapperItemSetDomain() {} const_iterator begin() const { assert(m_SourceVector); return m_SourceVector->begin(); } const_iterator end() const { assert(m_SourceVector); return m_SourceVector->end(); } const_iterator find(const TVal &value) const { assert(m_SourceVector); return m_SourceVector->begin() + value; } TVal GetValue(const const_iterator &it) const { assert(m_SourceVector); return it - m_SourceVector->begin(); } TDesc GetDescription(const const_iterator &it) const { assert(m_SourceVector); return *it; } virtual bool operator == (const Self &cmp) const { return m_SourceVector == cmp.m_SourceVector; } virtual bool operator != (const Self &cmp) const { return m_SourceVector != cmp.m_SourceVector; } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. virtual bool isAtomic() { return false; } protected: const VectorType *m_SourceVector; }; /** This is an item domain implementation that is just an stl::map, i.e., it owns the data, as opposed to STLMapWrapperItemSetDomain, which references the data from another map. This implementation is useful for small domains where there is no cost in passing the domain by value. This version does not support comparison between domains, and can not be used in applications where the domain needs to be cached */ template class SimpleNonAtomicItemSetDomain : public AbstractItemSetDomain::const_iterator> { public: typedef std::map MapType; typedef typename MapType::const_iterator const_iterator; typedef SimpleNonAtomicItemSetDomain Self; typedef AbstractItemSetDomain Superclass; SimpleNonAtomicItemSetDomain() : Superclass() { } const_iterator begin() const { return m_Map.begin(); } const_iterator end() const { return m_Map.end(); } const_iterator find(const TVal &value) const { return m_Map.find(value); } void clear() { m_Map.clear(); } TVal GetValue(const const_iterator &it) const { return it->first; } TDesc GetDescription(const const_iterator &it) const { return it->second; } unsigned int size() const { return m_Map.size(); } // Standard stl::map operator TDesc & operator [] (const TVal &key) { return m_Map[key]; } const TDesc & operator [] (const TVal &key) const { return m_Map[key]; } // Bogus - just compares to self virtual bool operator == (const Self &cmp) const { return &m_Map == &cmp.m_Map; } // Bogus - just compares to self virtual bool operator != (const Self &cmp) const { return &m_Map != &cmp.m_Map; } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. This class is // not atomic because it does not support comparisons virtual bool isAtomic() { return false; } protected: MapType m_Map; }; /** This is an item domain implementation that is just an stl::map, i.e., it owns the data, as opposed to STLMapWrapperItemSetDomain, which references the data from another map. This implementation is useful for small domains where there is no cost in passing the domain by value. */ template class SimpleItemSetDomain : public SimpleNonAtomicItemSetDomain { public: typedef std::map MapType; typedef typename MapType::const_iterator const_iterator; typedef SimpleItemSetDomain Self; typedef SimpleNonAtomicItemSetDomain Superclass; SimpleItemSetDomain() : Superclass() { } virtual bool operator == (const Self &cmp) const { return this->m_Map == cmp.m_Map; } virtual bool operator != (const Self &cmp) const { return this->m_Map != cmp.m_Map; } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. virtual bool isAtomic() { return true; } }; /** * States that can be checked for property models. We place this enum outside * of the class AbstractPropertyModel because this class is templated. This * enum is meant to be used with the SNAPUIFlag framework. */ enum PropertyModelUIState { // Indicates that the UIF_PROPERTY_IS_VALID = 0 }; /** A parent class for a family of models that encapsulate a single value of a particular type. These models use events to communicate changes in state and can be coupled to GUI widgets to allow seamless connections between the GUI and the model layer. The model is parameterized by the data type of the value (TVal), which would normally be a number, a string, a vector, etc. It is also parameterized by the domain type, which describes the set of values from which the value is drawn. The domain is used to configure GUI widgets to that the user is restricted to choosing a value in a valid range. For example, for TVal=double, the natural domain is the NumericValueRange class, consisting of a min, max and a step size. For TVal=string, the natural domain is a set of strings. In addition to supplying a value for the encapsulated property, the model can return a boolean flag as to whether the model/property is in a valid state. For example, a model describing the minumum intensity of an image would be in an invalid state if there is no image currently loaded. The corresponding GUI widget can then be set to indicate that the value is invalid or null. This type of model is meant to be matched to a widget in the GUI. Since the number of widgets is small (10s or 100s), it is acceptable for these models to be somewhat heavyweight. They inherit from AbstractModel, which in turn inherits from itk::Object. */ template class AbstractPropertyModel : public AbstractModel { public: irisITKAbstractObjectMacro(AbstractPropertyModel, AbstractModel) /** The atomic type encompassed by the model */ typedef TVal ValueType; /** The type of the domain */ typedef TDomain DomainType; /** The model fires two types of events: ValueChangedEvent and DomainChangedEvent, in response to either the value or the domain having changed. */ FIRES(ValueChangedEvent) FIRES(DomainChangedEvent) FIRES(StateMachineChangeEvent) /** A setter method */ virtual void SetValue(TVal value) = 0; /** A compound getter method exposed by the model. Return false if the value is not valid, and the corresponding control should show a blank string instead of a value. If the domain is not needed, a NULL pointer will be passed in. If domain is needed, the current values stored in the GUI widget will be passed in. If the domain is not handled by the model (i.e., a fixed range set in the GUI designer once and for all), the callback can just ignore the domain parameter. */ virtual bool GetValueAndDomain(TVal &value, TDomain *domain) = 0; /** A getter with a simple signature. Not meant to be overridden by the child class. */ TVal GetValue() { TVal value; GetValueAndDomain(value, NULL); return value; } /** * The model can participate in the state management mechanism with SNAPUIFlag. * At the moment, the only flag available is the Validity flag. */ bool CheckState(PropertyModelUIState flag) { if(flag == UIF_PROPERTY_IS_VALID) { TVal value; return GetValueAndDomain(value, NULL); } else return false; } /** Sometimes it is useful to have the model rebroadcast value and domain change events from another model. An example may be a model that wraps around another model, e.g., if model A is of a compound type T and model B is used to access an attribute T.x in T, then we want the value change events in A to be rebroadcast as value change events in B. This function simplifies making this connection */ void RebroadcastFromSourceProperty(AbstractModel *source) { Rebroadcast(source, ValueChangedEvent(), ValueChangedEvent()); Rebroadcast(source, DomainChangedEvent(), DomainChangedEvent()); } protected: AbstractPropertyModel() { Rebroadcast(this, ValueChangedEvent(), StateMachineChangeEvent()); Rebroadcast(this, DomainChangedEvent(), StateMachineChangeEvent()); } }; /** A concrete implementation of AbstractPropertyModel that holds the value, the validity flag, and the domain as private variables. The validity flag is initialized to true. The parent model is responsible for setting the value, domain, and validity flag inside of this concrete model. */ template class ConcretePropertyModel : public AbstractPropertyModel { public: // Standard ITK stuff typedef ConcretePropertyModel Self; typedef AbstractPropertyModel Superclass; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkTypeMacro(ConcretePropertyModel, AbstractPropertyModel) itkNewMacro(Self) virtual bool GetValueAndDomain(TVal &value, TDomain *domain) { value = m_Value; if(domain) *domain = m_Domain; return m_IsValid; } irisSetWithEventMacro(Value, TVal, ValueChangedEvent) irisSetWithEventMacro(Domain, TDomain, DomainChangedEvent) irisSetWithEventMacro(IsValid, bool, ValueChangedEvent) // Simple implementation of the deep copy function void DeepCopy(const Self *source) { // Copy the relevant stuff this->SetValue(source->m_Value); this->SetDomain(source->m_Domain); this->SetIsValid(source->m_IsValid); } /** Compare with another model (by value only, not domain) */ bool Equals(const Self *source) const { // Cast to the right type return(source->m_Value == m_Value && source->m_IsValid == m_IsValid); } protected: ConcretePropertyModel() : m_Value(TVal()), m_Domain(TDomain()), m_IsValid(true) {} virtual ~ConcretePropertyModel() {} TVal m_Value; TDomain m_Domain; bool m_IsValid; }; // A macro to generate functions GetXXX(), SetXXX() and GetXXXModel() in a class // that contains a ConcretePropertyModel of a certain type named m_XXXModel #define irisRangedPropertyAccessMacro(name,type) \ virtual void Set##name (type _arg) \ { this->m_##name##Model->SetValue(_arg); } \ virtual type Get##name () const \ { return this->m_##name##Model->GetValue(); } \ virtual AbstractPropertyModel > * Get##name##Model () const \ { return this->m_##name##Model; } #define irisSimplePropertyAccessMacro(name,type) \ virtual void Set##name (type _arg) \ { this->m_##name##Model->SetValue(_arg); } \ virtual type Get##name () const \ { return this->m_##name##Model->GetValue(); } \ virtual AbstractPropertyModel * Get##name##Model () const \ { return this->m_##name##Model; } #define irisGenericPropertyAccessMacro(name,type,domaintype) \ virtual void Set##name (type _arg) \ { this->m_##name##Model->SetValue(_arg); } \ virtual type Get##name () const \ { return this->m_##name##Model->GetValue(); } \ virtual AbstractPropertyModel * Get##name##Model () const \ { return this->m_##name##Model; } // A factory function to initialize properties - again, for shorter code template SmartPtr< ConcretePropertyModel > > NewRangedConcreteProperty(TVal val, TVal rmin, TVal rmax, TVal rstep) { typedef ConcretePropertyModel > Prop; SmartPtr p = Prop::New(); p->SetValue(val); p->SetDomain(NumericValueRange(rmin, rmax, rstep)); return p; } template SmartPtr< ConcretePropertyModel > NewSimpleConcreteProperty(TVal val) { typedef ConcretePropertyModel Prop; SmartPtr p = Prop::New(); p->SetValue(val); return p; } template SmartPtr< ConcretePropertyModel > NewConcreteProperty(TVal val, TDomain domain) { typedef ConcretePropertyModel Prop; SmartPtr p = Prop::New(); p->SetValue(val); p->SetDomain(domain); return p; } /** This class is only used to define some typedefs. It allows us to write AbstractRangedPropertyModel::Type as shorthand for AbstractPropertyModel > */ template class AbstractRangedPropertyModel { public: typedef NumericValueRange DomainType; typedef AbstractPropertyModel Type; }; template class ConcreteRangedPropertyModel { public: typedef NumericValueRange DomainType; typedef ConcretePropertyModel Type; }; /* Make some typedefs. The macros below define types AbstractSimpleXXXProperty AbstractRangedXXXProperty ConcreteSimpleXXXProperty ConcreteRangedXXXProperty */ #define MAKE_TYPEDEF_PM_RANGED(type,name) \ typedef AbstractPropertyModel< type > AbstractSimple##name##Property; \ typedef AbstractRangedPropertyModel< type >::Type AbstractRanged##name##Property; \ typedef ConcretePropertyModel< type > ConcreteSimple##name##Property; \ typedef ConcreteRangedPropertyModel< type >::Type ConcreteRanged##name##Property; #define MAKE_TYPEDEF_PM_NONRNG(type,name) \ typedef AbstractPropertyModel< type > AbstractSimple##name##Property; \ typedef ConcretePropertyModel< type > ConcreteSimple##name##Property; // Macros for standard types that support ranges MAKE_TYPEDEF_PM_RANGED(double, Double) MAKE_TYPEDEF_PM_RANGED(float, Float) MAKE_TYPEDEF_PM_RANGED(long, Long) MAKE_TYPEDEF_PM_RANGED(unsigned long, ULong) MAKE_TYPEDEF_PM_RANGED(int, Int) MAKE_TYPEDEF_PM_RANGED(unsigned int, UInt) MAKE_TYPEDEF_PM_RANGED(short, Short) MAKE_TYPEDEF_PM_RANGED(unsigned short, UShort) MAKE_TYPEDEF_PM_RANGED(char, Char) MAKE_TYPEDEF_PM_RANGED(unsigned char, UChar) MAKE_TYPEDEF_PM_RANGED(bool, Boolean) // Common SNAP typedefs MAKE_TYPEDEF_PM_RANGED(LabelType, LabelType) // Common vector types MAKE_TYPEDEF_PM_RANGED(Vector2d, DoubleVec2) MAKE_TYPEDEF_PM_RANGED(Vector3d, DoubleVec3) MAKE_TYPEDEF_PM_RANGED(Vector2i, IntVec2) MAKE_TYPEDEF_PM_RANGED(Vector3i, IntVec3) MAKE_TYPEDEF_PM_RANGED(Vector2ui, UIntVec2) MAKE_TYPEDEF_PM_RANGED(Vector3ui, UIntVec3) MAKE_TYPEDEF_PM_RANGED(Vector2b, BooleanVec2) MAKE_TYPEDEF_PM_RANGED(Vector3b, BooleanVec3) // Macros for non-ranged types MAKE_TYPEDEF_PM_NONRNG(std::string, String) /** An implementation of the AbstractPropertyModel that serves as a wrapper around a getter function and a setter function, both members of a parent model class. The primary use for this class is to make it easy to create AbstractPropertyModels that describe a derived property. The parent model must include a getter with one of the following three signatures. bool GetValueAndDomain(TVal &value, TDomain *domain) bool GetValue(TVal &value) TVal GetValue() It may also optionally include a setter method with the signature void SetValue(TVal value) In addition to value and domain, this class is templated over the parent model type (TModel) and the traits object describing the signature of the getter. This is because we want to support all three of the getter signatures without having to check which one the user supplied dynamically. Note that this class is not meant to be used directly in the GUI model code. Instead, one should make use of the function wrapGetterSetterPairAsProperty, which serves as a factory for creating models of this type. */ template class FunctionWrapperPropertyModel : public AbstractPropertyModel { public: // Standard ITK stuff (can't use irisITKObjectMacro because of two template // parameters, comma breaks the macro). typedef FunctionWrapperPropertyModel Self; typedef AbstractPropertyModel Superclass; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkTypeMacro(FunctionWrapperPropertyModel, AbstractPropertyModel) itkNewMacro(Self) // Function pointers to a setter method typedef typename SetterTraits::Setter Setter; // The function pointer to the getter is provided by the traits typedef typename GetterTraits::Getter Getter; /** Initializes a model with a parent model and function pointers */ void Initialize(TModel *model, Getter getter, Setter setter = NULL) { m_Model = model; m_Getter = getter; m_Setter = setter; } /** Get a reference to the getter traits */ GetterTraits &GetGetterTraits() { return m_GetterTraits; } /** Get a reference to the setter traits */ SetterTraits &GetSetterTraits() { return m_SetterTraits; } /** Set up the events fired by the parent model that this model should listen to and rebroadcast as ValueChangedEvent and DomainChangedEvent. */ void SetEvents(const itk::EventObject &valueEvent, const itk::EventObject &rangeEvent) { this->Rebroadcast(m_Model, valueEvent, ValueChangedEvent()); this->Rebroadcast(m_Model, rangeEvent, DomainChangedEvent()); } bool GetValueAndDomain(TVal &value, TDomain *domain) { // This is important! Before calling the getter function, we should allow // the model to respond to whatever events it may have received that led // to this data request. Otherwise, we would have to make an Update() // call in each of the Getter functions we write. m_Model->Update(); // Call the getter function with the help of the traits object return m_GetterTraits.GetValueAndDomain(m_Model, m_Getter, value, domain); } void SetValue(TVal value) { if(m_Setter) { static_cast(m_Model)->Update(); m_SetterTraits.SetValue(m_Model, m_Setter, value); } } /** A factory method used to create new models of this type. The user should not need to call this directly, instead use the wrapGetterSetterPairAsProperty methods below. The last two paremeters are the events fired by the parent model that should be rebroadcast as the value and domain change events by the property model. */ static SmartPtr CreatePropertyModel( TModel *parentModel, Getter getter, Setter setter, const itk::EventObject &valueEvent, const itk::EventObject &rangeEvent, GetterTraits getterTraits = GetterTraits(), SetterTraits setterTraits = SetterTraits()) { SmartPtr p = Self::New(); p->Initialize(parentModel, getter, setter); p->SetEvents(valueEvent, rangeEvent); p->m_SetterTraits = setterTraits; p->m_GetterTraits = getterTraits; // p->UnRegister(); SmartPtr pout(p); return pout; } protected: TModel *m_Model; Getter m_Getter; Setter m_Setter; GetterTraits m_GetterTraits; SetterTraits m_SetterTraits; FunctionWrapperPropertyModel() : m_Model(NULL), m_Getter(NULL), m_Setter(NULL) {} ~FunctionWrapperPropertyModel() {} }; /** Getter traits for the FunctionWrapperPropertyModel that use the compound getter signature, bool GetValueAndDomain(TVal &value, TDomain &domain); */ template class FunctionWrapperPropertyModelCompoundGetterTraits { public: // Signature of the getter typedef bool (TModel::*Getter)(TVal &t, TDomain *domain); // Implementation of the get method, just calls the getter directly bool GetValueAndDomain( TModel *model, Getter getter, TVal &value, TDomain *domain) { return ((*model).*(getter))(value, domain); } }; /** Getter traits for the FunctionWrapperPropertyModel that use the rangeless getter signature, bool GetValue(TVal &value); */ template class FunctionWrapperPropertyModelRangelessGetterTraits { public: // Signature of the getter typedef bool (TModel::*Getter)(TVal &t); // Implementation of the get method, just calls the getter directly bool GetValueAndDomain( TModel *model, Getter getter, TVal &value, TDomain *domain) { return ((*model).*(getter))(value); } }; /** Getter traits for the FunctionWrapperPropertyModel that use the simple getter signature, TVal GetValue(); */ template class FunctionWrapperPropertyModelSimpleGetterTraits { public: // Signature of the getter typedef TVal (TModel::*Getter)(); // Implementation of the get method, just calls the getter directly bool GetValueAndDomain( TModel *model, Getter getter, TVal &value, TDomain *domain) { value = ((*model).*(getter))(); return true; } }; /** Basic setter traits */ template class FunctionWrapperPropertyModelSimpleSetterTraits { public: // Signature of the getter typedef void (TModel::*Setter)(TVal value); // Implementation of the get method, just calls the getter directly void SetValue( TModel *model, Setter getter, TVal &value) { ((*model).*(getter))(value); } }; class FunctionWrapperPropertyModelIndexedTraits { public: void SetIndex(int index) { m_Index = index; } int GetIndex() const { return m_Index; } protected: int m_Index; }; /** Getter traits for the FunctionWrapperPropertyModel that use the compound getter signature, with a parameter variable i. This is useful when we need to create an array of models that wrap around function of the form bool GetValueAndDomain(int i, TVal &value, TDomain &domain); */ template class FunctionWrapperPropertyModelIndexedCompoundGetterTraits : public FunctionWrapperPropertyModelIndexedTraits { public: // Signature of the getter typedef bool (TModel::*Getter)(int i, TVal &t, TDomain *domain); // Implementation of the get method, just calls the getter directly bool GetValueAndDomain( TModel *model, Getter getter, TVal &value, TDomain *domain) { return ((*model).*(getter))(GetIndex(), value, domain); } }; template class FunctionWrapperPropertyModelIndexedRangelessGetterTraits : public FunctionWrapperPropertyModelIndexedTraits { public: // Signature of the getter typedef bool (TModel::*Getter)(int index, TVal &t); // Implementation of the get method, just calls the getter directly bool GetValueAndDomain( TModel *model, Getter getter, TVal &value, TDomain *domain) { return ((*model).*(getter))(GetIndex(), value); } }; /** Indexed setter traits */ template class FunctionWrapperPropertyModelIndexedSimpleSetterTraits : public FunctionWrapperPropertyModelIndexedTraits { public: // Signature of the getter typedef void (TModel::*Setter)(int index, TVal value); // Implementation of the get method, just calls the getter directly void SetValue( TModel *model, Setter getter, TVal &value) { ((*model).*(getter))(GetIndex(), value); } }; /** This code creates an AbstractPropertyModel that wraps around a pair of functions (a getter and a setter) in the parent model object. There are three versions of this function, corresponding to different signatures of the getter function. */ template SmartPtr< AbstractPropertyModel > wrapGetterSetterPairAsProperty( TModel *model, bool (TModel::*getter)(TVal &, TDomain *), void (TModel::*setter)(TVal) = NULL, const itk::EventObject &valueEvent = ModelUpdateEvent(), const itk::EventObject &rangeEvent = ModelUpdateEvent()) { typedef FunctionWrapperPropertyModelCompoundGetterTraits< TVal, TDomain, TModel> GetterTraitsType; typedef FunctionWrapperPropertyModelSimpleSetterTraits< TVal, TModel> SetterTraitsType; typedef FunctionWrapperPropertyModel< TVal, TDomain, TModel, GetterTraitsType, SetterTraitsType> ModelType; return ModelType::CreatePropertyModel(model, getter, setter, valueEvent, rangeEvent); } template SmartPtr< AbstractPropertyModel > wrapGetterSetterPairAsProperty( TModel *model, bool (TModel::*getter)(TVal &), void (TModel::*setter)(TVal) = NULL, const itk::EventObject &valueEvent = ModelUpdateEvent(), const itk::EventObject &rangeEvent = ModelUpdateEvent()) { typedef FunctionWrapperPropertyModelRangelessGetterTraits< TVal, TrivialDomain, TModel> GetterTraitsType; typedef FunctionWrapperPropertyModelSimpleSetterTraits< TVal, TModel> SetterTraitsType; typedef FunctionWrapperPropertyModel< TVal, TrivialDomain, TModel, GetterTraitsType, SetterTraitsType> ModelType; return ModelType::CreatePropertyModel(model, getter, setter, valueEvent, rangeEvent); } template SmartPtr< AbstractPropertyModel > wrapGetterSetterPairAsProperty( TModel *model, TVal (TModel::*getter)(), void (TModel::*setter)(TVal) = NULL, const itk::EventObject &valueEvent = ModelUpdateEvent(), const itk::EventObject &rangeEvent = ModelUpdateEvent()) { typedef FunctionWrapperPropertyModelSimpleGetterTraits< TVal, TrivialDomain, TModel> GetterTraitsType; typedef FunctionWrapperPropertyModelSimpleSetterTraits< TVal, TModel> SetterTraitsType; typedef FunctionWrapperPropertyModel< TVal, TrivialDomain, TModel, GetterTraitsType, SetterTraitsType> ModelType; return ModelType::CreatePropertyModel(model, getter, setter, valueEvent, rangeEvent); } /** A version of wrapGetterSetterPairAsProperty that creates a model that wraps around a function GetXXXValueAndDomain(int i, TVal &value, TDomain *). This is useful when we want to create multiple models that wrap around the same getter/setter function. */ template SmartPtr< AbstractPropertyModel > wrapIndexedGetterSetterPairAsProperty( TModel *model, int index, bool (TModel::*getter)(int, TVal &, TDomain *), void (TModel::*setter)(int, TVal) = NULL, const itk::EventObject &valueEvent = ModelUpdateEvent(), const itk::EventObject &rangeEvent = ModelUpdateEvent()) { typedef FunctionWrapperPropertyModelIndexedCompoundGetterTraits< TVal, TDomain, TModel> GetterTraitsType; typedef FunctionWrapperPropertyModelIndexedSimpleSetterTraits< TVal, TModel> SetterTraitsType; typedef FunctionWrapperPropertyModel< TVal, TDomain, TModel, GetterTraitsType, SetterTraitsType> ModelType; // Assign the index to the traits GetterTraitsType getterTraits; getterTraits.SetIndex(index); SetterTraitsType setterTraits; setterTraits.SetIndex(index); // Create the property model return ModelType::CreatePropertyModel(model, getter, setter, valueEvent, rangeEvent, getterTraits, setterTraits); } template SmartPtr< AbstractPropertyModel > wrapIndexedGetterSetterPairAsProperty( TModel *model, int index, bool (TModel::*getter)(int, TVal &), void (TModel::*setter)(int, TVal) = NULL, const itk::EventObject &valueEvent = ModelUpdateEvent(), const itk::EventObject &rangeEvent = ModelUpdateEvent()) { typedef FunctionWrapperPropertyModelIndexedRangelessGetterTraits< TVal, TrivialDomain, TModel> GetterTraitsType; typedef FunctionWrapperPropertyModelIndexedSimpleSetterTraits< TVal, TModel> SetterTraitsType; typedef FunctionWrapperPropertyModel< TVal, TrivialDomain, TModel, GetterTraitsType, SetterTraitsType> ModelType; // Assign the index to the traits GetterTraitsType getterTraits; getterTraits.SetIndex(index); SetterTraitsType setterTraits; setterTraits.SetIndex(index); return ModelType::CreatePropertyModel(model, getter, setter, valueEvent, rangeEvent, getterTraits, setterTraits); } /** * This model is used to decorate a member in a C++ struct as a property. This * is useful when we have a property of type X, and we want to be able to access * X.a as a property. */ template class StructMemberWrapperPropertyModel : public AbstractPropertyModel { public: // Standard ITK stuff (can't use irisITKObjectMacro because of two template // parameters, comma breaks the macro). typedef StructMemberWrapperPropertyModel Self; typedef AbstractPropertyModel Superclass; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkTypeMacro(StructMemberWrapperPropertyModel, AbstractPropertyModel) itkNewMacro(Self) // Set the source model (the one that manages the TStruct) typedef AbstractPropertyModel ParentModel; void Initialize(ParentModel *model, size_t offset, const TDomain &domain) { m_ParentModel = model; m_MemberOffset = offset; m_Domain = domain; // Respond to value change events AbstractModel::Rebroadcast(m_ParentModel, ValueChangedEvent(), ValueChangedEvent()); } bool GetValueAndDomain(TMember &value, TDomain *domain) { TStruct parentValue; if(m_ParentModel && m_ParentModel->GetValueAndDomain(parentValue, NULL)) { // Get the value of the member based on the offset TMember *valuePtr = reinterpret_cast( reinterpret_cast(&parentValue) + m_MemberOffset); value = *valuePtr; if(domain) *domain = m_Domain; return true; } return false; } void SetValue(TMember value) { TStruct parentValue; if(m_ParentModel && m_ParentModel->GetValueAndDomain(parentValue, NULL)) { // Get the value of the member based on the offset TMember *valuePtr = reinterpret_cast( reinterpret_cast(&parentValue) + m_MemberOffset); // Set the value if(*valuePtr != value) { *valuePtr = value; m_ParentModel->SetValue(parentValue); } } } static SmartPtr CreatePropertyModel( ParentModel *parentModel, size_t offset, TDomain domain) { SmartPtr p = Self::New(); p->Initialize(parentModel, offset, domain); // p->UnRegister(); SmartPtr pout(p); return pout; } protected: StructMemberWrapperPropertyModel() : m_ParentModel(NULL), m_MemberOffset(0) {} ParentModel *m_ParentModel; size_t m_MemberOffset; TDomain m_Domain; }; template SmartPtr< AbstractPropertyModel > wrapStructMemberAsSimpleProperty( AbstractPropertyModel *parentModel, size_t offset) { typedef StructMemberWrapperPropertyModel ModelType; TrivialDomain td; return ModelType::CreatePropertyModel(parentModel, offset, td); } template SmartPtr< AbstractPropertyModel > > wrapStructMemberAsRangedProperty( AbstractPropertyModel *parentModel, size_t offset, const NumericValueRange &domain) { typedef StructMemberWrapperPropertyModel > ModelType; return ModelType::CreatePropertyModel(parentModel, offset, domain); } #endif // EDITABLENUMERICVALUEMODEL_H itksnap-3.4.0/Common/Rebroadcaster.cxx000066400000000000000000000170631263013355200177320ustar00rootroot00000000000000#include "Rebroadcaster.h" #include "SNAPEventListenerCallbacks.h" #include "SNAPCommon.h" #include "EventBucket.h" Rebroadcaster::DispatchMap Rebroadcaster::m_SourceMap; Rebroadcaster::DispatchMap Rebroadcaster::m_TargetMap; unsigned long Rebroadcaster ::Rebroadcast(itk::Object *source, const itk::EventObject &sourceEvent, itk::Object *target, const itk::EventObject &targetEvent, EventBucket *bucket) { // TODO: for now, we allow the user to call this method twice with the same // input without checking if the rebroadcast has already been set up. This // might cause some issues if users are not careful. Association *assoc = new Association(source, target, targetEvent); // Add listeners to the source object, unless the source event is a delete // event, in which case, we already are going to register for it in order // to delete the associate when the source is deleted. assoc->m_IsForDeleteEvent = itk::DeleteEvent().CheckEvent(&sourceEvent); if(!assoc->m_IsForDeleteEvent) { assoc->m_SourceTag = AddListenerPair( source, sourceEvent, assoc, &Association::Callback, &Association::ConstCallback); } // Pass the bucket pointer assoc->m_Bucket = bucket; #ifdef SNAP_DEBUG_EVENTS if(flag_snap_debug_events) { std::cout << "ESTABLISHED REBROADCAST event " << sourceEvent.GetEventName() << " from " << source->GetNameOfClass() << " [" << source << "] " << " as " << targetEvent.GetEventName() << " from " << target->GetNameOfClass() << " [" << target << "] " << std::endl << std::flush; } #endif // SNAP_DEBUG_EVENTS // This can explode if the target object is deleted and the source object // fires an event. One solution would be to require the target object to // stop broadcasting before being deleted. However, itk sends us delete // callbacks for all objects, so we can use these callbacks to uncouple // everything when the source or target are deleted. // Is this a known target object? if(m_TargetMap.find(target) == m_TargetMap.end()) { // Target object is new. Register to receive delete events from it SmartPtr cmd_target_delete = itk::CStyleCommand::New(); cmd_target_delete->SetCallback(&Rebroadcaster::DeleteTargetCallback); cmd_target_delete->SetConstCallback(&Rebroadcaster::DeleteTargetConstCallback); target->AddObserver(itk::DeleteEvent(), cmd_target_delete); } // Add the association to the target's list m_TargetMap[target].push_back(assoc); // Is this a known source object? if(m_SourceMap.find(source) == m_SourceMap.end()) { // Target object is new. Register to receive delete events from it SmartPtr cmd_source_delete = itk::CStyleCommand::New(); cmd_source_delete->SetCallback(&Rebroadcaster::DeleteSourceCallback); cmd_source_delete->SetConstCallback(&Rebroadcaster::DeleteSourceConstCallback); source->AddObserver(itk::DeleteEvent(), cmd_source_delete); } // Add the association to the source's list m_SourceMap[source].push_back(assoc); // Return the tag return assoc->m_SourceTag; } unsigned long Rebroadcaster ::RebroadcastAsSourceEvent( itk::Object *source, const itk::EventObject &sourceEvent, itk::Object *target, EventBucket *bucket) { // We just call the main rebroadcast method with RefireEvent() to indicate // that the source event should be refired return Rebroadcast(source, sourceEvent, target, RefireEvent(), bucket); } void Rebroadcaster::DeleteTargetCallback( itk::Object *target, const itk::EventObject &evt, void *cd) { DeleteTargetConstCallback(target, evt, cd); } void Rebroadcaster::DeleteTargetConstCallback( const itk::Object *target, const itk::EventObject &evt, void *cd) { // The target object is being deleted. This means that the corresponding // associations should be detatched from their source objects. // Find the list of associations for the target object. DispatchMap::iterator itmap = m_TargetMap.find(target); if(itmap == m_TargetMap.end()) return; // Destroy all the associations AssociationList &l = m_TargetMap[target]; for(AssociationIterator it = l.begin(); it != l.end(); ++it) { Association *assoc = *it; // Remove the observer from the source. If the source is the same object // as the target (being deleted), we can skip this if(target != assoc->m_Source) assoc->m_Source->RemoveObserver(assoc->m_SourceTag); // Remove the association from the source's list m_SourceMap[assoc->m_Source].remove(assoc); // Delete the association delete assoc; } // Remove the target from m_TargetMap m_TargetMap.erase(itmap); } void Rebroadcaster::DeleteSourceCallback( itk::Object *source, const itk::EventObject &evt, void *cd) { DeleteSourceConstCallback(source, evt, cd); } void Rebroadcaster::DeleteSourceConstCallback( const itk::Object *source, const itk::EventObject &evt, void *cd) { // The source object is being deleted. We need to destroy all of its // associations. We don't need to remove any observers though. // Find the list of associations for the source object. DispatchMap::iterator itmap = m_SourceMap.find(source); if(itmap == m_SourceMap.end()) return; // Destroy all the associations AssociationList &l = m_SourceMap[source]; for(AssociationIterator it = l.begin(); it != l.end(); ++it) { Association *assoc = *it; // Remove the association from the target's list m_TargetMap[assoc->m_Target].remove(assoc); // If the association is for a delete event, then it must be triggered, // because these associations are not hooked up to the source objects using // AddListener as non-delete associations if(assoc->m_IsForDeleteEvent) { // This call will propagate the event downstream, even though the // association itself will be deleted assoc->ConstCallback(source, evt); } // Delete the association delete assoc; } // Remove the source from the source map m_SourceMap.erase(itmap); } Rebroadcaster::Association::Association( itk::Object *source, itk::Object *target, const itk::EventObject &targetEvent) { m_Source = source; m_Target = target; m_TargetEvent = targetEvent.MakeObject(); m_Bucket = NULL; m_SourceTag = 0; m_SourceObjectName = source->GetNameOfClass(); m_TargetObjectName = target->GetNameOfClass(); // Are we refiring the source event or firing the target event? m_RefireSource = Rebroadcaster::RefireEvent().CheckEvent(m_TargetEvent); } Rebroadcaster::Association::~Association() { delete m_TargetEvent; } void Rebroadcaster::Association::Callback(itk::Object *source, const itk::EventObject &evt) { this->ConstCallback((const itk::Object *) source, evt); } void Rebroadcaster::Association::ConstCallback(const itk::Object *source, const itk::EventObject &evt) { // Decide what to do const itk::EventObject *firedEvent = m_RefireSource ? &evt : m_TargetEvent; #ifdef SNAP_DEBUG_EVENTS if(flag_snap_debug_events) { std::cout << "REBROADCAST event " << evt.GetEventName() << " from " << m_SourceObjectName << " [" << source << "] " << " as " << firedEvent->GetEventName() << " from " << m_TargetObjectName << " [" << m_Target << "] " << std::endl << std::flush; } #endif // SNAP_DEBUG_EVENTS // Rebroadcast the target event m_Target->InvokeEvent(*firedEvent); // If there is a bucket, record in it if(m_Bucket) m_Bucket->PutEvent(evt, source); } itksnap-3.4.0/Common/Rebroadcaster.h000066400000000000000000000107361263013355200173570ustar00rootroot00000000000000#ifndef REBROADCASTER_H #define REBROADCASTER_H #include #include #include #include #include class EventBucket; /** * @brief The Rebroadcaster class * This class allows classes that inherit from itk::Object to rebroadcast * events from other itk::Objects as their own events. For example, if I * have a class A, which has an instance variable A::worker of class B, and * B invokes the event 'FinishedEvent()', I might want A to invoke the event * 'WorkerFinishedEvent()'. This is cumbersome to implement in ITK, but using * the Rebroadcaster class, it's quite easy: * * A a1; * unsigned long tag = Rebroadcaster::Rebroadcast( * a1.worker, FinishedEvent(), a1, WorkerFinishedEvent()); * * Later on, if we want to stop rebroadcasting, we can use the tag to stop: * * Rebroadcaster::StopRebroadcasting(tag); * * The class handles object destruction gracefully. As soon as one of the classes * fires a itk::DeleteEvent(), the rebroadcasting stops. * * The class also has optional support for event buckets. If, the caller passes * in an EventBucket pointer to Rebroadcast(), then any events passed to the * target object from the source object will be added to the EventBucket. This * way, the target object (or another interested party) can keep track of what * events, and from whom, were rebroadcast. */ class Rebroadcaster { public: /** * Rebroadcast event sourceEvent from object source as the event * targetEvent from object target. Note that if the source event is of * a type derived from sourceEvent, then the broadcast will still occur. * See docs for the class (above) for notes about the EventBucket param. */ static unsigned long Rebroadcast( itk::Object *source, const itk::EventObject &sourceEvent, itk::Object *target, const itk::EventObject &targetEvent, EventBucket *bucket = NULL); /** * Rebroadcast event sourceEvent from object source as the same event from * object target. An important property of this method is that if we call * this method with source event of type baseEvent, and the source object * fires event derivedEvent(), which derives from baseEvent(), then the * event rebroadcast by the target object will be of type derivedEvent(). * This is a convenient mechanism for rebroadcasting whole groups of events. * For example, if we want every event fired by source to be rebroadcast by * target, we can just call this method with itk::AnyEvent() as sourceEvent. */ static unsigned long RebroadcastAsSourceEvent( itk::Object *source, const itk::EventObject &sourceEvent, itk::Object *target, EventBucket *bucket = NULL); protected: // We define our own event type RefireEvent which is used to indicate that // the event fired by the target object should be the same as the event // fired by the source object itkEventMacro(RefireEvent, itk::EventObject) // One of these workers is associated with every rebroadcast request class Association { public: Association(itk::Object *source, itk::Object *target, const itk::EventObject &targetEvent); ~Association(); void Callback(itk::Object *source, const itk::EventObject &evt); void ConstCallback(const itk::Object *source, const itk::EventObject &evt); itk::Object *m_Source, *m_Target; itk::EventObject *m_TargetEvent; EventBucket *m_Bucket; unsigned long m_SourceTag; // Whether this association is in response to a delete event. bool m_IsForDeleteEvent; // For debug purposes, it helps to keep the names of the source and target // objects in memory, in case these objects are deleted const char *m_SourceObjectName, *m_TargetObjectName; bool m_RefireSource; }; static void DeleteSourceCallback( itk::Object *source, const itk::EventObject &evt, void *cd); static void DeleteSourceConstCallback( const itk::Object *source, const itk::EventObject &evt, void *cd); static void DeleteTargetCallback( itk::Object *target, const itk::EventObject &evt, void *cd); static void DeleteTargetConstCallback( const itk::Object *target, const itk::EventObject &evt, void *cd); // typedef std::pair ObjectEventPair; typedef std::list AssociationList; typedef AssociationList::iterator AssociationIterator; typedef std::map DispatchMap; static DispatchMap m_SourceMap, m_TargetMap; }; #endif // REBROADCASTER_H itksnap-3.4.0/Common/Registry.cxx000066400000000000000000000447471263013355200167730ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Registry.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:12 $ Version: $Revision: 1.2 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #include "Registry.h" #include "IRISVectorTypes.h" #include "itkXMLFile.h" #include #include #include #include #include #include "itksys/SystemTools.hxx" #include "IRISException.h" #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif using namespace std; /** Reader for XML files */ class RegistryXMLFileReader : public itk::XMLReader { public: irisITKObjectMacro(RegistryXMLFileReader, itk::XMLReader) protected: RegistryXMLFileReader() {} virtual int CanReadFile(const char *name); virtual void StartElement(const char *name, const char **atts); virtual void EndElement(const char *name); virtual void CharacterDataHandler(const char *inData, int inLength); static int strcmpi(const char *str1, const char *str2) { return itksys::SystemTools::Strucmp(str1, str2); } // The folder stack std::list m_FolderStack; }; int RegistryXMLFileReader::CanReadFile(const char *name) { if ( !itksys::SystemTools::FileExists(name) || itksys::SystemTools::FileIsDirectory(name) || itksys::SystemTools::FileLength(name) == 0 ) { return 0; } return 1; } void RegistryXMLFileReader::StartElement(const char *name, const char **atts) { // If this is the root-level element , it can be ignored if(strcmpi(name, "registry") == 0) { // Put the target registry on the stack assert(this->GetOutputObject()); m_FolderStack.push_back(this->GetOutputObject()); return; } // If the stack is empty throw up if(m_FolderStack.size() == 0) throw IRISException("Problem parsing Registry XML file. The file might not be valid."); // Read the attributes into a map typedef std::map AttributeMap; typedef AttributeMap::const_iterator AttrIter; AttributeMap attrmap; for(int i = 0; atts[i] != NULL && atts[i+1] != NULL; i+=2) attrmap[itksys::SystemTools::LowerCase(atts[i])] = atts[i+1]; // Process tags if(strcmpi(name, "folder") == 0) { AttrIter itKey = attrmap.find("key"); if(itKey == attrmap.end()) throw IRISException("Missing 'key' attribute to element"); // Create a new folder and place it on the stack Registry &newFolder = m_FolderStack.back()->Folder(itKey->second); m_FolderStack.push_back(&newFolder); } else if(strcmpi(name, "entry") == 0) { AttrIter itKey = attrmap.find("key"); if(itKey == attrmap.end()) throw IRISException("Missing 'key' attribute to element"); AttrIter itValue = attrmap.find("value"); if(itValue == attrmap.end()) throw IRISException("Missing 'value' attribute to element"); // Create a new entry (TODO: decode!) m_FolderStack.back()->Entry(itKey->second) = RegistryValue(itValue->second); } else throw IRISException("Unknown XML element <%s>", name); } void RegistryXMLFileReader::EndElement(const char *name) { if(strcmpi(name, "registry") == 0) { m_FolderStack.clear(); } else if(strcmpi(name, "folder") == 0) { m_FolderStack.pop_back(); } } void RegistryXMLFileReader::CharacterDataHandler(const char *inData, int inLength) { return; } RegistryValue ::RegistryValue() { m_Null = true; m_String = ""; } RegistryValue ::RegistryValue(const std::string &input) { m_Null = false; m_String = input; } RegistryValue& Registry::Entry(const std::string &key) { // Get the containing folder StringType::size_type iDot = key.find_first_of('.'); // There is a subfolder if(iDot != key.npos) { StringType child = key.substr(0,iDot); StringType childKey = key.substr(iDot+1); return Folder(child).Entry(childKey); } // Search for the key and return it if found EntryIterator it = m_EntryMap.find(key); if(it != m_EntryMap.end()) return it->second; // Key was not found, create a null entry m_EntryMap[key] = RegistryValue(); return m_EntryMap[key]; } Registry::StringType Registry::Key(const char *format,...) { // A string for prinf-ing static char buffer[1024]; // Do the printf operation va_list al; va_start(al,format); vsprintf(buffer,format,al); va_end(al); // Use the string as parameter return StringType(buffer); } int Registry ::GetEntryKeys(StringListType &targetArray) { // Iterate through keys in ascending order for(EntryIterator it=m_EntryMap.begin();it!=m_EntryMap.end();++it) { // Put the key in the array targetArray.push_back(it->first); } // Return the number of keys copied return targetArray.size(); } int Registry ::GetFolderKeys(StringListType &targetArray) { // Iterate through keys in ascending order for(FolderIterator it=m_FolderMap.begin();it!=m_FolderMap.end();++it) { // Put the key in the array targetArray.push_back(it->first); } // Return the number of keys copied return targetArray.size(); } bool Registry::HasEntry(const Registry::StringType &key) { // Get the containing folder StringType::size_type iDot = key.find_first_of('.'); // There is a subfolder if(iDot != key.npos) { StringType child = key.substr(0,iDot); StringType childKey = key.substr(iDot+1); return Folder(child).HasEntry(childKey); } // Search for the key and return it if found EntryIterator it = m_EntryMap.find(key); return it != m_EntryMap.end(); } bool Registry::HasFolder(const Registry::StringType &key) { // Get the containing folder StringType::size_type iDot = key.find_first_of('.'); // There is a subfolder if(iDot != key.npos) { StringType child = key.substr(0,iDot); StringType childKey = key.substr(iDot+1); return Folder(child).HasFolder(childKey); } // Search for the key and return it if found FolderIterator it = m_FolderMap.find(key); return it != m_FolderMap.end(); } void Registry ::Write(ostream &sout,const StringType &prefix) { // Write the entries in this folder for(EntryIterator ite = m_EntryMap.begin();ite != m_EntryMap.end(); ++ite) { // Only write the non-null entries if(!ite->second.IsNull()) { // Write the key = sout << prefix << Encode(ite->first) << " = "; // Write the encoded value sout << Encode(ite->second.GetInternalString()) << endl; } } // Write the folders for(FolderIterator itf = m_FolderMap.begin(); itf != m_FolderMap.end(); ++itf) { // Write the folder contents (recursive, contents prefixed with full path name) itf->second->Write(sout, prefix + itf->first + "." ); } } void Registry ::WriteXML(ostream &sout, const StringType &prefix) { // Write the entries in this folder for(EntryIterator ite = m_EntryMap.begin();ite != m_EntryMap.end(); ++ite) { // Only write the non-null entries if(!ite->second.IsNull()) { // Write the key sout << prefix << "first) << "\""; // Write the encoded value sout << " value=\"" << EncodeXML(ite->second.GetInternalString()) << "\" />" << endl; } } // Write the folders for(FolderIterator itf = m_FolderMap.begin(); itf != m_FolderMap.end(); ++itf) { // Write the folder tag sout << prefix << "first) << "\" >" << endl; // Write the folder contents (recursive, contents prefixed with full path name) itf->second->WriteXML(sout, prefix + " "); // Close the folder sout << prefix << "" << endl; } } void Registry ::SetFlagAddIfNotFound(bool yesno) { // Set the internal value m_AddIfNotFound = yesno; // Propagate to all the children folders for(FolderIterator itf = m_FolderMap.begin(); itf != m_FolderMap.end(); ++itf) { itf->second->SetFlagAddIfNotFound(yesno); } } void Registry ::CollectKeys(StringListType &keyList,const StringType &prefix) { // Go through the children for(FolderIterator itf = m_FolderMap.begin(); itf != m_FolderMap.end(); ++itf) { // Collect the child's keys with a new prefix itf->second->CollectKeys(keyList, prefix + itf->first + "."); } // Add the keys in this folder for(EntryIterator ite = m_EntryMap.begin();ite != m_EntryMap.end(); ++ite) { // Add the key to the collection list keyList.push_back(prefix + ite->first); } } void Registry ::Update(const Registry ®) { // Go through the children for(FolderIterator itf = reg.m_FolderMap.begin(); itf != reg.m_FolderMap.end(); ++itf) { // Update the sub-folder this->Folder(itf->first).Update(*(itf->second)); } // Add the keys in this folder for(EntryConstIterator ite = reg.m_EntryMap.begin(); ite != reg.m_EntryMap.end(); ++ite) { RegistryValue &entry = Entry(ite->first); entry = ite->second; } } Registry::StringType Registry ::FindValue(const StringType& value) { // Add the keys in this folder for(EntryIterator ite = m_EntryMap.begin();ite != m_EntryMap.end(); ++ite) { if(ite->second.GetInternalString() == value) return ite->first; } return ""; } void Registry ::RemoveKeys(const char *match) { // Create a match substring string sMatch = (match) ? match : 0; // Search and delete from the map EntryMapType newMap; for(EntryIterator it=m_EntryMap.begin(); it != m_EntryMap.end(); it++) { if(it->first.find(sMatch) != 0) newMap[it->first] = it->second; } m_EntryMap = newMap; } void Registry ::Clear() { m_EntryMap.clear(); m_FolderMap.clear(); } Registry::StringType Registry ::EncodeXML(const StringType &input) { IRISOStringStream oss; for(unsigned int i=0; i < input.length() ; i++) { // Map the character to positive integer (0..255) char c = input[i]; // There are special characters not allowed in XML switch(c) { case '<' : oss << "<"; break; case '>' : oss << ">"; break; case '&' : oss << "&"; break; case '\'' : oss << "'"; break; case '\"' : oss << """; break; default: oss << c; break; } } // Return the resulting string return oss.str(); } Registry::StringType Registry::DecodeXML(const Registry::StringType &input) { return input; } Registry::StringType Registry ::Encode(const StringType &input) { IRISOStringStream oss; for(unsigned int i=0; i < input.length() ; i++) { // Map the character to positive integer (0..255) char c = input[i]; int v = static_cast(static_cast(c)); // We only print ASCII codes if(v <= 0x20 || v >= 0x7f || c == '%') { // Replace character by a escape string oss << '%'; oss << setw(2) << setfill('0') << hex << v; } else { // Just copy the character oss << c; } } // Return the resulting string return oss.str(); } Registry::StringType Registry::Decode(const StringType &input) { // Create an input stream IRISIStringStream iss(input); IRISOStringStream oss; // Read until we run out or crash while(iss.good()) { // Read the next character char c = (char) iss.get(); // Check if the character needs to be translated if(!isprint(c)) { continue; } else if(c != '%') { // Just copy the character oss.put(c); } else { // A pair of chars char c1,c2; // Read the hex digit iss >> c1; iss >> c2; // Reconstruct the hex int d1 = (c1 < 'a') ? c1 - '0' : c1 - 'a' + 10; int d2 = (c2 < 'a') ? c2 - '0' : c2 - 'a' + 10; int digit = d1 * 16 + d2; // See if the read succeeded, only then use the digit if(iss.good()) oss << (char)digit; // A good place to throw an exception (for strict interpretation) } } // Return the result return oss.str(); } void Registry ::Read(istream &sin, ostream &oss) { unsigned int lineNumber = 1; while(sin.good()) { // Read a line from the file char buffer[1024]; sin.getline(buffer,1024); // Find the first character in the string StringType line = buffer; StringType::size_type iToken = line.find_first_not_of(" \t\v\r\n"); // Skip blank lines if(iToken == line.npos) continue; // Check if it's a comment if(line[iToken] == '#') continue; // Find an equal sign in the string StringType::size_type iOper = line.find_first_of('='); if(iOper == line.npos) { // Not a valid line oss << std::setw(5) << lineNumber << " : Missing '='; line ignored." << endl; continue; } if(iOper == iToken) { // Missing key oss << std::setw(5) << lineNumber << " : Missing key before '='; line ignored." << endl; continue; } // Extract the key string key = Decode( line.substr(iToken,line.find_first_of(" \t\v\r\n=",iToken) - iToken)); // Extract the value StringType::size_type iValue = line.find_first_not_of(" \t\v\r\n=",iOper); string value = ""; if (iValue != line.npos) { value = line.substr(iValue); } // Now the key-value pair is present. Add it using the normal interface Entry(key) = RegistryValue(Decode(value)); ++lineNumber; } } Registry & Registry ::Folder(const string &key) { // Find the first separator in the key string StringType::size_type iDot = key.find_first_of('.'); // Recurse on the immideate folder if(iDot != key.npos) { StringType child = key.substr(0,iDot); StringType childKey = key.substr(iDot+1); return Folder(child).Folder(childKey); } // Get the folder, adding if necessary FolderIterator it = m_FolderMap.find(key); if(it != m_FolderMap.end()) return *(it->second); // Add the folder m_FolderMap[key] = new Registry(); m_FolderMap[key]->m_AddIfNotFound = m_AddIfNotFound; return *m_FolderMap[key]; } Registry ::Registry() { m_AddIfNotFound = false; } Registry ::Registry(const char *fname) { ReadFromFile(fname); } Registry:: ~Registry() { // Delete all the sub-folders // for(FolderIterator itf = m_FolderMap.begin(); itf != m_FolderMap.end(); ++itf) // delete itf->second; } bool Registry::operator == (const Registry &other) const { // Compare the folders if(m_FolderMap.size() != other.m_FolderMap.size()) return false; for(FolderIterator it1 = m_FolderMap.begin(), it2 = other.m_FolderMap.begin(); it1 != m_FolderMap.end(); ++it1, ++it2) { // Compare keys if(it1->first != it2->first) return false; // Compare subfolder contents (recursively) if(*(it1->second) != *(it2->second)) return false; } // Compare the entries if(m_EntryMap.size() != other.m_EntryMap.size()) return false; for(EntryConstIterator it1 = m_EntryMap.begin(), it2 = other.m_EntryMap.begin(); it1 != m_EntryMap.end(); ++it1, ++it2) { // Compare keys if(it1->first != it2->first) return false; // Compare subfolder contents (recursively) if(it1->second != it2->second) return false; } return true; } bool Registry::operator != (const Registry &other) const { return ! (*this == other); } /** * Write the folder to a disk */ void Registry ::WriteToFile(const char *pathname, const char *header) { // Open the file ofstream sout(pathname,std::ios::out); // Set the stream to be picky sout.exceptions(std::ios::failbit); // Write the header if(header) sout << header << endl; // Write to the stream Write(sout,""); } void Registry::WriteToXMLFile(const char *pathname, const char *header) { // Open the file ofstream sout(pathname,std::ios::out); // Set the stream to be picky sout.exceptions(std::ios::failbit); // Write the XML string sout << "" << endl; // Write the header if(header) sout << "" << endl; // Write the DOCTYPE content sout << "" << endl << "" << endl << "" << endl << "" << endl << "" << endl << "" << endl << "]>" << endl; // Write to the stream sout << "" << endl; WriteXML(sout, " "); sout << "" << endl; } /** * Read folder from file */ void Registry ::ReadFromFile(const char *pathname) { // Create output stream ifstream sin(pathname,std::ios::in); if(!sin.good()) throw IOException("Unable to open the Registry file"); ReadFromStream(sin); } void Registry ::ReadFromStream(istream &sin) { // Create an error stream IRISOStringStream serr; try { // Try reading Read(sin,serr); } catch(...) { throw IOException("Unable to read the Registry file"); } // If the error stream is not empty, throw an exception if(serr.str().length()) throw SyntaxException(serr.str().c_str()); } void Registry::ReadFromXMLFile(const char *pathname) { SmartPtr reader = RegistryXMLFileReader::New(); reader->SetOutputObject(this); reader->SetFilename(pathname); reader->GenerateOutputInformation(); } itksnap-3.4.0/Common/Registry.h000066400000000000000000000335051263013355200164060ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Registry.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:12 $ Version: $Revision: 1.2 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __Registry_h_ #define __Registry_h_ #ifdef _MSC_VER # pragma warning(disable:4786) // '255' characters in the debug information #endif //_MSC_VER #include #include #include #include #include #include #include #include "SNAPCommon.h" #include "itkXMLFile.h" inline Vector2d GetValueWithDefault(const std::string &source, bool isNull, Vector2d defaultValue) { // Initialize with the default value Vector2d returnValue = defaultValue; // Default value is returned if the entry is Null if(isNull) return returnValue; // Try to access the value using c++ formatting IRISIStringStream iss(source); iss >> returnValue; // Proceed as if the operation succeeded return returnValue; } inline Vector3d GetValueWithDefault(const std::string &source, bool isNull, Vector3d defaultValue) { // Initialize with the default value Vector3d returnValue = defaultValue; // Default value is returned if the entry is Null if(isNull) return returnValue; // Try to access the value using c++ formatting IRISIStringStream iss(source); iss >> returnValue; // Proceed as if the operation succeeded return returnValue; } inline Vector3i GetValueWithDefault(const std::string &source, bool isNull, Vector3i defaultValue) { // Initialize with the default value Vector3i returnValue = defaultValue; // Default value is returned if the entry is Null if(isNull) return returnValue; // Try to access the value using c++ formatting IRISIStringStream iss(source); iss >> returnValue; // Proceed as if the operation succeeded return returnValue; } inline Vector2i GetValueWithDefault(const std::string &source, bool isNull, Vector2i defaultValue) { // Initialize with the default value Vector2i returnValue = defaultValue; // Default value is returned if the entry is Null if(isNull) return returnValue; // Try to access the value using c++ formatting IRISIStringStream iss(source); iss >> returnValue; // Proceed as if the operation succeeded return returnValue; } template inline T GetValueWithDefault(const std::string &source, bool isNull, T defaultValue) { // Initialize with the default value T returnValue = defaultValue; // Default value is returned if the entry is Null if(isNull) return returnValue; // Try to access the value using c++ formatting IRISIStringStream iss(source); iss >> returnValue; // Proceed as if the operation succeeded return returnValue; } template <> inline const char *GetValueWithDefault(const std::string &source, bool isNull, const char *defaultValue) { if(isNull) return defaultValue; else return source.c_str(); } /** A class used to associate a set of strings with an ENUM so that the * enum can be exported to the registry in a consistent fashion */ template class RegistryEnumMap { public: typedef std::string StringType; void AddPair(TEnum value, const char *description) { m_EnumToStringMap[value] = std::string(description); m_StringToEnumMap[description] = value; } bool GetEnumValue(const StringType &key, TEnum &outValue) const { typename std::map::const_iterator it = m_StringToEnumMap.find(key); if(it == m_StringToEnumMap.end()) return false; outValue = it->second; return true; } bool GetString(TEnum value, StringType &outString) const { typename std::map::const_iterator it = m_EnumToStringMap.find(value); if(it == m_EnumToStringMap.end()) return false; outString = it->second; return true; } unsigned int Size() const { return m_EnumToStringMap.size(); } StringType operator [] (TEnum value) { return m_EnumToStringMap[value]; } private: std::map m_EnumToStringMap; std::map m_StringToEnumMap; friend class RegistryValue; }; /** A class that represents a value in the registry */ class RegistryValue { public: /** Default constructor: sets this object to Null state */ RegistryValue(); /** Initializing constructor: sets the object to a value */ RegistryValue(const std::string &value); /** Comparison */ bool operator == (const RegistryValue &other) const { return m_Null == other.m_Null && m_String == other.m_String; } bool operator != (const RegistryValue &other) const { return ! (*this == other); } /** Is the value present in the Registry? */ bool IsNull() const { return m_Null; } /** Get the internally stored string */ const std::string &GetInternalString() const { return m_String; } /** * An operator that allows us to access a value using different * formats */ bool operator[](bool defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } int operator[](int defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } unsigned int operator[](unsigned int defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } double operator[](double defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } const char *operator[](const char *defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } std::string operator[](const std::string &defaultValue) { if(IsNull()) return defaultValue; else return m_String; } Vector3i operator[](const Vector3i &defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } Vector2i operator[](const Vector2i &defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } Vector3d operator[](const Vector3d &defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } Vector2d operator[](const Vector2d &defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } /* template iris_vector_fixed operator[](const iris_vector_fixed &defaultValue) { return GetValueWithDefault(m_String,m_Null,defaultValue); } */ /** * An operator that allows us to write a value using different formats */ template void operator << (const T newValue) { // Create an output stream IRISOStringStream oss; // Put the new value into the stream oss << newValue; // Set the string m_String = oss.str(); m_Null = false; } /** * Put an enum into this entry. If the enum map does not have the value, * nothing will be written to the entry (entry will be null) */ template void PutEnum(const RegistryEnumMap &rem, TEnum value) { m_Null = !rem.GetString(value, m_String); } /** Get an enum from this entry */ template TEnum GetEnum(const RegistryEnumMap &rem, TEnum defaultValue) { TEnum value; if(rem.GetEnumValue(m_String, value)) return value; else return defaultValue; } private: std::string m_String; bool m_Null; }; /** * \class Registry * \brief A tree of key-value pair maps */ class Registry { public: // String definition typedef std::string StringType; // List of strings typedef std::list StringListType; /** Constructor initializes an empty registry */ Registry(); /** Constructor loads a registry from a file */ Registry(const char *fname); /** Destructor */ virtual ~Registry(); /** Comparison */ bool operator == (const Registry &other) const; bool operator != (const Registry &other) const; /** Get a reference to a value in this registry, which can then be queried */ RegistryValue &operator[](const StringType &key) { return Entry(key); } /** Get a reference to a folder inside this registry, creating it if necessary */ RegistryValue &Entry(const StringType &key); /** Get a reference to a folder inside this registry, creating it if necessary */ Registry &Folder(const StringType &key); /** A helper method to convert a printf-style expression to a key */ static StringType Key(const char *key,...); /** Get a list of all keys that have values, append it to keyList */ int GetEntryKeys(StringListType &keyList); /** Get a list of all subfolder keys, append it to keyList */ int GetFolderKeys(StringListType &keyList); /** Check if an entry with the given key exists */ bool HasEntry(const StringType &key); /** Check if a subfolder with the given key exists */ bool HasFolder(const StringType &key); /** Find a value in a folder or return "" */ StringType FindValue(const StringType& value); /** Empty the contents of the registry */ void Clear(); /** Get a list of all keys that have values contained in this registry * and all subfolders (recursive operation). Prefix is used internally, * but can be specified to prepend a string to all keys */ void CollectKeys(StringListType &keyList,const StringType &keyPrefix=""); /** Remove all keys from this folder that start with a string */ void RemoveKeys(const char *match = NULL); /** Write this Registry to an file. The second parameter is an optional * header, each line of which must start with the '#" character */ void WriteToFile(const char *pathname, const char *header = NULL); /** Write the Registry to an XML file */ void WriteToXMLFile(const char *pathname, const char *header = NULL); /** * Update the registry with keys from another registry */ void Update(const Registry ®); /** Read this Registry from a file */ void ReadFromFile(const char *pathname); /** Read from an std::ifstream */ void ReadFromStream(std::istream &sin); /** Read from XML file */ void ReadFromXMLFile(const char *pathname); /** Experimental */ void SetFlagAddIfNotFound(bool yesno); /** Put an array into the registry */ template void PutArray(unsigned int size,const T *array) { RemoveKeys("Element"); Entry("ArraySize") << size; for(unsigned int i=0;i void PutArray(const std::vector &array) { RemoveKeys("Element"); Entry("ArraySize") << array.size(); for(unsigned int i=0;i std::vector GetArray(const T &defaultElement) { // Try reading the element count unsigned int size = Entry("ArraySize")[(unsigned int) 0]; // Initialize the result array std::vector result(size,defaultElement); // Read element for(unsigned int i=0;i < size;i++) { result[i] = Entry(Key("Element[%d]",i))[defaultElement]; } // Return the array return result; } /** An IO exception objects thrown by this class when reading from file*/ class IOException : public StringType { public: IOException(const char *text) : StringType(text) {} }; /** A syntax error exception */ class SyntaxException : public StringType { public: SyntaxException(const char *text) : StringType(text) {} }; private: // Hashtable type definition typedef std::map FolderMapType; typedef std::map EntryMapType; // Commonly used hashtable iterators typedef FolderMapType::const_iterator FolderIterator; typedef EntryMapType::iterator EntryIterator; typedef EntryMapType::const_iterator EntryConstIterator; /** A hash table for the subfolders */ FolderMapType m_FolderMap; /** A hash table for the registry values */ EntryMapType m_EntryMap; /** * A flag as to whether keys and folders that are read and not found * should be created and populated with default values. */ bool m_AddIfNotFound; /** Write this folder recursively to a stream */ void Write(std::ostream &sout,const StringType &keyPrefix); /** Write this folder recursively to a stream in XML format */ void WriteXML(std::ostream &sout,const StringType &keyPrefix); /** Read this folder recursively from a stream, recording syntax errors */ void Read(std::istream &sin, std::ostream &serr); /** Encode a string for writing to file */ static StringType Encode(const StringType &input); /** Decode a string for writing to file */ static StringType Decode(const StringType &input); /** Encode a string for writing to file */ static StringType EncodeXML(const StringType &input); /** Decode a string for writing to file */ static StringType DecodeXML(const StringType &input); }; #include #endif itksnap-3.4.0/Common/SNAPBorlandDummyTypes.h000066400000000000000000000073571263013355200207100ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: SNAPBorlandDummyTypes.h,v $ Language: C++ Date: $Date: 2006/12/02 04:22:09 $ Version: $Revision: 1.1 $ Copyright (c) 2003 Insight Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __SNAPBorlandDummyTypes_h_ #define __SNAPBorlandDummyTypes_h_ #include "IRISSlicer.h" #include #include typedef IRISSlicer SNAPBorlandDummyIRISSlicer; typedef itk::SmartPointer SNAPBorlandDummyIRISSlicerSP; typedef IRISSlicer SNAPBorlandDummyIRISSlicer2; typedef itk::SmartPointer SNAPBorlandDummyIRISSlicer2SP; #include #include #include #include #include #include #include #include typedef itk::ImageBase<2> SNAPBorlandDummyImageBase2Type; typedef itk::ImageBase<3> SNAPBorlandDummyImageBase3Type; typedef itk::ImageRegion<3> SNAPBorlandDummyImageRegion3Type; typedef itk::ImageRegion<2> SNAPBorlandDummyImageRegion2Type; typedef itk::Image,2> SNAPBorlandDummyImageType; typedef itk::Image SNAPBorlandDummyImageType3; typedef itk::Image SNAPBorlandDummyImageType2; typedef itk::Image SNAPBorlandDummyImageType4; typedef itk::Image SNAPBorlandDummyImageType5; typedef itk::Image SNAPBorlandDummyImageType6; typedef itk::Image SNAPBorlandDummyImageType7; typedef itk::Image SNAPBorlandDummyImageType8; typedef itk::Image SNAPBorlandDummyImageType9; typedef itk::Image,2> SNAPBorlandDummyImageType10; typedef itk::Image,2> SNAPBorlandDummyImageType11; typedef itk::ImageSource SNAPBorlandDummyImageSourceType5; typedef itk::ImageSource SNAPBorlandDummyImageSourceType2; typedef itk::ImageSource SNAPBorlandDummyImageSourceType3; typedef itk::ImageSource SNAPBorlandDummyImageSourceType; typedef itk::ImageSource SNAPBorlandDummyImageSourceType9; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageTypeIterator; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageType2Iterator; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageType3Iterator; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageType4Iterator; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageType5Iterator; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageType6Iterator; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageType7Iterator; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageType8Iterator; typedef itk::ImageRegionConstIterator SNAPBorlandDummyImageType10Iterator; typedef itk::Size<3> SNAPBorlandDummySizeType; typedef itk::ImageIOBase SNAPBorlandDummyImageIOBaseType; #endif // __SNAPBorlandDummyTypes_h_ itksnap-3.4.0/Common/SNAPCommon.cxx.in000066400000000000000000000051321263013355200174630ustar00rootroot00000000000000/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: SNAPCommon.cxx.in,v $ Language: C++ Date: $Date: 2009/02/03 20:28:14 $ Version: $Revision: 1.4 $ Copyright (c) 2003 Insight Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #include "SNAPCommon.h" // Non-cvs version const char SNAPSoftVersion[] = "ITK-SNAP @SNAP_VERSION_FULL@"; // Just the number part of the SNAP version unsigned int SNAPVersionMajor = @SNAP_VERSION_MAJOR@; unsigned int SNAPVersionMinor = @SNAP_VERSION_MINOR@; unsigned int SNAPVersionPatch = @SNAP_VERSION_PATCH@; const char SNAPVersionQualifier[] = "@SNAP_VERSION_QUALIFIER@"; // Hardware architecture for this build const char SNAPArch[] = "@CPACK_SYSTEM_NAME@"; // A string that appears in the user interface const char SNAPUISoftVersion[] = "Version @SNAP_VERSION_FULL@\n @SNAP_VERSION_RELEASE_DATE_FORMATTED@"; // Release date of the current version const char SNAPCurrentVersionReleaseDate[] = "@SNAP_VERSION_RELEASE_DATE@"; // String describing the current build environment const char SNAPBuildInfo[] = "Build date @SNAP_VERSION_COMPILE_DATE@\n" "Git commit hash @SNAP_VERSION_GIT_SHA1@\n" "Git branch @SNAP_VERSION_GIT_BRANCH@\n" "Build site name @SITE@\n" "Build OS @CMAKE_HOST_SYSTEM@ @CMAKE_HOST_SYSTEM_PROCESSOR@\n" "Build type @CMAKE_BUILD_TYPE@\n" "ITK version @ITK_VERSION_MAJOR@.@ITK_VERSION_MINOR@.@ITK_VERSION_PATCH@\n" "VTK version @VTK_VERSION_MAJOR@.@VTK_VERSION_MINOR@.@VTK_VERSION_PATCH@\n" "Qt version @QTVERSION@\n" "CMake version @CMAKE_VERSION@\n" "C compiler @CMAKE_C_COMPILER_ID@ @CMAKE_C_COMPILER_VERSION@\n" "C++ compiler @CMAKE_CXX_COMPILER_ID@ @CMAKE_CXX_COMPILER_VERSION@\n"; // Release date of the latest version whose user preference files are // incompatible with the current version and will be erased const char SNAPLastIncompatibleReleaseDate[] = "@SNAP_VERSION_LAST_COMPATIBLE_RELEASE_DATE@"; // Build date - shown to help debugging nightly builds const char SNAPBuildDate[] = "@SNAP_VERSION_COMPILE_DATE@"; // GIT signature const char SNAPGitSignature[] = "@SNAP_VERSION_GIT_SHA1@"; itksnap-3.4.0/Common/SNAPCommon.h000066400000000000000000000215261263013355200165100ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPCommon.h,v $ Language: C++ Date: $Date: 2010/07/01 21:40:24 $ Version: $Revision: 1.13 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __SNAPCommon_h_ #define __SNAPCommon_h_ /** This is an include file that should be inserted into all SNAP files */ // Some standard library things are used so commonly that there is no need // to have them included every time #include #include #include // Specify a standard stream for verbose messages extern std::ostream &verbose; // Specify a standard stream for error messages extern std::ostream &snaperr; // From ITK we use SmartPointer so many times that it is necessary #include // Include the VectorNx defintions #include "IRISVectorTypes.h" // RGB Pixel support #include /** Definitions for the string streams, for compatibility */ typedef std::ostringstream IRISOStringStream; typedef std::istringstream IRISIStringStream; // Non-cvs version extern const char SNAPSoftVersion[]; // Just the number part of the SNAP version extern unsigned int SNAPVersionMajor; extern unsigned int SNAPVersionMinor; extern unsigned int SNAPVersionPatch; extern const char SNAPVersionQualifier[]; // Hardware architecture for this build extern const char SNAPArch[]; // A string that appears in the user interface extern const char SNAPUISoftVersion[]; // Release date of the current version extern const char SNAPCurrentVersionReleaseDate[]; // Release date of the latest version whose user preference files are // incompatible with the current version and will be erased extern const char SNAPLastIncompatibleReleaseDate[]; // Build date - shown to help debugging nightly builds extern const char SNAPBuildInfo[]; // Voxel types // CAREFUL: do not redefine this to INT without disabling the usage of // UnaryFunctorCache in the GreyImageWrapper type. Greyscale instensities // 0 to MAXGREYVAL are used in a cache table, which would be too big with int typedef unsigned short LabelType; typedef short GreyType; extern const GreyType MAXGREYVAL; extern const GreyType MINGREYVAL; typedef itk::RGBPixel RGBType; /************************************************************************/ /* Enums that are common enough to declare them outside of a class */ /************************************************************************/ // Coverage mode for draw-over operations enum CoverageModeType { PAINT_OVER_ALL = 0, PAINT_OVER_VISIBLE, PAINT_OVER_ONE }; // Role played by an image layer. enum LayerRole { MAIN_ROLE = 0x0001, OVERLAY_ROLE = 0x0002, SNAP_ROLE = 0x0004, LABEL_ROLE = 0x0008, NO_ROLE = 0x0010, ALL_ROLES = 0xffffffff }; // Cardinal directions in the anatomical space enum AnatomicalDirection { ANATOMY_AXIAL = 0, ANATOMY_SAGITTAL, ANATOMY_CORONAL, ANATOMY_NONSENSE }; // An atomic data type to represent draw-over state struct DrawOverFilter { CoverageModeType CoverageMode; LabelType DrawOverLabel; bool operator == (const DrawOverFilter &cmp) const { return CoverageMode == cmp.CoverageMode && DrawOverLabel == cmp.DrawOverLabel; } bool operator != (const DrawOverFilter &cmp) const { return CoverageMode != cmp.CoverageMode || DrawOverLabel != cmp.DrawOverLabel; } DrawOverFilter() : CoverageMode(PAINT_OVER_ALL), DrawOverLabel(0) {} DrawOverFilter(CoverageModeType cm, LabelType label) : CoverageMode(cm), DrawOverLabel(label) {} DrawOverFilter(const DrawOverFilter &ref) : CoverageMode(ref.CoverageMode), DrawOverLabel(ref.DrawOverLabel) {} }; #define MAX_COLOR_LABELS 0xffff #define NUM_INITIAL_COLOR_LABELS 6 #define DEFAULT_HISTOGRAM_BINS 40 /** A debugging function to get the system time in ms. Actual definition is in SystemInterface.cxx */ long get_system_time_ms(); /** A child class around itk::SmartPointer. It's main purpose is to allow Qt Creator's intellisense to work with smart pointers. The current version (Qt 4.7.x) does not complete smart pointers because of typedefs used by the -> operator. In the future when this goes away (or does not matter) we can simply replace this class by "typedef itk::SmartPointer SmartPtr" */ template class SmartPtr : public itk::SmartPointer { public: typedef SmartPtr Self; typedef itk::SmartPointer Superclass; TObject* operator -> () const { return Superclass::GetPointer(); } SmartPtr(const Superclass &p) : Superclass(p) {} SmartPtr(TObject *p) : Superclass(p) {} SmartPtr() : Superclass() {} Self &operator =(const Self &p) { Superclass::operator =(p); return *this; } }; /************************************************************************/ /* PY: Some macros because I am tired of typing get/set */ /************************************************************************/ /** * Set macro borrowed from VTK and modified. Assumes m_ for private vars */ #define irisSetMacro(name,type) \ virtual void Set##name (type _arg) \ { \ this->m_##name = _arg; \ } /** * Get macro borrowed from VTK and modified. Assumes m_ for private vars */ #define irisGetMacro(name,type) \ virtual type Get##name () const { \ return this->m_##name; \ } /** * Get macro borrowed from VTK and modified. Assumes m_ for private vars */ #define irisStaticGetMacro(name,type) \ static type Get##name () const { \ return this->m_##name; \ } /** * Set macro borrowed from VTK and modified. Assumes m_ for private vars */ #define irisVirtualSetMacro(name,type) \ virtual void Set##name (type _arg) = 0; /** * Get macro borrowed from VTK and modified. Assumes m_ for private vars */ #define irisVirtualGetMacro(name,type) \ virtual type Get##name () const = 0; /** * Combo get/set macro */ #define irisGetSetMacro(name,type) \ virtual void Set##name (type _arg) \ { this->m_##name = _arg; } \ virtual type Get##name () const \ { return this->m_##name; } /** * Set macro for strings */ #define irisSetStringMacro(name) \ virtual void Set##name (const std::string &_arg) \ { \ this->m_##name = _arg; \ } /** * Get macro borrowed from VTK and modified. Assumes m_ for private vars */ #define irisGetStringMacro(name) \ virtual const char *Get##name () const { \ return this->m_##name.c_str(); \ } /** * Set macro for strings */ #define irisVirtualSetStringMacro(name) \ virtual void Set##name (const std::string &_arg) = 0; /** * Get macro borrowed from VTK and modified. Assumes m_ for private vars */ #define irisVirtualGetStringMacro(name) \ virtual const char *Get##name () const = 0; /** * A get macro for boolean variables, IsXXX instead of GetXXX */ #define irisIsMacro(name) \ virtual bool Is##name () const { \ return this->m_##name; \ } /** * A get macro for boolean variables, IsXXX instead of GetXXX */ #define irisVirtualIsMacro(name) \ virtual bool Is##name () const = 0; /** A macro combining the standard macros at the head of itk::Object classes */ #define irisITKObjectMacro(self,super) \ typedef self Self; \ typedef super Superclass; \ typedef SmartPtr Pointer; \ typedef SmartPtr ConstPointer; \ itkTypeMacro(self, super) \ itkNewMacro(Self) /** A macro combining the standard macros at the head of itk::Object classes, excluding the New construct */ #define irisITKAbstractObjectMacro(self,super) \ typedef self Self; \ typedef super Superclass; \ typedef SmartPtr Pointer; \ typedef SmartPtr ConstPointer; \ itkTypeMacro(self, super) /** * A rip off from the ITK not used macro to eliminate 'parameter not used' * warnings */ #define irisNotUsed(x) #endif // __SNAPCommonIO_h_ itksnap-3.4.0/Common/SNAPEventListenerCallbacks.h000066400000000000000000000045161263013355200216470ustar00rootroot00000000000000#ifndef SNAPEVENTLISTENERCALLBACKS_H #define SNAPEVENTLISTENERCALLBACKS_H #include "itkObject.h" #include "itkCommand.h" #include "vtkObject.h" #include "vtkCallbackCommand.h" #include "vtkSmartPointer.h" template unsigned long AddListener(itk::Object *sender, const itk::EventObject &event, TObserver *observer, void (TObserver::*memberFunction)()) { typedef itk::SimpleMemberCommand Cmd; typename Cmd::Pointer cmd = Cmd::New(); cmd->SetCallbackFunction(observer, memberFunction); return sender->AddObserver(event, cmd); } template unsigned long AddListener(itk::Object *sender, const itk::EventObject &event, TObserver *observer, void (TObserver::*memberFunction)(itk::Object*, const itk::EventObject &)) { typedef itk::MemberCommand Cmd; typename Cmd::Pointer cmd = Cmd::New(); cmd->SetCallbackFunction(observer, memberFunction); return sender->AddObserver(event, cmd); } template unsigned long AddListenerConst(itk::Object *sender, const itk::EventObject &event, TObserver *observer, void (TObserver::*memberFunction)(const itk::Object*, const itk::EventObject &)) { typedef itk::MemberCommand Cmd; typename Cmd::Pointer cmd = Cmd::New(); cmd->SetCallbackFunction(observer, memberFunction); return sender->AddObserver(event, cmd); } template unsigned long AddListenerPair( itk::Object *sender, const itk::EventObject &event, TObserver *observer, void (TObserver::*memberFunction)(itk::Object*, const itk::EventObject &), void (TObserver::*constMemberFunction)(const itk::Object*, const itk::EventObject &)) { typedef itk::MemberCommand Cmd; typename Cmd::Pointer cmd = Cmd::New(); cmd->SetCallbackFunction(observer, memberFunction); cmd->SetCallbackFunction(observer, constMemberFunction); return sender->AddObserver(event, cmd); } template unsigned long AddListenerVTK( vtkObject *sender, unsigned long event, TObserver *observer, void (TObserver::*memberFunction)(vtkObject*, unsigned long, void *)) { return sender->AddObserver(event, observer, memberFunction); } #endif // SNAPEVENTLISTENERCALLBACKS_H itksnap-3.4.0/Common/SNAPEvents.h000066400000000000000000000142771263013355200165310ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef SNAPEVENTS_H #define SNAPEVENTS_H #include "itkEventObject.h" #include /** To enable event debugging, ITK-SNAP must be compiled with the flag SNAP_DEBUG_EVENTS set. Also, the SNAP executable must be launched with the --debug-events option */ #define SNAP_DEBUG_EVENTS 1 // This defines the stream for event debugging #ifdef SNAP_DEBUG_EVENTS extern bool flag_snap_debug_events; #endif // Common events for the whole application itkEventMacro(IRISEvent, itk::AnyEvent) // Any event originating in VTK itkEventMacro(VTKEvent, IRISEvent) // Events fired by IRISApplication /** Event that never gets invoked */ itkEventMacro(NullEvent, IRISEvent) /** 3D cursor update event */ itkEventMacro(CursorUpdateEvent, IRISEvent) /** The set of layers loaded into SNAP has changed */ itkEventMacro(LayerChangeEvent, IRISEvent) /** The size of the main image has changed. */ itkEventMacro(MainImageDimensionsChangeEvent, LayerChangeEvent) /** The pose (orientation, spacing, origin) of the main image has changed */ itkEventMacro(MainImagePoseChangeEvent, LayerChangeEvent) /** The segmentation has changed */ itkEventMacro(SegmentationChangeEvent, IRISEvent) /** The level set image has changed (due to iteration) */ itkEventMacro(LevelSetImageChangeEvent, IRISEvent) /** Change to the speed image */ itkEventMacro(SpeedImageChangedEvent, LayerChangeEvent) /** Generic event representing that a model has changed */ itkEventMacro(ModelUpdateEvent, IRISEvent) /** Change in the linked zoom state */ itkEventMacro(LinkedZoomUpdateEvent, IRISEvent) /** A change to the UI state (see UIFlags.h) */ itkEventMacro(StateMachineChangeEvent, IRISEvent) /** The value of the common zoom has changed */ itkEventMacro(ZoomLevelUpdateEvent, IRISEvent) /** Toolbar mode change */ itkEventMacro(ToolbarModeChangeEvent, IRISEvent) /** Events used by numeric value models */ // The value of the numeric model changed itkEventMacro(ValueChangedEvent, IRISEvent) // The domain of the numeric model changed itkEventMacro(DomainChangedEvent, IRISEvent) // A special event where the domain description has changed, rather than // the whole domain. This never occurs for NumericRange domains, but does // occur for domains that are lists/sets of items. In that case, this event // represents the situation where the set of items remains the same, but the // description of some of the items has changed. The GUI needs to update how // the items are displayed, but does not need to rebuild the list of items. itkEventMacro(DomainDescriptionChangedEvent, IRISEvent) // An event used by property containers that indicates that one of the child // properties has been modified. For the time being, there does not seem to // be a need to distinguish between child property value changes and domain // changes, so there is just a single event. itkEventMacro(ChildPropertyChangedEvent, IRISEvent) /** A change to appearance of a renderer, etc */ itkEventMacro(AppearanceUpdateEvent, IRISEvent) /** Parent event for events fired by the image wrapper */ itkEventMacro(WrapperChangeEvent, IRISEvent) /** A change to a image wrapper property (e.g., nickname) */ itkEventMacro(WrapperMetadataChangeEvent, WrapperChangeEvent) /** A change to the visibility of a layer (stickiness, visibility) */ itkEventMacro(WrapperVisibilityChangeEvent, WrapperMetadataChangeEvent) /** A change to the display mapping of an image wrapper (e.g. color map) */ itkEventMacro(WrapperDisplayMappingChangeEvent, WrapperChangeEvent) /** A change to wrapper-associated user data */ itkEventMacro(WrapperUserDataChangeEvent, WrapperChangeEvent) /** A change to wrapper-associated image processing settings */ itkEventMacro(WrapperProcessingSettingsChangeEvent, WrapperUserDataChangeEvent) /** A change to the intensity curve */ itkEventMacro(IntensityCurveChangeEvent, WrapperDisplayMappingChangeEvent) /** A change to the color map */ itkEventMacro(ColorMapChangeEvent, WrapperDisplayMappingChangeEvent) /** Changes to the color label table */ itkEventMacro(SegmentationLabelChangeEvent, IRISEvent) itkEventMacro(SegmentationLabelConfigurationChangeEvent, SegmentationLabelChangeEvent) itkEventMacro(SegmentationLabelPropertyChangeEvent, SegmentationLabelChangeEvent) /** Label under cursor changed */ itkEventMacro(LabelUnderCursorChangedEvent, IRISEvent) /** Segmentation ROI changed */ itkEventMacro(SegmentationROIChangedEvent, IRISEvent) /** The mapping between display coordinates and anatomical coordinates changed */ itkEventMacro(DisplayToAnatomyCoordinateMappingChangeEvent, IRISEvent) // A setter method that fires events #define irisSetWithEventMacro(name,type,event) \ virtual void Set##name (type _arg) \ { \ if(this->m_##name != _arg) \ { \ this->m_##name = _arg; \ this->Modified(); \ this->InvokeEvent(event()); \ } \ } // A macro to add observers to specific events to objects. This is here just // to make the code more readable, because otherwise you never know what class // fires what events without looking in the code. #define irisDeclareEventObserver(event) \ virtual void AddObserver_##event (itk::Command *cmd) \ { this->AddObserver( event() , cmd ); } #define FIRES(event) virtual bool Fires##event() const { return true; } #endif // SNAPEVENTS_H itksnap-3.4.0/Common/SNAPOpenGL.cxx000066400000000000000000000024741263013355200167600ustar00rootroot00000000000000#include "SNAPOpenGL.h" #include #include void gl_draw_circle_with_border(double x, double y, double r, int vp_width, int vp_height, Vector3ui color) { static std::vector cx, cy; if(cx.size() == 0) { for(double a = 0; a < 2 * vnl_math::pi - 1.0e-6; a += vnl_math::pi / 20) { cx.push_back(cos(a)); cy.push_back(sin(a)); } } glPushMatrix(); glTranslated(x, y, 0.0); glScaled(1.2 / vp_width, 1.2 / vp_height, 1.0); glBegin(GL_TRIANGLE_FAN); glVertex2d(0, 0); for(size_t i = 0; i < cx.size(); i++) glVertex2d(r * cx[i], r * cy[i]); glVertex2d(r, 0); glEnd(); glColor3ub(color(0), color(1), color(2)); glBegin(GL_LINE_LOOP); for(size_t i = 0; i < cx.size(); i++) glVertex2d(r * cx[i], r * cy[i]); glEnd(); glPopMatrix(); } #ifdef __APPLE__ #include #if __MAC_10_8 #include void irisOrtho2D(double x, double w, double y, double h) { GLKMatrix4 matrix = GLKMatrix4MakeOrtho(x, w, y, h, -1, 1); glLoadMatrixf(matrix.m); } #else void irisOrtho2D(double x, double w, double y, double h) { gluOrtho2D(x,w,y,h); } #endif // APPLE #else void irisOrtho2D(double x, double w, double y, double h) { gluOrtho2D(x,w,y,h); } #endif itksnap-3.4.0/Common/SNAPOpenGL.h000066400000000000000000000075741263013355200164130ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPOpenGL.h,v $ Language: C++ Date: $Date: 2007/12/31 13:12:04 $ Version: $Revision: 1.3 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __SNAPOpenGL_h_ #define __SNAPOpenGL_h_ #include "IRISVectorTypes.h" // Include OpenGL headers according to the platform // #include #if defined(WIN32) #include #endif // Include GLU, for some reason taken out of Qt 4.8 #if defined (__APPLE__) #include #else #include #endif // Inline functions for use with vector classes inline void glVertex( const Vector3f &x ) { glVertex3fv(x.data_block()); } inline void glVertex( const Vector3d &x ) { glVertex3dv(x.data_block()); } inline void glVertex( const Vector3i &x ) { #if defined(__APPLE__) const GLint CastConvertTemp[3]={static_cast(x[0]),static_cast(x[1]),static_cast(x[2])}; glVertex3iv(CastConvertTemp);//NOTE: On Mac GLint is a long integer, so we must create a long integer vector to send to glVertex3iv. //A more elegant solution could be made if partial template specialization were allowed. //Perhaps Vector3i could be defined in terms of GLint instead of int #else glVertex3iv(x.data_block()); #endif } inline void glVertex( const Vector2f &x ) { glVertex2fv(x.data_block()); } inline void glVertex( const Vector2d &x ) { glVertex2dv(x.data_block()); } inline void glVertex( const Vector2i &x ) { #if defined(__APPLE__) const GLint CastConvertTemp[2]={static_cast(x[0]),static_cast(x[1])}; glVertex2iv(CastConvertTemp);//NOTE: On Mac GLint is a long integer, so we must create a long integer vector to send to glVertex3iv. //A more elegant solution could be made if partial template specialization were allowed. //Perhaps Vector2i could be defined in terms of GLint instead of int #else glVertex2iv(x.data_block()); #endif } inline void glTranslate( const Vector3f &x ) { glTranslatef(x[0],x[1],x[2]); } inline void glTranslate( const Vector3d &x ) { glTranslated(x[0],x[1],x[2]); } inline void glScale( const Vector3f &x ) { glScalef(x[0],x[1],x[2]); } inline void glScale( const Vector3d &x ) { glScaled(x[0],x[1],x[2]); } void gl_draw_circle_with_border(double x, double y, double r, int vp_width, int vp_height, Vector3ui color); Vector3d adjust_color_luminance(const Vector3d &color, double factor); // GLU functions are deprecated on Apple so we need our own versions void irisOrtho2D(double x, double w, double y, double h); #endif // __SNAPOpenGL_h_ itksnap-3.4.0/Common/SystemInterface.cxx000066400000000000000000000544631263013355200202640ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SystemInterface.cxx,v $ Language: C++ Date: $Date: 2010/04/16 05:14:38 $ Version: $Revision: 1.23 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifdef WIN32 #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif //_WIN32_WINNT #define _WIN32_WINNT 0x0600 #include #endif //WIN32 //#define WINVER 0x0600 //#define WINVER _WIN32_WINNT //#define _WIN32_IE 0x0500 #include "SystemInterface.h" #include "IRISException.h" #include "IRISApplication.h" #include "IRISException.h" #include "GlobalState.h" #include "SNAPRegistryIO.h" #include "HistoryManager.h" #include "UIReporterDelegates.h" #include #include #include "itkVoxBoCUBImageIOFactory.h" #include #include #include #include #include #ifdef WIN32 #include #include #include #include #else #include #include #include #include #include #endif using namespace std; using namespace itksys; // Initialize the info delegate to NULL SystemInfoDelegate *SystemInterface::m_SystemInfoDelegate = NULL; #if defined(WIN32) std::string SystemInterface::GetApplicationDataDirectory() { // TODO: when the username is not ASCII, this crashes! wchar_t path_w[4096]; DWORD bufferSize = GetEnvironmentVariableW(L"APPDATA", path_w, 4096); if (!bufferSize) throw IRISException("Can not access APPDATA path on WIN32."); // Convert to multi-byte int size_needed = WideCharToMultiByte(CP_UTF8, 0, &path_w[0], wcslen(path_w), NULL, 0, NULL, NULL); std::string utf8_path(size_needed, 0); WideCharToMultiByte (CP_UTF8, 0, &path_w[0], wcslen(path_w), &utf8_path[0], size_needed, NULL, NULL); if(utf8_path.length() == 0) throw IRISException("Can not convert APPDATA path to multi-byte string"); // Temporary crap-out if(utf8_path.length() != wcslen(path_w)) throw IRISException("ITK-SNAP currently does not support non-ASCII characters in user names on Windows (tm) platforms. This will be fixed in the future."); // Append the full information std::string strPath = utf8_path + "/itksnap.org/ITK-SNAP"; itksys::SystemTools::ConvertToUnixSlashes(strPath); return strPath; } /* std::string SystemInterface::GetApplicationDataDirectory() { // This old code seems unnecessary - Qt delegate returns the right place for Mac // std::string path("~/Library/Application Support/itksnap.org/ITK-SNAP"); std::string path = m_SystemInfoDelegate->GetApplicationPermanentDataLocation(); itksys::SystemTools::ConvertToUnixSlashes(path); return path; } */ #elif defined(__APPLE__) std::string SystemInterface::GetApplicationDataDirectory() { // This old code seems unnecessary - Qt delegate returns the right place for Mac // std::string path("~/Library/Application Support/itksnap.org/ITK-SNAP"); std::string path = m_SystemInfoDelegate->GetApplicationPermanentDataLocation(); itksys::SystemTools::ConvertToUnixSlashes(path); return path; } #else std::string SystemInterface::GetApplicationDataDirectory() { std::string path("~/.itksnap.org/ITK-SNAP"); itksys::SystemTools::ConvertToUnixSlashes(path); return path; } #endif SystemInterface ::SystemInterface() { // Crash if no delegate if(!SystemInterface::m_SystemInfoDelegate) throw IRISException("Creating SystemInterface without a global SystemInfoDelegate set!"); // Initialize the registry m_RegistryIO = new SNAPRegistryIO; // Initialize the history manager m_HistoryManager = new HistoryManager(); // Register the Image IO factories that are not part of ITK itk::ObjectFactoryBase::RegisterFactory( itk::VoxBoCUBImageIOFactory::New() ); // Make sure we have a preferences directory std::string appdir = GetApplicationDataDirectory(); if(!itksys::SystemTools::MakeDirectory(appdir.c_str())) throw IRISException("Unable to create application data directory %s.", appdir.c_str()); // Set the preferences file m_UserPreferenceFile = appdir + "/UserPreferences.xml"; } SystemInterface ::~SystemInterface() { delete m_RegistryIO; delete m_HistoryManager; } string SystemInterface::GetFullPathToExecutable() const { assert(m_SystemInfoDelegate); return m_SystemInfoDelegate->GetApplicationFile(); } void SystemInterface ::SaveUserPreferences() { // Write all the global histories to the registry m_HistoryManager->SaveGlobalHistory(this->Folder("IOHistory")); // Write the registry to disk WriteToXMLFile(m_UserPreferenceFile.c_str()); } void SystemInterface ::LoadUserPreferences() { // Check if the file exists, may throw an exception here if(SystemTools::FileExists(m_UserPreferenceFile.c_str())) { // Read the contents of the preferences from file ReadFromXMLFile(m_UserPreferenceFile.c_str()); // Check if the preferences contain a version string string version = Entry("System.CreatedBySNAPVersion")["00000000"]; // If the version is less than the latest incompatible version, delete the // contents of the version if(atoi(version.c_str()) < atoi(SNAPLastIncompatibleReleaseDate)) { // Clear the contents of the registry since it's incompatible this->Clear(); } } // Enter the current SNAP version into the registry Entry("System.CreatedBySNAPVersion") << SNAPCurrentVersionReleaseDate; // Read all the global histories from the file. m_HistoryManager->LoadGlobalHistory(this->Folder("IOHistory")); } void SystemInterface ::LaunchChildSNAP(int argc, char **argv, bool terminate_parent) { // Create new argument list char **newargv = new char * [argc + 2]; // Zeroth argument remains newargv[0] = argv[0]; // First argument is --no-fork newargv[1] = new char[40]; strcpy(newargv[1], "--no-fork"); // Now copy all the other parameters for (int i = 1; i < argc; i++) newargv[i + 1] = argv[i]; newargv[argc + 1] = NULL; // Now we have a nice argument list to send to the child SNAP process #ifdef WIN32 // On windows, these functions screw up when arguments contain spaces! char **quoted = new char * [argc + 2]; for (int i = 0; i < argc + 1; i++) { if (strchr(newargv[i], ' ')) { // On windows, try to get the short path instead long length = 0; // First obtain the size needed by passing NULL and 0. length = GetShortPathName(newargv[i], NULL, 0); if (length == 0) throw IRISException("Unable to obtain short filename for %s", newargv[i]); // Dynamically allocate the correct size // (terminating null char was included in length) quoted[i] = new char[length]; // Now simply call again using same long path. length = GetShortPathName(newargv[i], quoted[i], length); if (length == 0) throw IRISException("Unable to obtain short filename for %s", newargv[i]); } else { quoted[i] = new char[strlen(newargv[i]) + 1]; strcpy(quoted[i], newargv[i]); } } quoted[argc + 1] = NULL; _spawnvp(_P_NOWAIT, newargv[0], quoted); #else pid_t pid = fork(); if(pid == 0) { /* Make sure we survive our shell */ setsid(); /* Restarts the vim process, will not return. */ execvp(argv[0], newargv); /* Should never get here! */ exit(-1); } else { if(terminate_parent) exit(0); } #endif } void SystemInterface ::LaunchChildSNAPSimple(std::list args) { // Must have a valid path to the EXE std::string exefile = this->GetFullPathToExecutable(); if(exefile.length() == 0) throw IRISException("Path to executable unknown in LaunchChildSNAP"); // Create the argument array char **argv = new char* [args.size()+2]; int iarg = 0; argv[iarg++] = (char *) exefile.c_str(); for(std::list::iterator it=args.begin(); it!=args.end(); ++it) argv[iarg++] = (char *) it->c_str(); argv[iarg++] = NULL; // Create child process LaunchChildSNAP(args.size()+1, argv, false); } std::string SystemInterface ::FindUniqueCodeForFile(const char *file, bool generate_if_not_found) { // Convert the filename to absolute path string path = SystemTools::CollapseFullPath(file); // Convert to unix slashes for consistency SystemTools::ConvertToUnixSlashes(path); // Encode the filename as ASCII path = EncodeFilename(path); // Get the key associated with this filename string key = this->Key("ImageAssociation.Mapping.Element[%s]",path.c_str()); // Return the existing code for this key string code = this->Entry(key)[""]; // If the code was not found, create a new code if requested if(generate_if_not_found && code.size() == 0) { // Compute a timestamp from the start of computer time time_t timestr = time(NULL); // Compute a hash long hash = 0; for(int i = 0; i < path.size(); i+=sizeof(long)) { long word = 0; for(int j = i, k = 0; j < path.size() && k < sizeof(long); j++,k++) word += ((long) path[j]) << (8 * k); hash ^= word; } // Create a key for the file IRISOStringStream scode; scode << setfill('0') << setw(16) << hex << timestr; scode << setfill('0') << setw(2 * sizeof(long)) << hex << hash; // Return the existing key or the new key code = scode.str(); // Store the code this->Entry(key) << code; } return code; } bool SystemInterface ::FindRegistryAssociatedWithFile(const char *file, Registry ®istry) { // Find the code previously associated with that file string code = this->FindUniqueCodeForFile(file, false); // If the code does not exist, return w/o success if(code.length() == 0) return false; // Generate the association filename string appdir = GetApplicationDataDirectory(); string assfil = appdir + "/ImageAssociations/" + code + ".xml"; // Try loading the registry try { registry.ReadFromXMLFile(assfil.c_str()); return true; } catch(...) { return false; } } string SystemInterface ::EncodeObjectName(string text) { ostringstream oss; size_t n = text.length(); for(size_t i = 0; i < n; i++) { char c = text[i]; if(c >= 'a' && c <= 'z') oss << c; else if(c >= 'A' && c <= 'Z') oss << c; else if(c >= '0' && c <= '9') oss << c; else if(c == ' ') oss << "__"; else { char buffer[5]; sprintf(buffer, "_%03d", (int) c); oss << buffer; } } return oss.str(); } string SystemInterface ::DecodeObjectName(string fname) { ostringstream oss; size_t n = fname.length(); for(size_t i = 0; i < n; i++) { char c = fname[i]; if(c == '_') { if(i+1 < n && fname[i+1] == '_') { oss << ' '; i++; } else if(i+3 < n) { oss << (char)(atoi(fname.substr(i+1,3).c_str())); i+=3; } else return ""; } else oss << c; } return oss.str(); } vector SystemInterface ::GetSavedObjectNames(const char *category) { // Make sure we have a directory for this category std::string appdir = GetApplicationDataDirectory(); std::string catdir = appdir + std::string("/") + std::string(category); if(!itksys::SystemTools::MakeDirectory(catdir.c_str())) throw IRISException("Unable to create data directory %s", catdir.c_str()); // Get the names vector names; // Get the listing of all files in there Directory dlist; dlist.Load(catdir.c_str()); for(size_t i = 0; i < dlist.GetNumberOfFiles(); i++) { string fname = dlist.GetFile(i); // Check regular file ostringstream ffull; ffull << catdir << "/" << fname; if(SystemTools::FileExists(ffull.str().c_str(), true)) { // Check extension if(SystemTools::GetFilenameExtension(fname) == ".xml") { string base = SystemTools::GetFilenameWithoutExtension(fname); string name = DecodeObjectName(base); if(name.length()) names.push_back(name); } } } return names; } Registry SystemInterface ::ReadSavedObject(const char *category, const char *name) { // Make sure we have a directory for this category std::string appdir = GetApplicationDataDirectory(); std::string catdir = appdir + std::string("/") + std::string(category); if(!itksys::SystemTools::MakeDirectory(catdir.c_str())) throw IRISException("Unable to create data directory %s", catdir.c_str()); // Create a save filename IRISOStringStream sfile; sfile << catdir << "/" << EncodeObjectName(name) << ".xml"; string fname = sfile.str(); // Check the filename if(!SystemTools::FileExists(fname.c_str(), true)) throw IRISException("Saved object file does not exist"); Registry reg; reg.ReadFromXMLFile(fname.c_str()); return reg; } void SystemInterface ::UpdateSavedObject(const char *category, const char *name, Registry &folder) { // Make sure we have a directory for this category std::string appdir = GetApplicationDataDirectory(); std::string catdir = appdir + std::string("/") + std::string(category); if(!itksys::SystemTools::MakeDirectory(catdir.c_str())) throw IRISException("Unable to create data directory %s", catdir.c_str()); // Create a save filename IRISOStringStream sfile; sfile << catdir << "/" << EncodeObjectName(name) << ".xml"; string fname = sfile.str(); // Save the data folder.WriteToXMLFile(fname.c_str()); } void SystemInterface ::DeleteSavedObject(const char *category, const char *name) { // Make sure we have a directory for this category std::string appdir = GetApplicationDataDirectory(); std::string catdir = appdir + std::string("/") + std::string(category); if(!itksys::SystemTools::MakeDirectory(catdir.c_str())) throw IRISException("Unable to create data directory %s", catdir.c_str()); // Create a save filename IRISOStringStream sfile; sfile << catdir << "/" << EncodeObjectName(name) << ".txt"; // Save the data SystemTools::RemoveFile(sfile.str().c_str()); } string SystemInterface ::EncodeFilename(const string &src) { IRISOStringStream sout; const char *chararray = src.c_str(); for(unsigned int i=0;iGetApplicationDataDirectory(); string assdir = appdir + "/ImageAssociations"; if(!SystemTools::MakeDirectory(assdir.c_str())) throw IRISException("Unable to create image associations directory %s", assdir.c_str()); // Create the association filename string assfil = assdir + "/" + code + ".xml"; // Store the registry to that path try { registry.WriteToXMLFile(assfil.c_str()); return true; } catch(...) { return false; } } bool SystemInterface ::AssociateCurrentSettingsWithCurrentImageFile(const char *file, IRISApplication *app) { // Get a registry already associated with this filename Registry registry; FindRegistryAssociatedWithFile(file,registry); // Write the current state into that registry m_RegistryIO->WriteImageAssociatedSettings(app,registry); // Write the registry back return AssociateRegistryWithFile(file,registry); } std::string SystemInterface ::GetThumbnailAssociatedWithFile(const char *file) { // Get a string giving the thumbnail name string code = this->FindUniqueCodeForFile(file, true); // Create an association file in the settings directory string appdir = this->GetApplicationDataDirectory(); string thumbdir = appdir + "/Thumbnails"; if(!SystemTools::MakeDirectory(thumbdir.c_str())) throw IRISException("Unable to create thumbnail directory %s", thumbdir.c_str()); // Create the association filename return thumbdir + "/" + code + ".png"; } bool SystemInterface ::RestoreSettingsAssociatedWithImageFile( const char *file, IRISApplication *app, bool restoreLabels, bool restorePreprocessing, bool restoreParameters, bool restoreDisplayOptions) { // Get a registry already associated with this filename Registry registry; if(FindRegistryAssociatedWithFile(file,registry)) { m_RegistryIO->ReadImageAssociatedSettings( registry, app, restoreLabels, restorePreprocessing, restoreParameters, restoreDisplayOptions); return true; } else return false; } // Socket headers #ifdef WIN32 #include #else #include #include #include #endif SystemInterface::UpdateStatus SystemInterface ::CheckUpdate(std::string &newversion, size_t sec, size_t usec, bool force) { if (force) { // std::cout << "user initiated update" << std::endl; Entry("System.LastUpdateTimeStamp") << time(NULL); } else { // Check the last update time stamp string lastUpdateTimeStamp = Entry("System.LastUpdateTimeStamp")["00000000"]; // check for update every week if (atoi(lastUpdateTimeStamp.c_str()) == 0) { Entry("System.LastUpdateTimeStamp") << time(NULL); } else if (atoi(lastUpdateTimeStamp.c_str()) + 604800 >= time(NULL)) { return US_TOO_SOON; } else { Entry("System.LastUpdateTimeStamp") << time(NULL); } } int rv = -1, sockfd = -1, n = -1; UpdateStatus us = US_CONNECTION_FAILED; #ifdef WIN32 // Initialize windows sockets WSADATA wsaData; if(WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) return US_CONNECTION_FAILED; #endif // Remaining operations require closing socket/WSA on failure try { #ifdef WIN32 // Resolve the host struct hostent *he; if((he = gethostbyname("www.itksnap.org")) == NULL) throw IRISException("Can't resolve address"); // Get the IP address char *ipaddr = inet_ntoa (*(struct in_addr *)*he->h_addr_list); // Set up the socket if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0) throw IRISException("socket creation failed"); // Connect struct sockaddr_in sa; sa.sin_family = AF_INET; sa.sin_addr.s_addr = inet_addr(ipaddr); sa.sin_port = htons(80); if((rv = connect(sockfd, (sockaddr *)&sa, sizeof(sa))) < 0) throw IRISException("connect failed"); #else // Get address info struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if((rv = getaddrinfo("www.itksnap.org","80",&hints,&res)) != 0) throw IRISException("getaddrinfo failed"); // Create socket if((sockfd=socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) throw IRISException("socket creation failed"); // Connect to server if((rv = connect(sockfd, res->ai_addr, res->ai_addrlen)) < 0) throw IRISException("connect failed"); #endif // Create the HTTP request ostringstream oss; oss << "GET /version3/" << SNAPArch << ".txt HTTP/1.1\r\nHost: www.itksnap.org\r\n\r\n"; // Create HTTP request if((n = send(sockfd, oss.str().c_str(), oss.str().length(), 0)) < 0) throw IRISException("Can't write to server"); // Set up select to watch for data available fd_set readfds; struct timeval tv; FD_ZERO(&readfds); FD_SET(sockfd, &readfds); tv.tv_sec = sec; tv.tv_usec = usec; if((rv = select(sockfd+1, &readfds, NULL, NULL, &tv)) < 0) throw IRISException("Select failed"); else if(rv == 0) throw IRISException("Timed out"); // Read buffer char buffer[0x8000]; if((n = recv(sockfd, buffer, 0x7fff, 0)) <= 0) throw IRISException("Can't read from server"); // Parse the output istringstream iss(string(buffer, n)); char line[1024]; // Check first line iss.getline(line, 1024); if(strncmp(line,"HTTP/1.1 200 OK",strlen("HTTP/1.1 200 OK"))) throw IRISException("HTTP request failed"); // Read lines from output until two blank lines while(strlen(line) > 0 && line[0] != '\r' && !iss.eof()) { iss.getline(line, 1024); } // Read the next four values unsigned int vmajor = 0, vminor = 0, vpatch = 0; string vqual, vdate; iss >> vdate; iss >> vmajor; iss >> vminor; iss >> vpatch; iss >> vqual; // Format the version in printable format ostringstream over; over << vmajor << "." << vminor << "." << vpatch; if(vqual.length()) over << "-" << vqual; newversion = over.str(); // Compare version if(atoi(vdate.c_str()) > atoi(SNAPCurrentVersionReleaseDate)) us = US_OUT_OF_DATE; else us = US_UP_TO_DATE; } catch(...) { us = US_CONNECTION_FAILED; } // Close socket if necessary if(sockfd >= 0) #ifdef WIN32 closesocket(sockfd); #else close(sockfd); #endif // Windows cleanup #ifdef WIN32 WSACleanup(); #endif return us; } long get_system_time_ms() { #ifdef WIN32 SYSTEMTIME time; GetSystemTime(&time); WORD millis = (time.wSecond * 1000) + time.wMilliseconds; return (long) millis; #else timeval time; gettimeofday(&time, NULL); long millis = (time.tv_sec * 1000) + (time.tv_usec / 1000); return millis; #endif } itksnap-3.4.0/Common/SystemInterface.h000066400000000000000000000150671263013355200177060ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SystemInterface.h,v $ Language: C++ Date: $Date: 2010/04/14 10:06:23 $ Version: $Revision: 1.11 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __SystemInterface_h_ #define __SystemInterface_h_ #include "Registry.h" #include "GlobalState.h" class IRISApplication; class SNAPRegistryIO; class HistoryManager; class SystemInfoDelegate; class vtkCamera; #ifdef WIN32 #include #endif /** * \class SystemInterface * \brief An interface between SNAP and the operating system. * This class is responsible for finding the system directory, reading and * writing user preferences to disk, etc. */ class SystemInterface : public Registry { public: SystemInterface(); virtual ~SystemInterface(); /** * Set a pointer to the delegate class that can be used to access * system-specific information. This method should be called before * the first time SystemInterface is constructed, otherwise an exception * will be fired. */ static SystemInfoDelegate * GetSystemInfoDelegate() { return m_SystemInfoDelegate; } static void SetSystemInfoDelegate(SystemInfoDelegate *_arg) { m_SystemInfoDelegate = _arg; } /** * Get the full path to executable */ std::string GetFullPathToExecutable() const; /** Loads the registry containing user preferences */ void LoadUserPreferences(); /** Save the preferences */ void SaveUserPreferences(); /** Get the filename for the user preferences */ const char *GetUserPreferencesFileName() { return m_UserPreferenceFile.c_str(); } /** The name of the token file that sits at the root of the program data * directory */ static const char *GetProgramDataDirectoryTokenFileName() { return "SNAPProgramDataDirectory.txt"; } /** Get a filename history list by a particular name */ irisGetMacro(HistoryManager, HistoryManager*); /** Find and load a registry file associated with a filename in the system.*/ bool FindRegistryAssociatedWithFile(const char *file, Registry ®istry); /** Find and load a registry file associated with a filename in the system */ bool AssociateRegistryWithFile(const char *file, Registry ®istry); /** Get the thumbnail filename associated with an image file */ std::string GetThumbnailAssociatedWithFile(const char *file); /** A higher level method: associates current settings with the current image * so that the next time the image is loaded, it can be saved */ bool AssociateCurrentSettingsWithCurrentImageFile( const char *file,IRISApplication *app); /** A higher level method: associates current settings with the current image * so that the next time the image is loaded, it can be saved */ bool RestoreSettingsAssociatedWithImageFile( const char *file, IRISApplication *app, bool restoreLabels, bool restorePreprocessing, bool restoreParameters, bool restoreDisplayOptions); /** * This is a newer facility that makes saving registry objects more generic * and atomic. Basically, any 'object' can be saved, read or deleted in the * user's directory. Objects are grouped by categories. The main difference * with the registry is that the objects are stored on disk, so different * SNAP options are less likely to interfere with each other. */ std::vector GetSavedObjectNames(const char *category); Registry ReadSavedObject(const char *category, const char *name); void UpdateSavedObject(const char *category, const char *name, Registry &folder); void DeleteSavedObject(const char *category, const char *name); static std::string DecodeObjectName(std::string fname); static std::string EncodeObjectName(std::string fname); /** * This is a more complex method for loading a file. It work for files that have * been opened previously, so that the user specified the necessary parameters * for loading. It works with conjunction with the registry class */ void LoadPreviousGreyImageFile(const char *filename, Registry *registry); /** Enum for automatic update checking */ enum UpdateStatus { US_UP_TO_DATE, US_OUT_OF_DATE, US_CONNECTION_FAILED, US_TOO_SOON }; /** Check if an update is available. First parameter is output version string, other parameters specify timeout in seconds, millonth of a second */ UpdateStatus CheckUpdate(std::string &newversion, size_t sec, size_t usec, bool force = false); /** * Launch a child SNAP process with specified command line options. The first * option in argv should be the path to the SNAP executable */ static void LaunchChildSNAP(int argc, char **argv, bool terminate_parent); /** Simplified, non-static version of the above */ void LaunchChildSNAPSimple(std::list args); private: std::string m_UserPreferenceFile; std::string m_DocumentationDirectory; // An object used to write large chunks of SNAP data to the registry SNAPRegistryIO *m_RegistryIO; // History manager HistoryManager *m_HistoryManager; // Delegate static SystemInfoDelegate *m_SystemInfoDelegate; // Filename encoder std::string EncodeFilename(const std::string &src); // Associate a file with a unique code std::string FindUniqueCodeForFile(const char *file, bool generate_if_not_found); // Get the directory where application data (pref files, etc) should go std::string GetApplicationDataDirectory(); }; #endif //__SystemInterface_h_ itksnap-3.4.0/Common/ThreadSpecificData.cxx000066400000000000000000000050761263013355200206220ustar00rootroot00000000000000#include "ThreadSpecificData.h" #include "itkMultiThreader.h" #include "IRISException.h" #if defined(ITK_USE_PTHREADS) void ThreadSpecificDataSupport::Deleter(void *p) { free(p); } ThreadSpecificDataSupport::ThreadSpecificDataSupport() { // Allocate storage for a key pthread_key_t *pkey = new pthread_key_t[1]; // Create the key int rc = pthread_key_create(pkey, &ThreadSpecificDataSupport::Deleter); if(rc) throw IRISException("pthread_key_create failed with rc = %d", rc); // Store the key m_KeyPointer = pkey; } ThreadSpecificDataSupport::~ThreadSpecificDataSupport() { // Delete the key pthread_key_t *pkey = (pthread_key_t *) m_KeyPointer; int rc = pthread_key_delete(pkey[0]); if(rc) throw IRISException("pthread_key_delete failed with rc = %d", rc); } void *ThreadSpecificDataSupport::GetPtrCreateIfNeeded(size_t data_size) { pthread_key_t *pkey = (pthread_key_t *) m_KeyPointer; void *pdata = pthread_getspecific(pkey[0]); if(!pdata) { pdata = malloc(data_size); int rc = pthread_setspecific(pkey[0], pdata); if(rc) throw IRISException("pthread_setspecific failed with rc = %d", rc); } return pdata; } void *ThreadSpecificDataSupport::GetPtr() const { pthread_key_t *pkey = (pthread_key_t *) m_KeyPointer; void *pdata = pthread_getspecific(pkey[0]); return pdata; } #elif defined(ITK_USE_WIN32_THREADS) void ThreadSpecificDataSupport::Deleter(void *p) { } ThreadSpecificDataSupport::ThreadSpecificDataSupport() { DWORD *key = new DWORD[1]; key[0] = TlsAlloc(); if(key[0] == TLS_OUT_OF_INDEXES) throw IRISException("TlsAlloc failed with error %d", GetLastError()); m_KeyPointer = key; } ThreadSpecificDataSupport::~ThreadSpecificDataSupport() { DWORD *key = (DWORD *) m_KeyPointer; // Delete the stored stuff void *pdata = TlsGetValue(key[0]); if(pdata) free(pdata); TlsFree(key[0]); delete [] key; } void *ThreadSpecificDataSupport::GetPtrCreateIfNeeded(size_t data_size) { DWORD *key = (DWORD *) m_KeyPointer; void *pdata = TlsGetValue(key[0]); if(!pdata) { if(GetLastError() != ERROR_SUCCESS) throw IRISException("TlsGetValue failed with error %d", GetLastError()); pdata = malloc(data_size); if(!TlsSetValue(key[0], pdata)) throw IRISException("TlsSetValue failed with error %d", GetLastError()); } return pdata; } void *ThreadSpecificDataSupport::GetPtr() const { DWORD *key = (DWORD *) m_KeyPointer; void *pdata = TlsGetValue(key[0]); return pdata; } #else #pragma warning("No support for non-pthread threads in ITK-SNAP yet!") #endif itksnap-3.4.0/Common/ThreadSpecificData.h000066400000000000000000000037641263013355200202510ustar00rootroot00000000000000#ifndef THREADSPECIFICDATA_H #define THREADSPECIFICDATA_H #include /** Support class for ThreadSpecificData */ class ThreadSpecificDataSupport { public: ThreadSpecificDataSupport(); ~ThreadSpecificDataSupport(); /** Get a pointer to the cached data, allocating memory of data_size bytes if cached data does not exist. The data will be automatically deallocated when the thread terminates */ void *GetPtrCreateIfNeeded(size_t datasize); /** Get a pointer. If it has not been created (a value already assigned), then this will return NULL. */ void *GetPtr() const; protected: static void Deleter(void *p); // The key. To avoid having to do platform-specific stuff here, // we just make this key a void pointer. The key is used to create // specific data objects; void *m_KeyPointer; }; /** Encapsulates the functionality provided by pthread_key_create, pthread_getspecific and pthread_setspecific. This allows an instance of an atomic type to be cached on a per-thread basis. To use this class in an ITK filter, just add a variable of this type to the class. Then, inside of the threaded code, use the object as you would a regular variable to set and get the values. */ template class ThreadSpecificData { public: typedef ThreadSpecificData Self; /** Assign a value to the cache. Make sure to do this before trying to access the value using the cast operator below, or you will get a segfault. */ Self & operator = (const TAtomic &value) { TAtomic *p = (TAtomic *)(m_Support.GetPtrCreateIfNeeded(sizeof(TAtomic))); *p = value; return *this; } /** Get the value from the cache. If the value has not been previously set, this will probably segfault. For speed, there is no check implemented! */ operator TAtomic() const { TAtomic *p = (TAtomic *)(m_Support.GetPtr()); return *p; } protected: ThreadSpecificDataSupport m_Support; }; #endif // THREADSPECIFICDATA_H itksnap-3.4.0/Common/Trackball.cxx000066400000000000000000000137751263013355200170570ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Trackball.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:29 $ Version: $Revision: 1.2 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #include "Trackball.h" #include #include #include using std::cerr; using std::endl; #ifndef M_PI #define M_PI 3.14159265358979323846 #endif Trackball ::Trackball() { m_TrackingMotion = GL_FALSE; this->Reset(); } Trackball ::Trackball( const Trackball& M ) { *this = M; } Trackball ::~Trackball() { } void Trackball ::Reset() { int i,j; m_Angle = 0.0; for (i=0; i<4; i++) { for (j=0; j<4; j++) m_RotationMatrix[i][j] = 0.0; m_RotationMatrix[i][i] = 1.0; } m_Zoom = m_OldZoom = 1.0; m_PanX = m_OldPanX = 0.0; m_PanY = m_OldPanY = 0.0; } void Trackball ::StartPan( int x, int y ) { m_TrackingMotion = GL_TRUE; m_LastPosition[0] = (float)x; m_LastPosition[1] = (float)y; } void Trackball ::StopPan() { m_TrackingMotion = GL_FALSE; m_OldPanX = m_PanX; m_OldPanY = m_PanY; } void Trackball ::TrackPan( int x, int y, int w, int h, float ratew, float rateh ) { if ( m_TrackingMotion ) { float dx, dy; dx = ratew*(x - m_LastPosition[0])/w; dy = rateh*(y - m_LastPosition[1])/h; m_PanX = m_OldPanX + dx; m_PanY = m_OldPanY - dy; } } void Trackball ::StartZoom( int y ) { m_TrackingMotion = GL_TRUE; m_LastPosition[0] = (float)y; } void Trackball ::StopZoom() { m_TrackingMotion = GL_FALSE; m_OldZoom = m_Zoom; } void Trackball ::TrackZoom( int y ) { if ( m_TrackingMotion ) { float dy; dy = y - m_LastPosition[0]; // m_Zoom = m_OldZoom - dy/h; // m_Zoom = m_OldZoom - dy; m_Zoom = m_OldZoom * (float) pow(1.01f,-dy); if ( m_Zoom < 0.0 ) m_Zoom = 0.0; } } void Trackball ::StartRot( int x, int y, int w, int h ) { m_TrackingMotion = GL_TRUE; PToV( x, y, w, h, m_LastPosition ); } void Trackball ::StopRot() { m_TrackingMotion = GL_FALSE; m_Angle = 0.0; } void Trackball ::TrackRot( int x, int y, int w, int h ) { if ( m_TrackingMotion ) { float curPos[3], dx, dy, dz; PToV(x, y, w, h, curPos); dx = curPos[0] - m_LastPosition[0]; dy = curPos[1] - m_LastPosition[1]; dz = curPos[2] - m_LastPosition[2]; m_Angle = (float)(90.0 * sqrt(dx*dx + dy*dy + dz*dz)); m_Axis[0] = m_LastPosition[1]*curPos[2] - m_LastPosition[2]*curPos[1]; m_Axis[1] = m_LastPosition[2]*curPos[0] - m_LastPosition[0]*curPos[2]; m_Axis[2] = m_LastPosition[0]*curPos[1] - m_LastPosition[1]*curPos[0]; m_LastPosition[0] = curPos[0]; m_LastPosition[1] = curPos[1]; m_LastPosition[2] = curPos[2]; /* Have OpenGL compute the new transformation (simple but slow). */ glMatrixMode( GL_MODELVIEW ); glPushMatrix(); glLoadIdentity(); glRotatef(m_Angle, m_Axis[0], m_Axis[1], m_Axis[2]); glMultMatrixf((GLfloat *) m_RotationMatrix); glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) m_RotationMatrix); glPopMatrix(); } } void Trackball ::PToV(int x, int y, int width, int height, float v[3]) { float d, a; /* project x,y onto a hemi-sphere centered within width, height */ v[0] = ((float)2.0*x - width) / width; v[1] = (height - (float)2.0*y) / height; d = (float)sqrt(v[0]*v[0] + v[1]*v[1]); v[2] = (float)cos((M_PI/2.0) * ((d < 1.0) ? d : 1.0)); a = (float)(1.0 / sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])); v[0] *= a; v[1] *= a; v[2] *= a; } /* *$Log: Trackball.cxx,v $ *Revision 1.2 2007/12/30 04:05:29 pyushkevich *GPL License * *Revision 1.1 2006/12/02 04:22:27 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:17 pauly2 *Import * *Revision 1.6 2004/08/26 18:29:20 pauly *ENH: New user interface for configuring the UI options * *Revision 1.5 2003/10/02 14:55:53 pauly *ENH: Development during the September code freeze * *Revision 1.1 2003/09/11 13:51:15 pauly *FIX: Enabled loading of images with different orientations *ENH: Implemented image save and load operations * *Revision 1.3 2003/08/27 14:03:23 pauly *FIX: Made sure that -Wall option in gcc generates 0 warnings. *FIX: Removed 'comment within comment' problem in the cvs log. * *Revision 1.2 2003/08/27 04:57:47 pauly *FIX: A large number of bugs has been fixed for 1.4 release * *Revision 1.1 2003/07/12 04:46:51 pauly *Initial checkin of the SNAP application into the InsightApplications tree. * *Revision 1.2 2003/07/12 01:34:18 pauly *More final changes before ITK checkin * *Revision 1.1 2003/07/11 23:25:33 pauly **** empty log message *** * *Revision 1.1 2003/03/07 19:29:48 pauly *Initial checkin * *Revision 1.2 2002/12/16 16:40:19 pauly **** empty log message *** * *Revision 1.1.1.1 2002/12/10 01:35:36 pauly *Started the project repository * * *Revision 1.2 2002/03/08 14:06:32 moon *Added Header and Log tags to all files **/ itksnap-3.4.0/Common/Trackball.h000066400000000000000000000074551263013355200165020ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Trackball.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:29 $ Version: $Revision: 1.2 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __Trackball_h_ #define __Trackball_h_ #include /** * \class Trackball * \brief Virtual trackball for the 3D window * * \sa Window3D */ class Trackball { private: GLboolean m_TrackingMotion; float m_Angle; float m_Axis[3]; float m_LastPosition[3]; GLfloat m_RotationMatrix[4][4]; GLfloat m_Zoom, m_OldZoom; GLfloat m_PanX, m_PanY; GLfloat m_OldPanX, m_OldPanY; void PToV( int x, int y, int width, int height, float v[3] ); public: Trackball(); Trackball( const Trackball& T ); ~Trackball(); void Reset(); void StartPan( int x, int y ); void StopPan(); void TrackPan( int x, int y, int w, int h, float ratew, float rateh ); void StartZoom( int y ); void StopZoom(); void TrackZoom( int y ); void StartRot( int x, int y, int w, int h ); void StopRot(); void TrackRot( int x, int y, int w, int h ); inline GLfloat *GetRot() { return( (GLfloat*) m_RotationMatrix ); }; inline GLfloat GetZoom() { return( m_Zoom ); }; inline GLfloat GetPanX() { return( m_PanX ); }; inline GLfloat GetPanY() { return( m_PanY ); }; }; #endif // __Trackball_h_ /* *$Log: Trackball.h,v $ *Revision 1.2 2007/12/30 04:05:29 pyushkevich *GPL License * *Revision 1.1 2006/12/02 04:22:27 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:17 pauly2 *Import * *Revision 1.5 2004/08/26 18:29:20 pauly *ENH: New user interface for configuring the UI options * *Revision 1.4 2003/10/02 14:55:53 pauly *ENH: Development during the September code freeze * *Revision 1.1 2003/09/11 13:51:15 pauly *FIX: Enabled loading of images with different orientations *ENH: Implemented image save and load operations * *Revision 1.3 2003/08/27 14:03:24 pauly *FIX: Made sure that -Wall option in gcc generates 0 warnings. *FIX: Removed 'comment within comment' problem in the cvs log. * *Revision 1.2 2003/08/27 04:57:47 pauly *FIX: A large number of bugs has been fixed for 1.4 release * *Revision 1.1 2003/07/12 04:46:51 pauly *Initial checkin of the SNAP application into the InsightApplications tree. * *Revision 1.3 2003/07/12 01:34:18 pauly *More final changes before ITK checkin * *Revision 1.2 2003/07/11 23:25:33 pauly **** empty log message *** * *Revision 1.1 2003/03/07 19:29:48 pauly *Initial checkin * *Revision 1.1.1.1 2002/12/10 01:35:36 pauly *Started the project repository * * *Revision 1.2 2002/03/08 14:06:32 moon *Added Header and Log tags to all files **/ itksnap-3.4.0/Documentation/000077500000000000000000000000001263013355200160005ustar00rootroot00000000000000itksnap-3.4.0/Documentation/DesignNotes/000077500000000000000000000000001263013355200202225ustar00rootroot00000000000000itksnap-3.4.0/Documentation/DesignNotes/gui_design.txt000066400000000000000000000057551263013355200231140ustar00rootroot00000000000000ITK-SNAP 3.0 GUI Design Notes ============================= This document contains Paul's notes on designing the second-generation user interface for SNAP. The notes may not be complete or up-to-date, this is more or less a scaratch pad for my ideas. Isolating GUI Models from GUI Toolkit (Qt) ------------------------------------------ The first design principle is that we want to make it easy in the future to abandon Qt in favor of some other toolkit. We just don't know how long Qt is going to be around. Plus, maybe one day we want to build an iPad version of SNAP, and this would require a native iOS implementation. So the idea is to move as much as possible into the toolkit-agnostic GUI Model layer, leaving just a thin layer that is Qt-aware. Lightweight Events ------------------ The original SNAP interface did not use events much. Instead, every call that would make changes to the state of the system would tell other widgets to update themselves. This resulted in very highly coupled code, and in a huge UserInterfaceLogic class, where most of the connector code resided. It also made it difficult to make more than cosmetic changes to the code. In the second generation, we will make much greater use of the Observer/Event pattern. Objects upstream of the GUI (IRISApplication, etc) will fire events when their state is modified, and downstream objects will subscribe to these events. However, the observer pattern is quite dangerous for large projects. When there are lots of events and lots of listeners, it becomes very hard to keep track of what object is calling what object when, and it is possible to have situations when a downstream object is called in the middle of an update to an upstream object, leaving the upstream object in an incomplete state. There is also no control over the order in which events are processed. For for example action A may result in events P and Q, where Q implies P (e.g., Q = ImageReloaded, P = CursorMoved). You would want the downstream object to respond to Q before it responds to P, not the other way around. To alleviate this problem, we are placing some constraints on the event handling system. Namely, event handling code has to be extremely lightweight. All that objects are allowed to do in response to an event is to record that the event took place, and notify downstream objects of the event. The actual handling of the event must take place later, after the action that caused the upstream event has completed and returned control to the Qt main loop. How can we make it work? The most downstream objects, when receiving event notifications, call `QWidget::update()`. This means asking Qt to paint the widget on the next opportunity. When the paintEvent() occurs, the object is responsible for refreshing its widgets by getting the data from the model. The object must make sure that the upstream model is up-to-date by calling the model's `Update()` function. This function, in turn, checks what events have been sent to the model, and updates the model accordingly. itksnap-3.4.0/Documentation/Shortcuts/000077500000000000000000000000001263013355200177765ustar00rootroot00000000000000itksnap-3.4.0/Documentation/Shortcuts/Shortcuts_SNAP3.pages000066400000000000000000003274001263013355200237270ustar00rootroot00000000000000PKa'DZj)''thumbs/PageCapThumbV2-1.tiffMM* $$&&&&&Zιǭظվ&& (&&:&&j&&n&&n&&+&&j&&p&&r&&@&& &&%&&l&&M&&d&&j&&!,&& D&&t&&@&&Z&&4&&V&&j&&62&&L&&g&&n&& a&&p&& L&&n&&W&&L&&p&&F$&& @&&&&&&&&&$$?TYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYT? $&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&$ -8RS·sapplmntrRGB XYZ 0,acspAPPL-applz_gZvKrXYZ,gXYZ@bXYZTwtpthchad|,rTRCgTRCbTRCvcgtndin>desc,ddscm>mmod(cprt$XYZ \ 6)/XYZ t&YXYZ &RXYZ Rsf32 B&lcurvcurvcurvvcgtLMrtZ 5wmA!"$%'-(*2+-O.0j13i46v89z:PRSeTV&WyXZ6[\^V_a bbcefYghj@kln*oopqs9truvx&yfz{|~-^݄%In׋-KaԖԛjV9ФhK3ɭy[<µ`;ֻ^6]2ŭ|KɶJͩu>{4Ӥa։@ة`}1ݗGߧU`eec YMB7/6]# ;{*eoP d @ !  1Vy5sM :!"$N%' (x)+3,-/V02!346@78:`;=>W?@BHCDF'GwHJKTLMO&PqQSTSUVX;YZ\]T^_abacdf/gkhik#lgmnp q>rnstuw,xQyz{|~ 2Xq…݆ $4>P^w~o_TB3(֚ƛr[A*̦jO5iH( ͶhE#̽aC#õĕyZ8ʶˏoP. ѩ҈iI(ؤف`>޹ߕqO/ _<iF$sP/ ~[6b $K}FA 3z% M  zP0 yng`_[VQQ R!T"U#M$E%K&E'@(:)6*"+, --./0123456789:;<=>~?z@sAiBaCbD`ERFOGKHFIOJJKCLJMBNS:T7U5V5W7X9Y3Z7[3\0]0^2_7`=a:b7c8d;e?fmHnEo3p qqrstuvwxvy`zR{9|%}}~w\8ƈiG(Ǐ^<ԕgD"㛾uU3¢e@Ũh4ɬY%|Hڳw@㷳_:׽vg`[_luǂȞSΜIҫ&ժ:ڳ܍u0#ndin68ZkU8'`P T9\((Q$e!]F*d C}-fWPW  c 9  _ J = 2=IcJ!`<zn_^[Y_i }! !"*"#F#$v% %&>&'n(()R)*+K+,-O../c0012M3 345X6!6789]:-:;<=>q?N@/AABCDEFGHIJKM3NaOPQS7TnUVX%YcZ[]?^_a+b|ce(fgiNjlmnp`qsKtvFwy_z|w~L愔0≒TːP'ʙɡģ0Rd[fXETٍ^9W!brF>6".i#`^]"d1vIgJ , }  m  c  g  q '@i3uS5  zu{~1G f !"#"#H#$%%&V&'(3()y*!*+z, ,-.5./0X1123K4456g75889:v;I<(==>?@AsBXCFD:E4F$GH IJ-KLLeMNOPRSIT|UVXYFZ[]^M_`bc{dfgrhj0klnYoq8rtuwxxy{z|~~-ÉU#‘]󔒖?{.垌;󣜥_ͪBP帴O$ñŎxK9ֽةڢܔޑ Ldu.Ec%V% h F -   ";e7uWSav+ W!!"#Y$&$%&'A(()*+Y,'--./0n1N2.33456789q:\;H<@=7>-?"@AB C D EFG HI+JMKnLMNPQ+RVSTUW.XpYZ\/]{^`a`bdejfh$ijlWmo.prsuvwy{{|~+;чk-ԏwJf+ϠpSB4ص_8ºu3ɱ^Π.^M׿-ژ?ވJrqDIM'DOH#VG6desc Color LCDmluc nbNOptPTsvSEfiFI daDK0zhCN LfrFRXjaJPjenUSxplPLptBResESzhTWruRU$koKR deDEnlNLitIT*Farge-LCDLCD a CoresFrg-LCDVri-LCDLCD-farveskrm_ir LCDcran LCD000 LCDColor LCDKolor LCDLCD ColoridoLCD color_irmfvoy:Vh&25B=>9 -48A?;59 LCDFarb-LCDKleuren-LCDLCD colorimmodb]textCopyright Apple, Inc., 2008PKa'Dv7f[thumbs/PageCapThumbV2-2.tiffMM* $$&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&$$?TYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYT? $&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&$ -8nRSvs~applmntrRGB XYZ 0,acspAPPL-applz_gZvKrXYZ,gXYZ@bXYZTwtpthchad|,rTRCgTRCbTRCvcgtndin>desc,ddscm>mmod(cprt$XYZ \ 6)/XYZ t&YXYZ &RXYZ Rsf32 B&lcurvcurvcurvvcgtLMrtZ 5wmA!"$%'-(*2+-O.0j13i46v89z:PRSeTV&WyXZ6[\^V_a bbcefYghj@kln*oopqs9truvx&yfz{|~-^݄%In׋-KaԖԛjV9ФhK3ɭy[<µ`;ֻ^6]2ŭ|KɶJͩu>{4Ӥa։@ة`}1ݗGߧU`eec YMB7/6]# ;{*eoP d @ !  1Vy5sM :!"$N%' (x)+3,-/V02!346@78:`;=>W?@BHCDF'GwHJKTLMO&PqQSTSUVX;YZ\]T^_abacdf/gkhik#lgmnp q>rnstuw,xQyz{|~ 2Xq…݆ $4>P^w~o_TB3(֚ƛr[A*̦jO5iH( ͶhE#̽aC#õĕyZ8ʶˏoP. ѩ҈iI(ؤف`>޹ߕqO/ _<iF$sP/ ~[6b $K}FA 3z% M  zP0 yng`_[VQQ R!T"U#M$E%K&E'@(:)6*"+, --./0123456789:;<=>~?z@sAiBaCbD`ERFOGKHFIOJJKCLJMBNS:T7U5V5W7X9Y3Z7[3\0]0^2_7`=a:b7c8d;e?fmHnEo3p qqrstuvwxvy`zR{9|%}}~w\8ƈiG(Ǐ^<ԕgD"㛾uU3¢e@Ũh4ɬY%|Hڳw@㷳_:׽vg`[_luǂȞSΜIҫ&ժ:ڳ܍u0#ndin68ZkU8'`P T9\((Q$e!]F*d C}-fWPW  c 9  _ J = 2=IcJ!`<zn_^[Y_i }! !"*"#F#$v% %&>&'n(()R)*+K+,-O../c0012M3 345X6!6789]:-:;<=>q?N@/AABCDEFGHIJKM3NaOPQS7TnUVX%YcZ[]?^_a+b|ce(fgiNjlmnp`qsKtvFwy_z|w~L愔0≒TːP'ʙɡģ0Rd[fXETٍ^9W!brF>6".i#`^]"d1vIgJ , }  m  c  g  q '@i3uS5  zu{~1G f !"#"#H#$%%&V&'(3()y*!*+z, ,-.5./0X1123K4456g75889:v;I<(==>?@AsBXCFD:E4F$GH IJ-KLLeMNOPRSIT|UVXYFZ[]^M_`bc{dfgrhj0klnYoq8rtuwxxy{z|~~-ÉU#‘]󔒖?{.垌;󣜥_ͪBP帴O$ñŎxK9ֽةڢܔޑ Ldu.Ec%V% h F -   ";e7uWSav+ W!!"#Y$&$%&'A(()*+Y,'--./0n1N2.33456789q:\;H<@=7>-?"@AB C D EFG HI+JMKnLMNPQ+RVSTUW.XpYZ\/]{^`a`bdejfh$ijlWmo.prsuvwy{{|~+;чk-ԏwJf+ϠpSB4ص_8ºu3ɱ^Π.^M׿-ژ?ވJrqDIM'DOH#VG6desc Color LCDmluc nbNOptPTsvSEfiFI daDK0zhCN LfrFRXjaJPjenUSxplPLptBResESzhTWruRU$koKR deDEnlNLitIT*Farge-LCDLCD a CoresFrg-LCDVri-LCDLCD-farveskrm_ir LCDcran LCD000 LCDColor LCDKolor LCDLCD ColoridoLCD color_irmfvoy:Vh&25B=>9 -48A?;59 LCDFarb-LCDKleuren-LCDLCD colorimmodb]textCopyright Apple, Inc., 2008PKa'DQS:QuickLook/Thumbnail.jpgJFIF@ExifMM*iC    C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(((((((((((((((((((((((((((((((((((((((((((( $C2oa~g˝Z㦷-kL.b崑-;-HT7F+׾BL|K^OK #I{O/F.7H!èbޗݵB7M|IE*ifQnޭ%ݟau1eZ\Qxl 'X|'E ڮal_5ޫ^65ɮjRMm]ɆQDRV [~X+| :7v6n,[fkM83x`(e5k3ϒU#*^vUnoUn^qO}m/t/2Ӟ_W'~1|m 4{?&恨ojeVP7zZHQ2?kL2#ƽs0,~=} S]"+{t۝V4k,>H'XNب{8Fkv[~M|.&NJJܶv~GnG\8 cOAj(Ě{iuf{X_Zj4bHh _ZwO~ Ѽ /x|=mk |S|﨤Yү,c+l=q[[חTihڥ'v/&^{w/{3 i?ڋ@g=UΧ}?.dcwYK]>G.4RIa"8#<%| ŧ+pxf֏~q?7p+m>Ky >gNkIm}/RI;&]SIW]mgo*r@`HJ0' ߲~5|Sc[-[z\KL`K[5zi Q$xB^7fO M~?D|&Y|CMq> / >&E}jsKޏc,%XSTƊ|ɶ[+KvVkWjUyTi=9k+]l_i8|=5'VW'^xg6w:<t5i\8b+Idqp%ӍXCy>d<o^7O3zEƿNynJmX\Eȴke%;t- y3uv{_M_=t]_lںZ_%Zc%_> 9|%zޅi^>vڿZmlf4sv&oڏnO2ͿiCvwŬdBIcGwkxPcR릝urS}9#6V~mwgQEaEPEPEPEPEPEPEPEPEPEPEPEPEcx_|+{įOtꥊCrT+¿Xٮ87oL7k~Ff_hu=[62=ϗ wSیujgn}9(ۛK+_ֺ+a`xri: j ֬S&Rnڥ ء,'iSᯇ~k߉>,G࿅ۿ Kun,FmLOy~m>dI^ϴ/,/>!FkM<\K^w# f!9u"RVV[裐+F4dENI5jѧE*Rrwi_sv}4W䅯 so|DWĽ/z>!:ßZO.FxadV-_KmW[cn,xy-lmTALedrDTR}kѫZkvEPEPEPEPEPEPEPEPEPEPEPsERjQmΧï]}ėqyELRܲ sT+Mo~oen__O_.⿉_/6~w.MR#o%Id-*lu-"۹c~ɚe -,b c4$M*H8e}Hmǝu$/g?$Vu5n}vx!ҪHc#RS{pYyTgaY_GObC^6 bUqZ&bG_=o$=e[;x^n>Lz0Og5+WW6k$WOnonZ?U,pM~tIoӯ'o^1Łn=cqhڥ[ys-<ǔ#=jUPdg&bNIup|˙ 9oc:ƒ.--aV1_T&5/&xH?X\`\Aq@th)@9.~Zt'H%"[ urlȝI+pU@BPmft_["J~{g9q ݴym8PWL8#Q /LgӚs/)s¾ .j6qi' %q[C"x_mռEM]i}pHs % c2O(.Tpm-;//kNR*u+忊$i>.dKywR7=o$R|6,|^umŔb@#QR9}DhI&?U̺Ғ)iQI+YT TX+25pXe\ ?b #ŚLFIlTl~T"VC†%'-#?`/7^&{6k7=Ҕ#V#uڭY}I_KyZ}O-ٷjK?ei"5{ WKGLn!GxO"E`p}GC,ucLDIUG`K2;|/<jqx{H>2}SY 2]xHJ@O/Ӽ= |9S6 ! pAד[ѢM[NIdmιMTg;)=Xd=??7ď'ṧkPgPdFc3+l'<5xom8o4~_A aLCcAP">jt}'-?^[ꏻtͻΥa9q^?:EFG<7FdKkFԚDv|/~uip->KKo.`H)Xz۔ϻ MSS𶡦?-_?M.8Wy$i-|2nv/f!0+b*IY_ŶY6wH[L Sс#ER/n4]wHb](NH#/=k?~τ|aZx?PԴ9㵒Ym,aBGCa 856OO4Z%iΞ:`o)bíJFԏX9$ѶvsdYa%e9כ|"[<=·փC$< UPi@jݶ2v+>%~1>,>:$itKFK%3asOii8VʉmokHJb2bs }MXZƁf{ C.alZݴrM$ A oD.oi׮_]eZۥ] G"8[/_6V,syP -l/aHZ7g+}/M~4񇆼 ky=ݝWv3XKgvIln$ppF}o6Amn8#EtU~UjJտ`\v~ .u-rJ8,\;+# oZU׷F@iKE?_WNVyo{cmu6ueMV\K?دƯ?xJ:d1XKc4,ѻkjCePY37]6tkWzk{.?X=kMFH5msT՛g bxo亟PT5YBY/LsSn/iziiG$(%EYk[v#thW ?xIz]=-纎bWe[@dm坤Bہ'>|6/mC]7\4車y=JtgВG$[6hѰJg5*{6wپ)_ٿӢ6TfM|=E[k!dxe`5@p@Ux'$$|;o#s@yC GjF<9MOoD5?} $"447DA \b.a'ߊ> u'a-BX-∪ }vkg~[W_-R]vrVz6z?_],5汦Z!S$껤b'$`{.ki,i+@'_0#8m5G?|E^32}2kYD׋+FUH#$T` eoEo`No"ik4XϗHT k_k}Z{?/_k43qo4Sz20`R׏|W7ouq{9g{m6->&3Kq W'kj$]ТP]eHA!s?GI(A@QLi ++1ڠn8'׀*$PM+)eU8(MѐNx@(((((((#F^|QwkvPndH-*I`8Y8tbNJ|Nnn K3}^g,1LS>/x:Btv2R}308Bbt%(^_vv4խ?=|F<+MbFfi:E90 )b+vHЊZ~q\|!eik.RA$-qm6GFܳKMʴd?oGJֿb 9x/:޷vhWYڭ`CHyx.IHثų[]yIoy,P ÅC!H<"Hg}\1?rLtm VI_ Ur)s^fqW5|1#hφ>|>g}kjjWrxoR,$N-qIF))Wm~P_ɯxVӎzv|_\YM$Q"iiAG~|_`gtnY^? jqK-b)Z'|ce y̋_J}>?8ChW< VVy0nςϰ6K.zZj_KinWoziE;TUu'S__v~ПWzէX|Ym嶁ȍL n[hx!H Ջ7 ZX4'MXӠ#8Yp'^ gi_jtf8Zoæ}Mekg[E8UUV}<'߇֚xb-6M1B3nK<@GW19TWv%'m7[[G~?|S} P%Ԛ[ϣ%]f!,|/*k)4nDi{eAYr~?M;3i6>8(-/-i<Ȯ(D0ܮ 9mZbz杢B4MNSu[_i"y$dyVf ;a`:X]/Wb!f}mo=uӭխw~6:5'Ϳ:e \ BYIB@?/5_Yg{KQbw--qDذMH;G~.m/^<3GVIdX!ti3 d+23}hYڇ#Zx:ԶsOe9c&51H8EI7]zXTc)vWnV^W}t%+-:R9ԧ#V.V, .Sd{xeT+?\oxXC{wk{o4`ݧY.dR2 زϴ?b k$4x=Z &[$Lw 0>R]ŰSɹ[ط-/^Ծ#j2]]x4 ^q7MߜD%߉ ,nJ˗MIyi~'IIɯ/U{[~ٟ~)?nu@,c0dM+aPTkjjwJ}?\4/;澇GXi#FO*+)]w㇊/McQa:G5[%M~2U/ڎc5C#ţƟs,!; {/e+5O .©S2Eе \hGs?'SZ_ETS׼?ci:,2[Q̲m0bT"Fޒkq!#Kv=>Hu[g?q-Áͽ@vu BRviFﬤK륳얞{+y]E|ﶧ?~ź商NDBm}_Rs,m%ƠYĭQ& Vb߈Z_ߌZSxW6_)HZ"ͱ:&tU[XqgMd9s{)zlkt "8׋uZjKk%;mld6kn_H1njRI| ;olپ^+</Y|~^g鲋X> do\-hbޗL]"ꑴlZ: Y /=j2 qugrʞnb=rّĒ+8MY+mllP;K!IeɺiK2/њ*&ũ?NW?òȗ_|p=s߉umP:666w/he"%%}ȍ|6w|V6c΢Bei?~ m#(9+>]ͽw3MEYzߗMgn|@ծt.jl- N 4M3#de|5]I UԮW.Mssy;;9%IC]3I)#;Q}}M[q}[?M|,#|2cc b =d.%1,lȭ cP T7o"m} (aEPEPEPEPEP^S>ܺ|\ [M$-zucp>T$>g}H|iqtA ÕwR}+'mw{_Kz/[]n:im;4?k,aAK,xle_{kx[VTFҠ-ɸȠ1v$t%=k!wΗ3zݎ֫|ln>:AuLGQ95,_G4.|?{;e("u.CDxS??5V'3fGY&Κm &i]73adkKF_×"| iPBb2b idW>'4_NGϖ?"A1zX竮7^ W(_Lǧc ~m>bޛOˉXpFDݵKw፟Ĉ.us.`mJ#{I*Ƣ8jR8ќ@,z~t?;<|mymZX˩_C>hsnBRB*$fpDT'`ӄP Ukz$:r3u5|s4";!$}/F/u)K{XpnhvFVd?{Wgzg"%X,;/iq",O"k;;#RVbX_Om7k|Iw_^E^2oŮѿUa3S𙷻!F1&K E# 3azw? 4je&f5% ( ⹫طNt+Zj"_CDžQ6vޡpS?v¿|1o:_l[m2]˧\Y\[B 3[/iGt'΅'u i7:k-/.ZitGm<8Fdt3T]ն7 N:ϦkN|TRSsyVR7]5 $/DQgtDC"ͅ _ fem)Ft^fQdgUUpF۳.euGJkMwsN,r3VL(I$ / '/SCx$Wuռ5V%2rь57xNmJ_F0?{M~V_wtWW_Z'~"IwIw]\moQB!mjܴ (aEPEPEPEPEPEPEPEPEP_G4M_D/nmc4V-mEެFv_j^0joľ W=ņ!70w$[\$SI Z>Zvwg >6ZE.Ia%\&XEF*yl``?2O gx\};C(h]I؇]r˟f'$[xǫz7x.|WKjڍzdo"̲5jE\ #NKs;V(ď F6^PcA >g?|oELj% &"NG+ȱG.9kzgz ZrjZnqYI2gxchdT.u(){oM/j{_^5nbVqZq4n#zJ%H Ï'Ծ-nԴ kCB^֯-VbdTav4` ^B[ ğԬ<}oXxT&EѷM!($` yx/|Tv|:|k^ęҬInEc&*6u &(tעp_;m`&⹯{m/>t?sսm\y{^\M_#dm!\o#x]MZa񖸯wݬZwc? +f⟆^Tۮ().d)d}r2+'nj#4j-tsݳrNi߀!r+'LaW~ h 7oayj\EPvgpž4\Oxuanxc,"\Li/;߽^N$~֚NQ+->/_[]kʷS(yr"Ŵ34-j LjᶸS[r0+y .P!$k~>|;Okwցijscq$W?cʭ^g{ h|q'o"Ww__Kֿi ~̓J1'*p$}V[U+|6mzxžW]&KOM1Kdg{#o5̋,p͒cSK⟄OŋRF]eҴyYrMVbհI 2 \>,W`in|3K!YXK8##xL ЖO+9.m< ki$ o.uNs;~?zw2E\nZyn٫wm38ɥv={7x'/|S6"~\Akx[.,wL i"(eh5}47 GtInՑguwFI!DCƊV_Oj5]xCPHkrG"s#1' ݓou D麾i{pq3!W_3l|0aIMyu=/OY?'rP叟ѷW&#_Rg٘Qmp' r5x~|#c^x>Gק[wq3!(x*p:cRInM~]3nQEf (((hOq/-nRO~٩\%3Aem<(03|)`#o|#>Mnj6}_L/4.^Xi^\IS-͵&6ݚoGuQ~v#Rkύ 텞4+#Val7sXimCCҢMKS}>C9cg=K_!񯋬~'?ombmGVxs ֵI&pK^we6 jW:$3')x~@:r$⮠U}z},=p]|m"[NSL=bn(%>n]L:?3_pY|Fuit[kXS~wn H/-ٙ2E~lG Ewû?IDUӵOY{u6wH|i!3~S  ¯^;=fsدn1XmYśtM4Mqd'y*2ֶ]4ޏGxx9'Tc.'5եX_ʲ]aa3^7 %!LVOŽ7Y^6Z^C$&(7N]2eMx`ث?ƭ7_kvkv u7gP" mټeCsۓ~;N/'|*E^ik"kZX\Mnl2&À 1,ešWokKuv/[>ոݮT~z~,x;Ot_T /oOks+Fm`TRn|pbOfƱ 8uXOs"xgK/ψ9qb mvÈ N.F42$Kֆ(KDLq+#+Gy%|u|_ Wnӯ.@kn&9a|DFm+'-4Z&+MQ{Qזnf_ۋneo_-~Ϛu?G<=Is]_\D71:;1 uw4{^/f6F SkĺJLga_=S_~˟YK/M?φQH_KE1b@a nѼW Lj*xė4K˫ۻ&KMVkfځ"r& *s,vzwS]-)&ݢ+SRݵk}otÿ? >,~Þ/NhԡY%Ӥùܱ9VAId3Oa\ G╺چ5K<׷L7:v|)Js~89 +`((((((((((0|@k_6k# hq'HT,he`l _:>3|L\>3Kgiq7gԮEn5fo-Q\8䢺noș(OD~Ws?O N xsAݽ,-#Y*{saTd(Q_|qiᇋ$_x6vzki< )XdC;*@ q|Z>71_Z t3_[RX/|O #C'6 N g^6q9?K'#xڢ(cNs_xǿO]xGO deS,Q $H7quvOã^c4_4;WH΋FHżWLƱ% @%iK xoM𵾡xrVҮ<7A5 ʂ2<dlcM>N<)o`f[됾f1[ V?/ +ʀ7\)-{Ǟ GῇG-r3 [u [Qs7^d}s*9'gطOž-ut%Vv$m5#~ 'jk޴WoߢɾΔd٧{|W}K04: : IKh\o-|-`f\6剆, խ}/k4'i#YrO/eKMKRlky.yHdIR"3;`Bki;]4֯vo/O]>:#O|(K;E<=[`,,,h171l=ySKi~&x;տuhYGk"6VP YӚ?|{Zō3ުkZiVkhkI(aIR>*N@!$3voVV*:-wzWهkYOdKyy \ 2 o0PWJK I]A__~ڴ1o+fw1ĪJ\M;i*`J_KVItZmHI>fg4Z!(?9QAVTf%%o=Ƣo_UnH#+q,@M>?$/K/z晥jڥX[ᵳ7-F a땷7|KI.d޻[DFڈq$üR#'m|ͥz&W~G]FIJ$-,k3ʅX d/e?7DT>fTvU$W¾ |gkh4_X]2\Yti3ZrSI9vU1_J <޵6)Dψ{m&\[Ou-@$ާ{;?mm܎gm{|_c)wRG&([;[+U]0~ڊj]]F_H${C/JK!OWc5O LKcĚH1EwF~VWUt`FA 3 k%O-z}#J4%3nnf6o g#z^._~ÿ]_z|:yvZI[A8h(`HAUVbjckWrOVٜQMk}"H F"TdE|ؓZ_ 4ۋn.1q\s7ː}~5,W~0ZnvYm$)g&i b64q4FQ V'i%=4y sz $}Q@Q@Q@Q@Q@Q@|3⧄Gō5EŬ-jn$@ ᵹ/U2Ɨ-~niq޷=RZP2:ߢ:V;nV Vo-_ǟ~+=R-N𬌁3FNv6wb#cGBec"f0N rOrs]SiBOmk'sx2o>Oy)D-* 9] ߍdx4-F[2`I$yGܡqď $GH~&,#,Qڪ7,-|o'u|=>z^ mߕ#7ˍJ5}}^& ~?o;UZЕ1Dn$Mo)Ef焿iOٛ:}=[/-tg")|?_ tڃH:o4=bO-_ JH<<\ HOɤ ̤_1wu5xUkϙ_>iɸ+=6vUÏkZv6DJa8rT"KE}g? t O%kon^ơe&.%en{-_Q&^𾛧xwJ}f7 iw CH?[6mKỈ t8mբ +G Ѹӎ<U);,RhRڷks+FmOgj? ݯ.?$qލ,v\9v\Ț ɜ5+?a<1xFҮ,nf"B K 煑6[ߊ|]$w CeڅHEᏏf,| ,&0xu!OvYO"@rh-niNG\V?-_vA{F|.Z| P*|HJ;m(KBb_xI<-Y xAwiʼE phѿj\~GƺnzL"HX!|ScO (}#v_^xi!Kk-Nv0ۤ?._RիY;~zgZ_[]%~>ē LG#fb =+0m|3I<%ۨe k'0\0dTeu7_VixT~Yh{t577-:Lmn@<X6 u梽)~QNrm]msV^= gxwI>xM^hA9d ];c q(Ӂ~xh:t.+x4Œ\V*]U$XPٯQZY_Z]cMũ r]st5Jr;Zv^g*UI?^Ns^WyÚ6k_-;,VT3ZI$d&=p=k׿߲]SźEyJΐ&@垍ڵB'k->ߢJ'7)Ѽ3gR?5[ ƵfkW7M+T,J 6ϧ:k7N2 Ѯm6\lߗLM5ۊZ>w)6oϵhVrdZZS(QECo k: \xB{-y/f[7ژ[ UUf:htOI7چ.`x ţFb 3pyjai:\75o#y.mc$ҳoU۵G |&^/kSIʲdr,Qc"X噍sz/s◅3?HxΑyy,RC$Vo<<+mq22,Zv;3Wiwg7oߨQi_I|6)j>:_fhYZ"UtK]#w7k]%GQ .s0Fq~ чœ6MYMͫG -iUZ/4 o?+ah^>{nkT|E|êq#DSJ#ybkviKߴU:ZRl_R_g %fM'zWrW}̭_5NJ|SAeaji֢ⷴ5; \DI{ +^ȺǽMtinKy뵴H,h _*U៏~Ix`5ȵ{1{DBJF,+s_k+{rxoVF^d{DPwwraI$*Ì6IwKnǯU~&.wwחk^>#o iҾ<N/[2K,8{BLE# ̿pdƷz'nC|drnM8]t/Z]>bߺFEPW)E7MkQn,dYDg>k#[`o/AI3+r—Ts_x- $[4UGPbu⯂?/K[S{k!pZ[{3w!HG 1,Y-_(>髩|P &ė%u9dkx4yn|) 157e$jVd.].wO-ODž~8ñi,IտF(rUm5 ,x?'\>4x%ןo$S,d(crwYDqѨM"S_xComwRK}VI(' d#$MzuiV{(vPӖ~>-CSe/lS%lIZVzIW FU=eϊ_7MJ6]Y˼yx'QaAı?sQY97>w3ء9'+~p?c"Yk}ц9ic{?f;x+_ ͖%^;#Up;vPkj+HUmnoșͷ +0 ( ( ( ( ( =C=r>7^!Ӣ:cΚP&ݔZF Xui4;~*EqqSLrVg d@so_{m7|Cm4/Hm&h7Bl?|2sZmn;䦲W֒=wǭzk\Ej)gmvO}F9\^x/@徧5:}Zhbv  s\?atjZzΓug$K,22}eM:J|si χ,,` |Wa LO;U4_'칿o&0vg;UY;YֿbE/~]I-nu6Gn[)S9``1fj_~HcAbicDr7hAlFN9|+ѵ Yb_mVmPCV&^.%]nWO[v9nS1*RKxЀ9Nk8;}rwʓI~ ?x [R-#>D<`ֲd+mmçNauk:Re[ʨ7oEr6C#Z|f}GI%Cn :4{Ysx-[V1\\O [XfhsO#mdWvI'o>no2?ܳ=k?d|GM R]^!4o,qwR[iUNsdjZnj%{|&mKOn,""wS~27kfzh) Iwqp.&*2˅p2Wp8*_l׈[~un͎eTTdo31TP0d((-E}>|'OoC;;MNӣOJ7xƯXZ_kUθ E5E#+2%u*Ob|HmtIy2۵E.8cy_/cSt1lI Mĉ+H4bgY4].}?[ j*m;/xX+gCL^)Qom :x(YaG NOCK7TeEo$X dwu??d/Υvk7Vْ^q+w~WwW{_ !? 88By9 |MW'׬^ymaRYchRmg(̻-#=14T[]| [>LgvIIXGZ<~S®Rh|5wwmu?\[ޜ#鰲|x+|ʧZp @UT :KE((o;់-|8_!a{$]Ol[: PGz2Qj[Z.s(Vc1&tҜg_{⏊ 5x;]Úơ\'h-{/͆)"NxB\J?irTgkiەu}Uf>K[NJ%4 pd&$ psg??b-kxzI-#p1*6L҇۰|<;Mx_>ktCEcRxHüeE)!,v~׮˖_ַק[vܗi go;>T^ЦlfY,x4blP0X!h[=BKƞ8Κkqp2۰[$uV MK?{A?G57~G¯~%x^8i3BΙ.fmZEfdВ09'xn7ħ=?  nlVNrm|nI|ylȚAdJeABX¿۫[,],]yr)dY16ۃH[/ϕo߻ Kߦ}fLѾ ~zF$Kk hf2F"4);$y,zn.i=[Ihg-J6L#tkṆWݿ 7*9l9໾#Sgk^d!T3 1H^Z-ٹudrA^)?E}g_>|qnm_> ŅVb媫p\/:WӴQPމv-GV@) (((((((((+Q#SRr=9zxGď|TGMo&mlxǑ:"0fMќ08$eݝxoƟ<KM 7Rzk/ ZLJ.ؤGfb)ÎzkRW٘M.W<Q֡zh@^{b=8s_\x )~  [Zb5؎+i#_!$B2V?X< wV^ k(ܵBk'1m/b<rfdqܬhqq*WpON&[u{ۿ;E+OҾskAYOi$I:ڃq3 a+]&/n5H'X]DZ.xc `z  ~Ͷ -Rx BARĺn2n$If&?Q7xgCpܚe ijuqNAUoRda;%w6{A?>\S^GY״B?jC5j0J'>]kUvmYYrWEF ?dmW]A7Y㶛LΦ[x12M#= EܟwgQu<?`KBօN⻨-'mBs_%B6^޼g4'신x]+wGl-IĶ !ۙހcsHO9kNKtD`շٯǘxs¿O+M^݇‹}FơRjE{Rr[gRFXs&i35tgt3j7jӥWW.v;ıYQ >7R|DĖ?@5dtc,f?tOُGuojU!y{rZlè2,I瓚Xs+/Ew+[ۻ|^k-Ff+="Dt;^xf9r I*=Tl٪+y,)7K}u12m幊S*q2Ÿ?O!/Zu$u|f1m {#x $~o,UٚWO&Ky(ONEʕvIl}௎_ >"?#Ǻ5ϱmܙ#.A1אzkի~6=Z v^KiYdD2˱eh9{CAFQH((((?l;_m_&Mp.olmIGs7#S+S^:9#(өQ{:'m+þ-!y}⛖ԏcQqnAB#sɮzxnGgu7lWO977~WlO |CuM lb Jsr.x-b,tI%8|˰9824XWB+ۯjhi]^Cԭb #FKH/ ǰZ>W?I.Kes++.)%é~%$"ZHl79ڽ Vff|44o zNAxxBt%ٱkb{:/85{ܷNJ[]Q_#i'YxY𽆹ΐv@ln7Ag]kGtt֋y ZhһJ( pK"@19Pi֩e?s}E|in|EGOo4y|>i!/d!s;_R_ŐhrhK2Sdx8\̅6m ŴO:rdeg4/ Unu k69]fU\ϸznj=W*nQEQEQEQEQEQEV5 xča{ >{klgwfۀ=HOa4 ;ÿ5?˭Cűk 迋F&vmn@yyvԔo6gmm%_3E'/5~זw5zD77ZEqoq\\*Dpͺ  _ke3PMZ8@եÑ"l lK QŚu7F5!{4խt[:T* ?:GM](Jk*:SVZi/Oi.pmWVpZi~OZXvԍ:nZ/G~?1xDѣ5wR-no."p$|#5V} x}3_2F٠f52ClIy¿*3k6VxY7^p$6Lw5rxI$,X5<:内=rOqED[1 z&I*qXN}t_uQyQÞ+Fߩ7o؃῀|cxZ#Y5=I(,"(~h8k؏}C7j*l+Mov#X`f.¯ 5K]V{Vih$ݺZڑ~Ŀg5;Y,x#"7) clKUWA5__j֗ì WFy<*>^ZT2OB^'E7g w `r|Æz=M#-Ï{tL!ơhzIly.~R!pPuB/mYuoY/]۩IxJ/^Yy-|ϲ3trĜ1iMٿMu{X|pH׎"p̯,OR|Ηڷ|FҾPOwha|XYoN `k7> YIvJ`-XL+gN~KQBOqӗ?Ov¯^!~[> 5s,.c%H 98$#EwQJER((((((+ X𿆼E_ǹ_v(7 ;zkve[^ 4=JUI-e* RMzbx3E 3cܒI=&CHV:K ݕB n m1N/Eo^B_+oߴ?]>?7/A /"K~>YTԋbs&] *>h:ϪIejW1YۻR@rcʵvR^mk6E|a>$C'5_l,i{vP*Rq[?c#Q4nvׅx줗}r%ISH"L/4$쓏 Mu-ݓG,I9ڏZ6o-G@h4$BѻTMm*pBgwo{)bew~^+vmw#OQHNPSGѮf(&vQb0x{EkZMm<&u"H~>rr?5:?76xuO隥,NiM10F 1 v5ZI 8~wup<f°8H5sN3ms3Vw?U.]u:w/楤iw"[;Gipl?apWlmcN'm" ˼(w;x'kZx+/F{-cc?|V坍ܶ/q+_BLʒ0Ck~oi<;/nP:)ǻ@k>~ӟ[xs_X$աG$b\~fxR9Ԛq=WMv"IYh_}u8K4~eޑn]ƣ=K=͋r|OZ ~N/z_vxm.{=4Ci*8rAtۏxZ}>H- {v+{O< ª znCg?>z722׾n}_Vsn$qo&urL炱=HgM/+]2ŻQr`8@ ˟Gdφ>: j> ԣ7hYqn vU, ⏄3x B{ "_rNO*C# {V7xA*x$A&]2bIP/=k4]#Zde;{XV(гb@$}I&(6U]Xŭt4袊aEPEPEPEPEP@ 2+m> ›;{]\"-j~G^E Guuy ch6i$+vxW]b-/`6 uܒ.SnrU_oetwk} 7V~vUXnHYde12tUk/¯Dxz#LRFLKaM'g?L~|8]u ۝HO$Gqu{s4<:ٷ{\Oه>$߇5mz:y-ԭ&B̲ e(Pn 3`zdS~;ԣ? ՞ t"A.-e-A wx_oycvZGuPf,e0q޾5e?(߉dRkV_HI*c+%di[xX.6}->[;Y~wm7e sO<]',_%'I%c]=м+}KtEpdd<o^?|uo߇5<7{xN;mens7yέ*~~*ִHkJjvOmlV1F^XcmZnOW{{?ӊK,tc;GOh?xC.4յӣLԠVhЩ"K9 i ?c <^ j*D6j vbޮ+% @[i95g#Ѵ+w{CTMQ$1ȿiov ^ _d/eA]  M?[WO*H⼺uc?n=HR: *h/(=Qvr~?ͧǣ6zσ6ZCbH[F*c*lΉ/>w"t` - !g_$8#O6k6kJu9DCBs7q]OW|^)<#e /qAdR<ٛߪcg#$֗]{C>8մghp\]Y‘ʾR΄߻-ig,uCO[>e:B;m#/̭MEQwn${t>{o~fQ=OcDSEtcl|w@o-xPj!"oNǂ> ~ɟφ_L:uG$ИĄT`2@އ"0𼶮4u;Pw`YF=Xz cݷk1_iZĺQI;j/|(qyUG }F{Ꮘ6x[[%`AqvArBW4Q^ӖwnީEO=}Wlm/E "6,w dCjf[I/299 {㫿k7w4[ދ ^]'{,Otf!\>۴y"? }чdG<5߁_1{SZ]6[NcF JV`0#S| U?H)K=aKN^&]1XVP maS=woN{6qoWKXjiI__gfEّ8<4|I'_^yo 8ܮ>F$`o\wr>qY+3q y>=_g4hYh^mBkhe2K<&z~tU~swe߯=ފ(((+ɾ7W)ROs&Io/.*$fjVK:m9Vx.aYcrIV=&TM9l|Oiw-Ʀk՞_׭MtMY߳]OQ|kx~Im[@@\E0c]B(5g?eOqch1ƗVPɕrW+5zO;jOQ#D (>>M΁kqfƭj9T eAoD2n]?_[,FxwQ7:m͡48eh A ??hq;y<=X)Z)WX'NʃM}iOxvzGÿNLVآ D s3}/V4߆m͒8mhXh쫀0 >hM~W4\׶4wn>|U𥯎;-I71^[o0H$H"ӭz Gm xhip, ˄Hۤ p-ߴZ[Y[y6CEY$ܒj9-F^#s^Ɛ+wQ,BY4X,A˄_Lz՝3oWQ/+PX!=qs.q]-%7p(QEQEQEQEQEQEQEQEQEW͖zW^,u=WGٵk}Dͥav*K\( N?xF=;'Ni?h;wGI!}"|o1[Xݭ]Guc 2wt[Bůω%R[S/)cB RV7-c~v'n/QKhS:KSŸ1Ts8_.' [Z׋|JϨZk6UH'1f9z×ӿo_$ض~϶c|-o߆^귾ZF[" rs^q/c8a-08Q@>b:`n5/xKsɧǩ \I4cYHvzkwg%j}Pɥ~O/-|ujox?.nnxo//.܍u¯AdD*+* Ȯs|1g-Þ {=:O1w61vz_iMu[OhE͡6UJ&vVwaŖZں˫:y$Z%$1]n(|:(; iY[\ 9aϾ-_K__S].G>#֬;k1Zw GLmX` 爵=;SI#ZZiPF*6hBH,@N7W[Ǻ_7ռQMsI.4/\t%6f;'N+rVV{ZދHdم9֖w;/^J'77z'th8VXIǕpd9bHߏ&Ea7#νe M6q-ל-cLhª1Tg_TQYO޺ȯNz߅>(vpY~ըt\Cc!pY,s;zwEѵ]CBa0W[E! ED1_eVI^vqEe; x> 6H׬"]_q䲾0Iqv2z0sG)N·~t{}f^1-j[Q ZJxM=NSQK#ӣ X<0}EnM R(Q@~`B?g?a/Ts"#J+/K5ۧΙ!4`mD>bow *'ktWOUf4VPmǪkOī/VMzdZū&%Ɖ$ZZYPitDܱc^O),zrK}6񠳵k Z5&|`WYH_U|@ ~_|W<*5 aO=KAZڭӯ3TfwFݿ=jfǂ~#6 A{a=tR4K# djo_x onOZmO>E]^FQVp-_zo Q>JNaou$V:.u,ij : kO4}&}2MByͼ嘣vo8TӗdY)imz?Gh~Zi;qͭ\\R>Dh,`UCUgWno;_x}*@((((((((+~*1qZ6t%ƞ+FV0nJFdPy^^]?wzǣV[  lV ĩl0Q} <%gO~*k˱Fd|c6` z%p? ÏxY5[rayLqsww5͵~UI 8tn8+$+ '/|j%2\MrfBF;d.+glhO.KU.[wU+݋/?s'Mb@Rӵ{;ȣY ՝WQA&"rSJ4ߋi/;8Gkmċ-8qm)$mEєm';kC+/~.C!ҡR6i2FźB٫ۓ$*3pv?`;DKO^X]-ė.ͻ#2nl2)DF6gMzC\!"sGKKq1I7 .ݔ<'xMvǯiz{iiV6QBh'I$YY\?Niν>7g+,xPheRV({/ ~ў?Ѵ4X/%tk$eowHe8 N5 K"kNiHe4Rڇ"O$F&}/ *I ( ( ( ( ( ( syif!7wV֢I( Hz(R{f8nOKG ^*xD+ +[N6+&6[uI7uޒv٬Ab~߶ZknlO.Dv7fk_GV9ݹ6'N VEmzXʜf/,HkrTN?~xoóx mJdn,_&1kg i߳Kočgnu^!]@4%%YtˇD8`خ63+ۏ躆+ƁbPΧUWJ +ì?P O"_x?ZF}\[sZs[XZmIN B SPoG˳wVsy䖶nϺm-5Wm%v*1)( >K;ygAI%`ԓkcW=fO Ӻt^}N]COڌC)> dFjQ.&/Ai/}?|GRrrvrqMWh9>DҺ]=蹓z$B2r*+ )ui|1vSh>կ] E234nW)xe)Gbs4 jӿk}n%5vk׆KFF턗r#'68Iۭ%o6+RrWJkmOތ [kKǂC 9dUO=5{<~ޏgh_2]wO iMڥOZM9Ĺ0jӘK[BO?[mG hVymךPn.%,Z1j9Ai@I򍟪7l W̗o]Є[E Coj4E =*\ >य़CukkG\xo5+i4Voq>? H|KA/kaLe odIRi%՝WU '&}msNͫ-%kkoUAPLK,rbP}Q^"I6}47q//o<'e]?I/V5_gZ4> 5X; FK]F9%Bq|>y~5i*Mz]ekTdPZ[msI&(DooA}ah%xI?j>o~(h^ uyquo eu(`ƷwVb7a1?Poo&J]/H5Œm^XH~I&EA2)Us||սS׳N+"rVZ>~_}&C$HFSi _M~3Z X «w%ŬWMVّ[mF_<]CiROf_~ck}$QHAEPEPEPEPEP^i5k7+gXZ{w1E h]PM~rxo =i6_|'.cCC$^SjvS<6K2@ȱl.Һ"ݚ&lxg)rʌe# _LثC J[֋+#m 7Ai?Ղ6EtM_k.k۳-K#VtߋFӢ /cXY-la:,ڇV^1wZ|s趪Ϭ[z~kqg8 N8J#T$fi[y!uԿ-s4}2uT VQҌƢ3KM}tGD=[RaxŖiWXhf[0NwHSj/W_$iO$w =:߼ֱ30c-. o8g' |Q4 uHu+t7r\IM:7ͽY=#1ڬ2G]^kmӼrjvnf02Fj,{ vGFᆱ}볾TGuVwoOM}W)Oi|݇4k>&XS)*+rW#9 {|k},8_><[:֬\[".p0[(.mlp@`ݴ[Lvve[+;y v-QHHPYrIMH9%2( ( ( ( ( ( ( ( E8$]\H7V @ly1(?eQB*2i-?ͥxv^0FԚ}nN%Ůc`6 9la_p7oFɼ#trxf{_,`H(&#+Tڗ3䗙dk~s|`|GqX,)uQ}qkoi؞A.H 1r%E@6ksփ_>#|ʹzf@ѿctd2 __ {6|Qv$qo2j7ljΊb -#Owf]OV+_[߽_i緭>x>#Bhk~࡫W/}OڭW[dUe-rbRe*;V俷Nq&w9/O,Sʱo 0*O99O] ᯉ~'Z?x.,l翚C69-R_koIZnI>u%&oiXFc煵o^&761s[v(* kЫƭ]ҴubI[(. Jl@NI$ W osjnO|ljaX.O5hҤϰ~\`(׽7;[Lh7f׿к+|PxW&Xi?hSDWM$Vu0w8d$+mSڏ׊?]]xQuYk;XjPtoX4O 7#+~> Oi|ؘnŴg% )V\l`5ۃG M6[m`EB]YFWs.$ rQ]oqUOK_-OJ(((((((((B@(k~.o @&Ƈu6Z[OǀBKs v<7}š^xcSԴ˛mFFkdfFR$𮧣kEx<_ʞK6+l`W {/Wu ]6̜#eebFWs Oٻ=g!B|+hMo" WrUɚr|{w_>(z.ؖf̯/:KE<5A2G.u MIF9lu7EMX ͛1ەǴr#5W;OeimQkfmto6i$G$LgB1ߌeߌ:υ@<]ei.zn{uuUXfŵ(S\/AqZd264m>n@ݖ N" z>9Mҭ-m&y^]@mpUQH*-$Jʗ̥'ޭZKm%E)?GܯN cċc>#*k: #s+J ('[ >I#eBl*B I;?h7cM?"++mK- ^].oqwI_.I/g5_EO>Oh6MqBveFj0_vAзz浨}-"imƏ6`3V)TgJKZ;}z_S%&ݿ_i_#wƟx Z߈|7 Ӭ$W#q*ϺwR"ҵK:d&M ^\g>T%۩&Z+I~Oy.Ce~6nzJis@xkRmH2 ^i22Y d(v8d9٣ɤzo^ YV{eqqRYeX[rWk[]+?g[:NDjVݦږxb6 ;+8r|x\?e__M67owK/k:}\)Y6ۼH_}|?k~_5Ku6{kkQIt.cS+saAp})NqI;ݠ*DQEW IO}14zį}y4I^.n(Fm.J,rN>G|KŸ>l,<..c)TyY-xBNG5l?ߌol5(eZ]7ٖPڙ(lK#I$ic&1Rq.;_PW;+O7SZi x\]ܢyqjfFE6 yr?ᯆ|x{c.]Ϻkߵ~/m>x- T[YϪ_vѫ$1we]_'u# it}6.E2:2wp" mz=j(T67c-Kj>{kZ#|+rZd1%.Mġ(\+7%oxx,YsOϤ/ sjVؤrf+,*] O$cm`)3ܪK,N随k|Dw׋?!m.k3HTV-# nT¢SrEU^o;ߡNև|@տgG0%ԾiKF#'p9F]j| {~+ߨn\~tҴmVܴ0yG?QG5}2 F(v!Yۧ ͇1ϭkֹ9e+1KX&|87^[G^V06q枊T黿ˡ*+8{__%%g?fF[e޸Ƅ!!I?gx+[M xZ$}aƚy`E8|cSYA/mgψ?t\JtKbLe.kܢ߁?l5ozHaEPEPEPEPEPEPEPEPEP^!- zo' Kl8h$Hi!h?,o5>^1hBk}rWEܷ qfڿ4njt"ß3~5|5Ծ)|-<iZZ_O>3O8G(XD8wa.Tl< aYE\5]LՎỿjYNjo3n$L@2159o߲o8/>$4}SM77Nlr#4Mw*<8Y-#%oj߂h支of7A-#<;=b;X,thd!-Z)dI! gg-$T|O:Ҿ-?nh{M;font[[`,PZbEd;rkc?o_!Ak%^mAo2+\!egu!CDQA9i'x7CARawn'0ie6lwo/A|+G{k5gu_\ۥ s3߳G9ROf}D=ΨŖs$q:NjLnGV!_ |9ox-DjR4I،I;3_3~_V?i[4o_:inHy$O U)|s!ço+kdHh QP bnROErkIK/oTYQEŠ((((((&\ Z(?&1/dLS3 LAKE&<zg8(0=p)hp3@P:)h(((((((((( ZmچxeIq..K*XtKY~/x{(]}\m&"[.Y"Ckd?Y9#h{Q46/>T7)R3 g&t>T!nC-奋BXjЪ!y*%|&wUmc~4SO^ w^ ռ{9om_B$E")"Xu2x,u3^'<-6RlD~n36MMF0 ¿&cߍ<{-f xK!t100Hi@Be]z]N_vqi;9ZWϾ%mTuC Q|";~OB4.$[[sn%[YMẅj>$+RڄYδb2-/gKK`R];U9bIM?4U$䣣[uv[^zw}t_/û,Π."g{Qk%ߞ?")r/\ SƉ(\Ck$uP7@T6o+ό> 1h=kH<,.a-H[ID~]x`A;Er?g% it4-kVz~\^5ζC"M Y1(#!akտ-O]ד]v7+7{^:o}ώ |I>CvoKti".3cvgpQO߅?w\Kǚ>ͣ9hἋ62>eʞ)~'ZW v'F"KcH{ar%{9[nLJ Ok' _~#GڍWqXn ‘Kk) 3ss5}_EoG䦪IDX?Qh ]| [ x;ĺ_O:tFw32G)R~f/zDŽ|;^xC:-弮"t岲NRv==-ٗ}9oW>ϢV;*_j2xW.ͣlbYTbh&Y7 -fjG٤jZRD{hO;"a~vE!p2Wa!LNm(}s F[S+?~|? ?/l?k[S!Y%Pv2Emo-b}i5䖑鲼jRom@%LTY?+giFg9.ho_zS[]?3Z+[ڏ- mJχl潼C :uFTr0.?tBzӼ~ x_.4N+:d$#mȊY}n]oɔoͥ>ik:ŧ7GFN{Ō`A6@$eW+j_#{5r+B8KɄW.F"f, ]?~ܺ [io_gٴWɗ8| CC^Mx4G QrJP6oBHɐ?mg5BVJJЌ핃 'UY(7jOr]/O}Կ9lQ[mAЯGz/ ~> i?_<; 4_eM O4pUi$ںBrIٿ>1okm[opkĆH6xޥSIdWY[N-XɨCszZkA]!,9j8ٚٶ9l6^|-qwvܛ;OOI$VO, 6+$AxЮTA |GG^~k MZ4褷v]DuFu4_ړ~|MxL/&K[6kKyHԺu`˕( XǮޖ_*zMɯ;v^g}6mmO2wyBGjCx1fڼRCZ]޵c4ZN\jV,Pi 4hG+(2,g}}۟ReՏ K`RkiBQYgyx_km$m{s5Ȗ7Fh;y< %x|Es&Cז~K-qh y. 1)o > xX6扠\! nsGeuXox{uPՓF][HImm;6vrGiUd VL.X&֚V4ZyIK+w;-G![k=n;9XeyKnT g/~ίii|^EhťG䌥)Pz]dzPJ.2U ̐ #Rs I)uO}5K+Yo,]Y\C`Wx nGyaR7ueÿĤZW܏V?X^6Z{mBB\c)vi ]OisqiXvBbY ibI>5|&kw_ D)[dB|5[-M_xTikL|beK0Nq֦>|F$Bk]?G8--!"nEBql}j՟o~l|OGCvhg Jms<-vRJfw#]SlAER((((((((((((((((~'x'Vtz;۽)R5iHDE f@8 ;*;$|; J>x_,4XC{.C )Q $ 5U?.y2Yj8;]?NXܖQsr$40w[nlZ~^dh}Zk]Z١oc61^[}WQ泱-^[hXϳ -NboZhÏxWVm[~?or\~{y#%?yP6e~OtnoxP<:u1$skn.`0d*CoHcJnoi6u=3I\iB6!2ऎhdNЍnWv^WuX¤OMߛZ7m]࿆_S_u隦" ޭik-󬀫LLne`1wG+KNo}KqN;0FC)yVsѧ|[/ĭcw3bt-.DVml; N.de_ w45ºϤ +;$(yaɮ7OD~~;kD~̐ij OiäYB ǩ'Ď#$:D,o߲~5Wb6R5ڒFEL֌!n/SY!5|MxR+[H_J&+ ;=QRO-7tcuE2*-s>+ iKঝiZx~-+MnL rDvTkoWuլ 7r[G;M &52**>a?ۓuu]zi=voU)\e2!Uf,IR:r/GʃOizٔKOg$>xmGL!,18ailo%6j?bPŶLWyػ[,#ɒU"U/x>,m7Jѭ=KNmeFaoE`#UmǑW h_ L|Ax.^8c,H#$GT u[qwW~wݲ/ ʴ]vs)[՚៊.ޡNGf/@ݧ\Aq@ҧ #O"yʈU©o) xgE9m5 ] CA,H@#%X8o?!6~]kXoDY\%\4Dev\Z;eki|8+?yoOg㋼tzWoun( (((((((((((((((((((((OOWx{hto./ [Ee[}ᴹpT|^? hKf$ 7HȤt+#6iIJu~@_Yv^)ҵK#Dm"Q36Q{Gt{cKk:(%Xf6UUʳ+Lc> mCX⿅W_5C.mjoiX@ hO,uPMGGŸMgsn:7|PǪ_XC9C M&3#".K+ꟛZϺ2I?ȥm|gDž9Xe2̭><?-'MվMvH; ;fDHHQ`V/>8ZNX] woZ\}>kՒ"#**`KB̺TJWV]Ob'[揠>>|+_&huJ>qc0"q wlQ*eck_פz-Mb- I% e}f%Oڗ'>e|:jnM߄NfG$"Q;+pn~,5; >?3bK=IB4ٻ 6+=7TV[dYnLv/>>kHM׉x^k;KkƉZ4t2qsDQ*+_Kj(O6@ho??x|/{~.N%[cnmZHcxkqgBQ=*_Z[ɩCjJj )#&Ԍ)A)Iߥm۵:hz۱%ӼAqjZ&AjDqj(^np-ehI .܄8浟aêhͷ/ckh=Py%hB.G ik~79Ux\r.HP۽B^I ]łm7#Z?m ʫxXCK&P0CRwKM-%{o}}xTn4.]˶󲶷G?WP,zΗMbJG.a6ܤnjɷtP^]Ui/#D(шC;L1lay (AዄzR5P./1heI{414嶫;RO <;-ᇋtO<:%6cP2*I N5%VkIqn-&OףxJaGXM,.?wJF˱ 37ˌWEڻ犴xO˻ln.m՚x\@FY\WD?:2߰hz|S[ZOaӪaDxvJXYxx, ;|ax+֐~OY}Tvm6ᜫba]~Ut~}s~\w~ x;◄x^7՞a«$nGNi-;34QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEq T֥mm>6x76>#nEr>  |3 eE"8S*2(ORkh)? /? ⼸UUѭxnK2O5k,~͖;>-X #-,a 'L{`oտgo/_>j}]h.vEO3̎C a$,1O <4bh9!xZE!r$[WaК*(nz-e٪ͧ{Om2y~Q$l2'*Dd5>/ᧁ<;Z\+F,D I8^ESw/N? |n9ϋkr[~^ZQHI[DQE ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?PKa'DE7S{{buildVersionHistory.plist pages-trunk-20080703_5 pages-trunk-20080707_3 pages-trunk-20080904_1 5A167 local build-Oct 16 2012 PKa'DEٵ1bp index.xml}isF9fcl(ٵ:${c P*A@)ʚd>]< #7ߟIcnL7+ǏhGlwyt`c{4XM|{Dk{Dhzby2DEiO7ӥGŶ?u/y__>g (_, 8ߟE/eٱs( )d띦-=gfuN3* 5u&tA;v<$,EA/ti=t в=,QVKC;N0`\*s|ҹֱ9])PhzsS;F]7]"'!mᘤDi$wN0`{QCN|Rۛ[goeuHP ͍{Ϲqgv|/'~_ ^ΩN4&m`no.`n:Wv\j@dG;Ǝm'v<}eΜid1^_Am1oCn%KCt>c]IPa&E@#v wi`ɷ]<Q-d[Z mUI^_.59vz$Yj7Ԯ$2?~3aBg6MX7ry3Qib^${w̞wۛ/p} ijl_=i-IN?WbDkCH@%l QĽL ~Qmbe $ Ǘr[)je/5lI P6LM{9,O( H=iP-zϷ N(J %#>{'LWpDA&T+"S֓&Kau|{Q53PtrϷؤEN~vJϷW?]p>a~sq3| ;nJ'f@ Zs CG$;밙ॴ*xw"IIO*JBN5*( ҟHTA/ϐ&$ +7B (-p㜥ԳDKlj>^?͟b;F1S‹gY_o=F~ MwN(bz;BSNDy>˄ۮ"R6$MH<\%@h0R5G+(|NQw¡so :8V^<5V$-ՎU-A|!A#7D%HOiiGX'$k3MeJ3I),llQtU#g3= px^:jT1?GY&!'tT|mE 462K\l u H\Jf0R.O$9a{]9hőQ)`f_6W'3;/YrM YC,grF4YîtB7v؁_'8Urtѧ;c&n<[. u qc"><:~}>Bmi|;ulIȾc#CMAo(EflIR$S%IVߖ:d7 \gϊ S <9N2QJ=9 ?R !)~fry"D>AC>C59 Ue_gedYS*8-B[UܠA%geaVuz.jJz79=W=-f3ug/ fQTXB*KRxU,FSU&Hd)F8Cb¶WFHÄsFX3DuMM]|A ( :LN[B]e>֍eY&w7;ޙ*+aҖ6Yiɚhe&Kywd'M0ъ\r3:BNAN6j$f rv|X$QU!QDehv;x2C?NEvyHvU%M7uٰ4E-E+zc\$ɔTI2$/sK=-PDղ5׋6;4@DvDN Oh\r 7X9&r)L8h:9%Cq$.1]06Y A3{t73mW0ⳃƇ611O/8J?%+OFZaC  vqV *3X@22 /1:f+l0ٰMh"Em,#B B+nO6 "p|{gnB~opEh`xY0Pc`Tlxl-=>py% `0O~rn4ށ؃S^{.a@*"B@(rOk 9~jH{v4՞mT-1Y,ߟizl8e))<Ʊ;h CK2#^4((5I)I Ue=p~+/T5.j5߱}b()-bz"4-H^]kU˔ݣ4[㚏2UnH'6t6jy.CW Omt&m2)~5z!u':ӎcd|mUqKCd|* $-U:YbH6MM+%4T?!N"jK&-QGxrۘWɠfܹXlO"MB첵a_z1'{CE{f׬ u qc"><:~}>Bmi|;y27΁ZD \mnNtFi!a$ndT/v cWTPNkYokYZV[eU' \/WU\^7GsCe6&ϑssi[!SɓI$>)JyRtR2(`S{Z=QBoSڌ]0,\]O 4#BM"$baMv/'A5tb(Uc>^/UxZ¹Ճ+ZZNI%jkT5g-$mޗCڪjsI+1ZxA0 v`Gkb"8̱[, %kpIchxD)yN)y^)yt22#YwZ֝G%ŏCxv ̣E荔Qȝ6b@5}M߼/P,_̹w@o"=2K4ãJm ?Yi Cй˫n9fvPt8#+}92!cl쐕!vxh^x+I1"E@=ဨ2}i 8t x0y`;!3ߪ2f]dMkkc}]6A1s$cKߋV.0n>anc/=(7XxES*}r 'eЕ([eqȱ[_U]`mf[8Zc/eJ7z|QԻW\n6Swfڀ*j; gda75NCV}*pUIB2Rؒ-)u3Inl p2^T^<2abC=ޟf+lzV)XGPGY(UNfD Ȳ'r /04I$QTm,};Ӱ)05UVd̳6Yiɚhe&Kywd'M0b+pA-QpW3'qŨx[]m)s˄@{maSd5N+a?eKO2/A*uO4qС`6Q)N/Pz MD*n}û b;XDo۴4ͬүd7;m9^%ZW*47͢t/ 4HRۤrroL:u1Aɽ".2RNOtěkEi=Ac B*F&Ie!z޷7ԸRVI@yrٍ'Ǔťn+FE~͓N*N^8!ΜE7ƖIzѭ**a.uE-UR,dYQEz)5U *u?広 GiwlX(E+-\$ɔTI2,;;ŒqOK2Q,a:bqw(DIt$'tbݸ~{n[EZ䨵Xʹ^^eKS-į\*eˮ2`e``mWkxLF\wH& /M.!\ i֪}`ݘ.X+~'˭A"V]1N'WTxE% WTAWYu{mB"\B bÅ*',0]&FadV/`׼MƵ)?t+_".4U"= 0J'nR宑2zCF'ynM ^f *arP25*]·ۛu&T?>uB6IaݵD1yimf3ug/ tH9[}šJvG![̅Ž"h6hC_Ɛ,NY:wddYtg##OP:1DX-AEm_f@B;PRJaFK?IMKEPٖ*YMSV+m϶ܶT5o_)W1<7;H|-9kǎi[,hRS][n|6u7P4>G FTgu_v0~WSyu 9 3H ۨtb۾:<׸>ɐϬ#hIQ6& i;DI ;.IZ>zҴ-( )GEŖ㨴#~A\{Z:D&A 9.1_h{oNL( r| PnwnHPY4v ;c2&ƤIl6D8aiKѹnC.'#AS<> 8EKEU@ fQku7ߑfNmA4`>ԁ l# 1Ѳ%/,wEi5tCW רѕAvfi}0˷NnGjBTXHH#i艳B${:Yh(p5}U:eHRsv&d5!Y.YZbגqfwGz]1:̷bk˥㊆#8J.MWXeI$dM۟h[-+Zm~A??xөO&>~*Nf.\Z>9pSnl"y yy O% C_?/'r򛋾AxeNx9儗^ndjłm77%?]iܺQC]]Nv9d͓KE>ԥ䗓_N~9@~Ayy\r˝*Sy$p<Ow:Yxi2mh8 4=dγo. R8X4eML-;$*Ibɸ%ZBvπf=)9T>W<>>\dMځCy!<{ȑV;8&qGhqG;&ܯ=emJ|c2TS8 v߻boύ #EUo#-Y]: \Muv #aëT  HDT{tbwj.7uދt;y~]E-D=Rj<2eDD\oD:ѿCj*3M\ MoJ9S EN.%hu2ѦH&X{a<# M t7:ԜrjΩLм&7AsMGO@37y>(*wg[ yES QWkT>|sfxAj/JNHt}RXmD6~M$I'$H0+7j#BM4P$yB yON"WԦ+џd 2qM=- qݡ͛c|QbSaK&ũ^&JN +Kx/ FӽҾ{7'%?k޾9G:Rk4%5rHh [ʂuЧ]ֺͬ%۰䙳?K#=e./-l8 {A#*ڙn<;ĻTjk̺i.iʢUYoxWԂƍ=\uf" +lfiв"'{2וDՕ wg Q qg_+pgO%)i,h-@5I:ubG k+O %gWN 0aY0j;qQymmkxѤ2ueLN;HAc"ykXzL;~?ϣbT=ug)jt?}=G~*4Iu ǣxl"۟tВ"t$vA{ll""yeKH/JnqQ+;Hݺ\~CM~1It0NA%}_e*Gф`)j{oՎ_݁g=UC,Y 45d2_JʪVG;3֔eñ'7,!$ !2!ԕJt 6i.Ħw@$1GhmU:ShM M-~F'ʧCID9S2̭*BM*%+̓epXs#՝! @ęJ:UW!bDs8|co!E嬪-V>Fܚx@K.#M r)V+!Z jw$I }~u&dnvZء-;|`rdlL#h\Di"yfe5-e|I-n;xVO5@q;Ň#U7T )j+fO%ro4]EȥVDJQZYL=̾Lz0VpKJAb@ܕ& |&MtF3,7(4RU}.MHE|Ȋħ7$At9vdM^Mxڡt?hkKJnRXcm$YhɦʾB'\m t2 s c49RBYɈ}*ވx~gȏi3{:;ECcE$74"?6=s n2;D`vd2uObV U^F8 Vvg o r84XWa]eUXí։d(E.\c|)Ao΋ xf= ݗ1x^ X!G}qORZudJ9M/%yE}KRSA\gmU %fuwӗxu|O65h:OrH%++Kbt 2EbE PK%G(*&jʪbdPVSY6DSR!5[USXxw-V_N9ݧ~{v| BitV /#C.{ 4>)ͣ܂$an`Qd-5&@e/IjKGy*ZmX{Sac,ݕVBWH GrC%tB{z%t I"&F9Fvjʊc?XIfxfJg)j^M5zZeDVdzu-9MՏmZVaz5WE]ĕc~FNYy5'ұgQU_>)́?~S=c,5Fd9yN|p+)ogn%~`Oc']lܙglY!Xn7+:WU|fc]|ș Vo206QZyRJ%i.IsIKҼF]>x)%]⥔85祔85ԼkY{J~"fՊC|Bw}\uY&וuzr]\WuyrݓJ+.wFw³Rgmsx\-gyCO/[7[n겡jjA|S%&I B7-f%K Iqsy}UIK~޹Ӈ_*t3,\_L@f 'y[@ž n?>@)idBJFyJRhOIs)i4iWyJ8N/%'QET 0 K,i5K15YV&^EM,7IsҼ֜4sҼ4{"^/f9TVMYixVgYixV]ҨGFYixV渲[ iixZyj 7iXbx4‰iMff}Dxn!\ʜZriLr}+ON3@r6ONs4t4i4G"i.+d)$Yl"{|Ihn@Vʚ!arh4;;)<=aӼN i^.5.BzhF Wxh>?|]K<4B<4 ~6" ˒ȺUjheTJ%J.).<4|]+4OB;C2<4|KhvġCyh>?}<4|: 7o,4__04__84mH,4 'C2 '% P1?Q/9d(V},|g:-N_s7`N ]-/6P'^S3'DgE^0'ݍUFnvj -e38:]`J,ZzetX>CH7(>>BKwWלyfP2OH擊; |2@|r'x4O3'GDK0IՔDIL0LK"v뢢 > \ <ω<'s:Q< st9yNN$ωVp;A팵T 'o_ wlyT^|n\%/Yw 7Xp;}zT0.bUjI%lZ]_U|ӿ™FP rM*D΍ai0 O@_3cCp7xr;c/av fE,2r6#K4 8- 7~6S0s_7«ygRf颪Y~)yds8XBjӶ&EyoŰlMM7êp5C_ڍ$Js[h?䡘G#bC)F_x)2D~ZQDؕOBr "#cmF8W:^~g|ma@V D';6LbEжl"qq'LiJu;dr8{fOy=eMnIbz܊؟ 9L@Ә=r|Oʠ`Wd 8V` ?,G<¸?-`\UN"Ӑ^et";Բ7K5Ka"շɥ[J >3l{ƹ] {0X5|*sش d6F\"5w_FZK%RuTj.uNn]Xxݮ]n^.CbDdJrA|4PeÐ4I 4Ka(L5 PuY(n۵]v]!^/Uk9T.Cux.^uxݮ]v/U`ܰ)qSyJWwS|!/_x l yB^/|S,_gK/4ԗ._g FH|'|!|_I/dnĪ6u*n'he';,=]"ߠW2]I q~šfeDܒ+C4Nsv!|ӯxKT4N 47$H!$R)awMI0pE6pQc&rbO2#BIٳ+LRA/zz鋍2k (itϴ~MsnS6δ nΈaޮSqJhbHB̄ *Ӓl&]8hU{„K)5B 8^Nրx 8^jw5,׀kfkp}uz5,_䘑hfjZJA|UPMSVU2 Cbl0C>(&/Nj"pȋ"p^/Tn9T8Kex8^ex]/U%\GH/ E+ Hol` $zH{Ao }& ҁ h ī ;ڼZ$UeL$Ke.De^"ļdi:HұTL^5tݒ%2Yjla^WtUQUdE3$;hiӂak#K#/ޱT;x~q";*Rap$)H itU;>wW.ю } fKl֮(fuLrz+gݞk߂L2$kYrD:PiҴ@iNȽ-Pd @L`w} ҡ4'#YR6%dm^#"N R-!X-A",V2\8m%i7쿞 ]e ҆~|)-J4@ JuϘ v*p]@  <vbooPiUX.@xdET: b.lѶ( T1YjBw ȵUԖ'\* wZr|$],0EQUtQ)dIMДLYLSVŽ)nYΒ]qyZB"ݞh{8Ҭ(W`C7vf[Lq*F6jfIf>!k9znYmS`&@my!a4*^,4/ $3g5mS{Er,×nOIP̲5!0gqfW*9D20{J1yxLIZV|TOͶ"UYPYhJ]"+srJ<9ĕ,ݟ@ iʃ4Mw2.k{6i:>yL-(種*T'\@)*>*k36S%Ut}`Mh_X6utcwh.,Q364% N7*9ޞ1sWUah?g'O4A)\ʤҤMGrô=(W}k>3g-WCYƴF,љiw*ԁ7:Gi :džSsrg«[c#y-F¨K&Ab™ajО>r㬱|,T43ys>e8>@p&Ozs~G Dݗn~g B9 M"GnޞPj7Yӑ ,i L/W`kFQ?1ި ^meYκך0+-oFB8 4t{ 9Kg^:2@H) QTxo :)2# p{\Fx|^/LvC1JM.o]B/mtJec@O,JSOxu%6e%k6:j 8t kwXI'^ٵI.Nsq*#SxqnZ-D=uݍaѻW] ̀\"-)/ݗCrϣӻKx -TܔsSM97rS唹iW-UI*#Zu~;JUdXeoK c–8҆P:=,i׈yId"tT9Χ31&2:DԆ-#żHOnEOb85Aʥh4%Z(0NH b#Qr;Q!g)!%"%\,n*A,4z j3f=%GvN"H1Y#80׈}W| +s7VHl5o| n ' v2xj:XozfTaؗG7D^wM]'M ӄ ti16t1f7M'rߨEՖ诮?BipP>0m72=+."AyY_v ]?$I;Q.Nl?#Q#p{9Xaw}bhY{7.:sGRNX`d[ViT@ʢnբwFO0;tn%e9UJX֓ h@p@.2;6a߂'1ߘrHSǭ^~qĶ0&&L&9KD) }^e{?ޫ^=T6ʊx!vPaA*vPfTҁrIL7pIPS5;pFQ>QD'%mUct}}}/D'u*&5ًbQ)D(W pY03oudVJ,vtO2‡I蓲xtd-K*[k8Ikx,&K'kF5Y}i}m`Hy "v&vY9Qr{/M?mM;}8:Yu̷"bY'&b@R4eerU:yK9m"UQvEW a)oňk l0C^Vk 83[v&ʆN3']UM"n4g h$iWQtJ+.s/,@Y]gZˬ8(IF3p5Ȅ>3z ;4d7y )>55kB>h'֐vO3vXvk9؝iNrlD'IGҫ;,d#6*GDvV3t9IQaTكޗ0)*DC>$?XFˬEf^%cCOzPzgYKߙt;+]RsRxhGٯFc1ڑul2&7끡mvA`k-)U|=PɁa}CT)YN30x>&:i$4#UDǗ,-R?'M:&6Aܓ~Q2]'˅>CR 6=o^r1e:&/ĂQy65+R"= e/O^ 4qxzma7:d1_A ~$UT餬m6h~|/DTG2גgMhz ŝtRn,XCC^;x ֩4/}5TdhNM݋KGGS(M:}nۤdY86}V>>,Hˮ|B#?5OegD7)Mú_CQi_8!ܑ\e72ǀcuhCQytCs!?邃swKI秶79}{7 'EʎP=h '|y;`ϲWQY awOѭ)2g{4nYbh,y2dfnBV><|3L޻0̒"x'F^;O=;zDj!8S#-џ8|_pڢ`̊doBCg13 6Y rϔTyft@.Y[]LWz)Lڎ3'ű&i 6dIe]LESX555PTŀv2~bʦeX%KngފaZ"h*<3hNH]UL!^5$KЖJ |2:6 S7,4I- tx(k##|}B J.)F OM&(G(q3%n. Bu?)CvrfRED3ptU~΁ !o+AsmRC=jXzp}INno/ 2Ԩw~>*(cQ|L]a<)%MU-Ie'_e2-s"[v}ͷDr Ia-qbFUmJǝahLJ>1J/ɴ%-;7K+4,"QS|)46m.qW 9a28F U)#ß4|N1h7j럥|4R8&5N$~ *QtBKv&Y]˕P[ 'RHL92so#_l& TqpZ8iXy/+I)$2$iH 򒐆zC*C&Q3v$”cI L9ց:0Xcyu`x#;Z/-񗎖KGK%I ܣٱp0z{l*A7cQ4?[LN)'5;a$*o?.jkCE>߂?#A %"iDvHQOŸAl&m_Q'{^Cve + &Z ?jмBdHadMN?ga""3,˺^LԃYf<-\Ͼwaj> E.ٖ[PlrDueET~ V" +pMR&Ic uѶNɢG'l<6ESv3Iu}{ktU3^:׏֘Z TR"2q?!:ɐdž$>ٿ6Q,(w,s< 3Ў{m6kdf{icQdPeJ`2"| :sؾn\Zr(aRَv1V7nFQƢ'uHm:N[ȯuB^1+2dQ25;S-ُJ>89Erb YASgfS! hX+,YG+"Gh@vX1O{[9ǖوWߜRIX3eQ#}H2Xl $I˪.!sı$JZL8] @H̨IYIzt^IEvta1B8'd?+w궊(2>=w=ZIOw H%V$~=|_4%wss[z&e#چLeZk\X9a<١ؾT{ʏro3:Y=K:{u9K霥|dqX`2.4JDRҥԲvTsup,]3MMe(EF;I>ZvUg)%! ZZ&1斥QQЫ:Zg wRN*IeN*/}IYUQB좇2KB4[Xa]K5vϕE/l?ЕN˟@ (y~]+%_x b f6WlӬnT~a9w^9L=V[=Vs:9L}ar;4nF0bkzh2恚u  COS'jc'wzs4QX9ncOXcc{2( 4t_XA/D+X7'=Q*Nkf xr _~nP1J (wZR;N^Zڎ^Nay^vt5ǢV܏?l֎ hȚ\ M!`RfPңttQi0{Z0+Q>7DK>W%R5]k%]Wôa5]F}H0 ]]zK~]zxZí!]zw@ƚ.k,YCDVMgT*RCȧ CGktG'1*tUT"uzuqw.An &L% beqD!~CDvoIRSjퟣ4xj*+_BYa;-2za^+J#3-\bXVHOB2x %OCǎI((Sq[T:UJf*URtø2Ts(_\]__o*@395ݻQnrp7KH3nf)nfӉ4Ch+aX)5L7C5P<r4IJ"LI 6A C^+$k_Gu/j,$:p`@/pe,pކ8)ۈ@W[iR%%iFQ!QF(/BP>\ꦩjl\Gt}~~.+W[Ig5k*xG9F7ōQG(n(nb6F#b]I)h,9֞;y} hVآd䮵FC$r2Dc\bY{:uֱlF+Jsrkm^v_(Il.YPTfsfrp+NʈɵDvf=4C44]pz9vR<\~(jv3b{.//./. UR. R:?%YkZ~cAHT0 5,?<Ϟv {l͆FQ1XD/Ncݨdi c?= W"` < 'vO(N21Tdm7VvIuvn՘ }wwTPڻq(]!uF]ùjl  a1"jr.b]]DΌ i?ڻbsVԫޥc-cޥw[-GTOR_D(ZJUshMU^ۮ߇hE#QAf;jJDUn"j "SD>qE&ra^(}ߘ*ETQDUN˹Xُ*jrrr2ҎRo3."@z M8f. mlQ ұ v\OU$:6$l*x`w؆pŴ>G$a{P!SI*mOhQLJүy<=[kwr*۟yGgc&+*Z?$m\"))`Z^?L}R g]iEhi<$(¿8D'\֣}ɚ Jz0TPf=Yn0au,`=?5D]D<#y@oqɿS @'N`Ҁ>-S7UAM̪J{2ΦXGk{?0a}d/@$!뵇=@>aODHS2nX"IC+z 쉚dhyFr+]PƵWZ^.)nr%s (h`D,4^C:ZV}M>g&P 3@LtW :ob'v0K ̠;4 {6CL ocg-9ڙ{`j)'5bC$kع 1 D)A/8sw=j@>7D•흡!$Q$EKOMQ64Zpf0rgν]|hJ(ijiƖ*&˒l^j>E\,P MMK%(5(nn/7{vo癱ea*HY `9,קudm**C0-Q< oPK>a'DZj)''thumbs/PageCapThumbV2-1.tiffPK>a'Dv7f[,(thumbs/PageCapThumbV2-2.tiffPK>a'DQS:AQuickLook/Thumbnail.jpgPK>a'DE7S{{D;buildVersionHistory.plistPK>a'DEٵ1bp <index.xmlPKWitksnap-3.4.0/Documentation/Shortcuts/Shortcuts_SNAP3.pdf000066400000000000000000001415671263013355200234110ustar00rootroot00000000000000%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x]ےq}h~ r}lҮL~ ) I3wO]dvUuOrt=O̺/oS_YQ}շuW?ܦ_^TH-R.ۻek۶o}v z8ӡ]_wY_O_yyww_ypQ]tDaalwa8(mwV#?w0||͟, v!+nNiǺNinBǺvcSo/.US_n.aQG}3~N%kU8C 2Vi>?i[QoToC滗55Cc݄4uVE?15jm^^TuoXY7..Vqt_~p1/Fƽnn.>N;uY0Сa7HaUl 4%yY^v@)2^d? MhzIZS@ 6?%+=p+@!f*C?q,\iPoUeJ gh iFi'&>Є)IJ<)6h`hŎ}EiSgyrhT2Җgq?9)(rCW bBn@ eP"^2KU8UC2U-Ar ! Urs!mXq}bFV +bbco8ԈSlj&`RLk;`2NX$L\~FX(XLEed =;[#١o47LMAȧnLjj¸VE EEx$)mGhѣYHD9`foBI,IJiJ4jaD&ӭC U5F:t+ή͌AD0S)nMp_(fY jQ1GIx=zExQ]Tv&K 2ż()kiѰ^:/f.tJ Jf٬+[DI|$u@,%^BST3e9pHӡ3!ɔV褈FyPlA,X >H"KVL%17U 6]7'+2}4KQ5.PI?Q$]uΒJ_]lP2z5~F5dG8`&J eIQ:l:ɝ᠞ ZOAc/ k9Mb6 !jP7xDJ FI Pڏ(kMA]}ߟS<d:Y^@ RPtAPAGـCˬtl w+r/3Ð0@io] u?I@BkenbdZOWXΚ#<& ga.~1$Ǻ֤UKP@m `0R&pad0& Dda.[Q()wYHLB$!$6-`Bq&|Z7>tNXzgU9 pP K#_R n*,F]ƶ P7bUD'M06d%*Q`̄Ƶ:D7RrqK.)Kmd#ЈWIgoX+0'\OD(Ŧ Gk9VqUl  6OTi3u,6uŎD(9>2D!q([JYi7լV 2؈G^e.xJb_XH|61%3CL"od.a*seU1@ ueyLa#J%|Nw֙iXO.*K,* `ejwܬ=!eJB%o14`"AN d3 p<,_@h-9#"bW>H3@b(%@R,ggp褤'&Q}G>hKB)߷gN*Pl=RM1q4R1^#AJu(1h4\&RLmX浥Pá 2CQC[r1M+ܮ(23akBkR>2 E!Ɂ͢_KxiX&USd:/IiQ(chns jnB ` ͠])4/˪\q=X#Qӯҝ~in,ٸHž*Sc: BOHQ|BwY\q&\wiP;Dufy}u%/\SHw.ٹ;Cw.ܹ؟uzFf75nHf/if4M'ѕm/,!dAKvxu"Em5ܬ8t59}+٘hFĬ9J5;7ά7"9ؚv@vNaݒ- ;c=⤫`c 0ݷ`[s I~Q:NgsAU-!sѢm&uHCu0ֺ+:$F!%NW~sE,4kvFCjMLglq><%]u肂|37a68I7,6A^)]zEIu8c =?B[] e۟haaJG87]S׵`;h%tv-z8 :}/nۚ0p<߰E5ǹ;,<:;zQ`KYF@pryVk֎^٪C݊Lp<6 `X3=2ٳOT1)v0A gB&lݴ$hğv>GM@h)`|g?R+zi-z2;5#dc7dDelB'ّD~g929P>{˙!ր&Fݼ>qDk=s+M$6IF+) QK>4˘mHn;?ƫ*t:*sk#xBt3^G9۠->~쌓d#ـ tSxB\hav9mh-pGhn\ DzBb?*;D-e~(: M #/ VPyЈۋ#g2e=( -1fmεt(.0lqŀVػA-$Q)s5$Q6lX%x9+Z#sup[SF\92?Ӫ^,qDgyNv1L""HSxHCX,NM֕2NTq^>Fr-O-@I>XrVOX3# pӎ$ak+^JslW{9iO1lSdTg)QGM`AՅH)М d2V㊔3DtZs(u 6&Yss uihPD˘)]5\Nv3vuK@}FJH9ڙSJdJK^Eo)h2"LlM \2.fQ!+52 D"|ǢVoXzRINڝ9֊ kz\>mbg.(jH>x΢ICbAPfⳜnRoP#$ f?n8߬cDu f#&L&}fy)0e}1,EXL-g=3hO>fL;K*"m 阇ԶD)WUDᜑ\ҢyNOU+CJ?Y2%V$upjI(z|?Z0 F<=NJ:,m,4$p :|Ő\ mvߛ%Ζa5+j S +E&< 0Q.}W'b =k\074abo`+5p i|,w}.vo ]r@ʎ|| އ/6wHzzr/i3ȋ&pa/I];C+̴W7>TwӬ;xBW0[\uĥ1.D~VRI~֟ggp#G 2c'c[ WJҺ;V.a=.CӼ?8*{8? /oۡ/gQI+^t WMBgTGp##lj4)p52\CTGY[.IL̮@Jnľ} U݆vjy|F;xf=,ZϨwrR]XZi"7@_K|̴H[S,nvm` 'xrܵqеbY 5̀Ƶqe&<bk3 6L]PWj\tڬw&t4uFJ #pKM-SP-#p6:vQIn &)hs>I4t\-qh{ ٽV"0t·KK爍n:YאttUfA$LCp܄:\n(e$_f )ԵYک|V@ Ы^M 9{rs%Ğo4 .<ۉN%z$W֠ A$&e3b U1E8'0$uR2;I^]!%`XZvw Ҟbbh{ "3uP@=4;0r/e!_,_HaKx*;LQ`M5zʊnȚZ=R=A%PLj u} Le%RKfqJ]T[6Ze} `\::jr9'HypJA$e|W :z8gJ)yUyK**+*[8C\ Cdw&N(6:wB*QubaG%lD6I$Ⱦj9wZlg붸=³ec#͑eGKE_mMTJa" JuĩV0Tza10l?_s'7~VveVOy%4lLUCX %V*+.t9\˔MֽBV3Ne%؟$*V_~t<-6!6H6=M8D٤YTcF8i|; K!Q(SM"D)υcn0>ZggUi4m`!ode LjHY$!:tYqMO8=Ή,l |R>%dlj]R6,"Vp-P/^S%;x4n endstream endobj 5 0 obj 7274 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 792 612] >> endobj 6 0 obj << /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R /Cs2 10 0 R >> /Font << /TT2.0 9 0 R /TT1.0 8 0 R /TT4.0 13 0 R /TT3.1 12 0 R >> >> endobj 14 0 obj << /Length 15 0 R /N 1 /Alternate /DeviceGray /Filter /FlateDecode >> stream xUMlUgŠU+'ZEi *:i"Rm֛ew6QOT! zZ~^TTH 8YCz̛fK}D|XU$~uB}NRI/<Ƈ7ޅצר܎{%U:,vsks$Gȭ!|];DbUxfϵt$Y:卵HNIVz-#Cz [Eydʈx0q+G#؅бGU ZT扞ȗsgWs;<ć̫|xHK}/wѴ%Q)o))_)ϕ$+ʊrY||ݧ(u[=vfq1ܱAk3o,mEp gK~nߥvjjyaqmBim ր6vq5y=klfUg=.SfE#fsH޵]:QV݇I!H&ɒf3Ì}sDf7N=ΈY1:yɄsd{}^_{4md(EsߚbPןY"s>aض@ Y'~Gjju72J&"j endstream endobj 15 0 obj 1088 endobj 7 0 obj [ /ICCBased 14 0 R ] endobj 16 0 obj << /Length 17 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xUoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqx endstream endobj 17 0 obj 1047 endobj 10 0 obj [ /ICCBased 16 0 R ] endobj 3 0 obj << /Type /Pages /MediaBox [0 0 792 612] /Count 1 /Kids [ 2 0 R ] >> endobj 18 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 9 0 obj << /Type /Font /Subtype /TrueType /BaseFont /UEITHG+Helvetica /FontDescriptor 19 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar 222 /Widths [ 278 0 0 0 0 0 0 0 333 333 0 584 278 333 278 278 0 556 556 556 556 556 0 0 0 0 278 0 584 584 584 0 0 667 667 722 722 667 611 778 0 278 500 667 556 833 0 778 667 778 722 667 611 722 667 944 667 0 611 0 0 0 0 0 0 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 0 333 500 278 556 500 722 500 500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 333 333 0 0 0 0 0 0 0 0 0 0 500 ] >> endobj 19 0 obj << /Type /FontDescriptor /FontName /UEITHG+Helvetica /Flags 32 /FontBBox [-951 -481 1445 1122] /ItalicAngle 0 /Ascent 770 /Descent -230 /CapHeight 717 /StemV 98 /XHeight 523 /StemH 85 /AvgWidth -441 /MaxWidth 1500 /FontFile2 20 0 R >> endobj 20 0 obj << /Length 21 0 R /Length1 20896 /Filter /FlateDecode >> stream xռyxTE?^u^^twHHBB!ʢ A,BXdAY}"D0xQpGGt%י":B>u: dg>/{NYzwT̛;1e'F=h?N9~vn?I8i^F.f>y(!u !%wO41u\ƾt*;YSg[Z|&vw53/x? ;Ϝ~ٳ˳N긞ցw "X@|^ β`ۄ*#VE6!mW0r]5#6#WhaSJF浒JP (/YFAπx2>HV WK/v>GP"!t w[TW%1 z }4}DbRK閽9Kd6h׶'Xzv!Q t_]C_rtOhUWAi#Bz 3ujGz)p{h}nZhn~ j,*[PiY8T8**߄p#.C kXS@M'A'=с({mlw.ʼn9hP4_<-'["9OΖcrXNM(fŨE[{*C!TB,;*"_pگ S^h;[,>I+Itԡ]Jv±26*H[%T驴63Wy7Y׼#P\ +{85*/oЈE{fO\3)R3.R3 4e22^>hc&L'5ώLnxItS2榺W&'&UiJ4DWP5wU]Uasٻ&huzW;=kBb.i#̨6(9{d3Wҭ8X=GE}7. k4ʟmJ<{Ip*`p$\pGkrz&wNw 7njGC- x$< <$a}~v^acc3|k[rs_;{ 8ޯLaJIbi%4Q?k%,FBn;w+,qP?m\[Ƌ@s$tr}C 蜃>0'҃-^#=;t9K̤kE"f}%rw}#Qi'P>n%cHI!;jiḦ3ƛ Zxf97=HIO}" @I2 |C v{3M Ob ;"0c?yxBbgd3gMt8/ᖩnq~3\x'%c}wi*{1<q42B[iq(Q7D_gi:9Ajx7L}hb[bkWxQ) OnM - ;Fh ޻zEI^b(--Ӯ/LcgrkV9vO,3~ۅ6wmФdEeV^`sSmVlHCa~kZc9r6 &>*Nl0aPxCJm4ߑLY#(lN,Q i=Gl\rLWͧZU}rj=kPK֯Pi//?ipi;4"-Ƒ]'L:?g~WicR GBdSW6p:dc²T=@fAJ]nWq(LI. "mؤ؝H$# ~׾곶sMaI-51qO3?ĩ䃕s6/|ҧy.V-~f?8zeoTbwOd"c:N0^IITo卲RtIyYJi͚VU2kc-פcK,`:j b+{Q-o'po~=@ Tm^I9%U ~q"CZWngebz*FL4yn2lvzw LL?3iPq"`eY,#8ߵCgU&.\i6 ~}xA%:64$d)ś-Y_7c]qfx "™YmIg_ְӽv.mg3 uj::7fZ}[Z~aa%|btP*»St+ }u?Blr}} XDb?_Øq6/dDQ%R)D=OD2|۫ڻ+,P һ S YVa?hI:uI#_^ōoԍ-1sa_5p 9CO'sh0s{xUs$ށY6tvuΝL~j퉪:ROWk%dzkktR"> "]J7{7Zá+,[%®KCkG>G NrFDLMUM ut*sV %K=EGz}ԆcQb$SAuzApnAYi oIEԶ'l&AOѧ^𚱐wϐdҫLjtIjüǔ(F t‘fX|%;> :@|P~ږ9nHz-an43 VVԛ %=2lroliu:74ݺ[j\@ !;dWn+Baˆ/Wf9)eA{Z.@Rsɟ oT!>%FY{A fHzMgyXo~ϡܱ_u$ݵhC_ 2Jܘ3dNH.LPhr y^Z{Њg9$My%+]D7|^B\eG*) ƞN] |!Rhe PƳ) ih>!6mI=f .]19,EfM~DQ c,"i6lTW sx4e S[v?G2bcnG1BpF}I m{\} {3>_,?:auķBꡉ,9(>Tqt>3Ddn W)sxWYW4y!-Ǟ ..}(U;Tn?Z-@EGƅ i7 tY(7x^_w&YpB{d#C&?5LO]2Ą&8]ekKsYMkn9垙S1"\$mtP |/K ,,䘲7<}v}Oj!^Jai:L ]fhԘPD9^ Sх"*rذC5ugNRygJEY0HJqMo 'ȫݹ˺Mzߛ_k4?,~5Ng׫og܋𬼁/O9gOsZBǭCV D7,- ڑ:uJKb(vR(PJS MP:`L5J|N}TFzJ N,tr!h0xXoFcthƜ 龃S~i. 3$ИMf~0Xb\J~3ߊ?ȆVbۮ'bp*c>kntQTbDӼlWt zY^]yD.>G HM0!|G|WaSeIreŐ<-soeЈ[ ތ̝@4@4C.[#Oޢ<5T|T<νlKkm")LؑurAATxL(a kFMxG^ ƿ%֏NWApTǻvt(ZǿO?7|jCNm}L_fύ89hs 3mhhKO! %KBr :^(/ǟs)$b2-zcWC'%V=d0w0${i o(Dt+} jquxRL8c_~b .u-mOBXx%0qu 'I^8{=e Bǝ\U"3(be!E"6:ƫO/"Vf4Ǩ"tp~0X3[?n1uG_2?9,~iuvt }Z~|.(FzB}8z7>Q28Ͷȸ&:SӘw+d3 :Ptp,N׿p,Iܦbl۵U-, #m8?WNVݩΣk&ɗۓ=C*?a?Čit"~}cti>}9߹п{ҧ)t.N_#K7hC=NN"tN4B8i= "*ys:w2,FĚbV-,GEA`S*yPLbiQ9i*"`I(E"`UEDS#iq365"ddG.X07YW#kNMZ)Y=#oO9aѮ_)Rջ S)פiN߫o`h@Xv(.~ G64֊N&6:KyI:'"8|o ~'B̖ .]?]Ɉ:CQ_6-ݗĢq=ax#䂉ey8TܚXVk9!e0K|  Qn' <s{OV}'g3s-D7/荂Qn,_:g{<±G.;~SN?G^qw'WC&?.^516'-J% Yr33+S ~<O/ufŚꉺ(l_Q(j@¢g;5$?6`( ƙ:'hgHv5D.źF/wH|m'"G Cy$ɣ^*]ܐGMI6UXf b** `T<{C/"LꙒvi}tL{POq=?wkd=Vx-{~{5Vyn=d躌>,,.vR2fk et+5؅w<߂0lb!tO$2'( 8>* $ȷ35(nP)jA l6GUŸXf0=-:r9tQCLxuPHIGܓ2|̋`^E{[ F]|W<|y3ӟ5i~֩~MV`sb)C3KЛ(t Sŕ ,2|QPHEDBZH! KJY|`lXT眀SPr6a);i3gc 8? _1YRy*F,ޠByna}@A\5_+ Q |`3LVqȫ q KVEy! ^1x~,Aɮ@*Ih4KF 1b=Px႞]Xf t0|)U RT` mJ.nI Stz'[z<aDQ+-ZLU _8;q=PSRc",G9 %1܆9:Z?ѡ'@LSYuu ]rя}7(P%ebv6-)[D\L*BhZTdE'CeP=7FmIDŀl D(zr(^Lk<3t^jyg%'Ӹ1-h7(u )$1YHz#G RPA7z=g`|&6V 93]q,5疃o--_+NjA9)K  }/7/5(L۽ғZv͓4mnToAZ[rfEr.`1zq9zFmYTL x J rE!nILy{%#zn$pKv`q"X5Ǔ]ÙH+JKDjp`2p3\7鑥L!uF}0ne #^(Ӵl+^NsV1wfW4߆iab[`nlWvxq;O6nKF1=fq׏OlK:4r%]h Ɇѩ݂V2Ћp°\pBlt)p I2J2b M+:pȏeïܻ-sg?[, @mW QmKbTqpi QٮnoϷXm!C+sm~Э0&džxѧ+14P]s"sc>RšEJۊ/{vɳ[|٭*_Wm?:O~ab(䶵X 3ԘXIy̷-ċf.Mt8Ͷ4#aL8dǟo'?V>}8r}>b8a=a(b8+m+U.ɲ+7c QWZ/zFيy@\ųޚR~d꿟f zraӴ* Rb8,dfg0`@c4ǨqHF@0+ A[G{2 ,Gp9éP $WÑb-m+Z>,+Y.+>7us"X8b #N" wݽ]>7H00JwT!qbӐD9]&ɦ"*19nlK3̜^OYRdC1 YLifCe$'oIqɫk5Ӳg{7bFOzЕjbHc2@WV_Jx)5@+3|L "H9Чww}݌温O,tf\h+g<]|Jej:=eD}A5+jEac5$l4E1Oρ0p$9SR2eLzR[dYarw-.Z1[=/xIK~^AZ^]g>+.tknal2|#{njq*).22Fqn%.b7qv,t$iԛr9ą_^ OHֽ2-)[t9m9F_|%QB\%+YJs+n;-akx iv}6 5=m~-?gRӵ{r•EVhi hCW<(\{,%I=4crfO"l1weV[F>qdak&,;L5]`ұ $w4Ida 7"y z Иi'K)7~4,JyMSظ_1SRz;eD-t;Qϱ9*3-EOڦ0b3#*Dfe1׋ve%WrRHG"<1`3xe|麸f+!B) jt.X3uV.Fe7J %xFq! hm ,1UlÔ-VTnqoiMۺq죗<ǟU4]F`qXzE͘훗e+~Y 842\#dy#Y2 [J'/&H:&^snMZX"Ti154G!F7E@=4`ñt'L32$i6)q}Ƣ:=P06A?̐TZPTgx*Lohͽw>y1j.8Z5Ͽ=!tnFZh;3w=vE&z큂.5>tg@r:q4ÈWM#f1LDWzrLCXtم ';2Nd3Ɋ6KЖZ}m`2`2,b`& ^) & 5l,nYr4ңZﱫG7gD{\q:e H ?z̛~ڢ:tC!~|Kgޗ_@;K EЈY(̓$3ek+%VqS7PPxoԋz. tSʳTh9 6LܱڹҳóVkM 5isk{;ԒASNZ|CV[VMEB^4't(:Cӳ 1Qðs:/5ԁa=c6'&c\3mnKKـcev |a9l^Ko9k˩K{`=z'bMzb`A"1$P0E}D/-I {GP{0÷1+D{Hd$X :EZ F^H4Hs7ں{Qjpeq#984 vT<6:G9q84*/WgO=qnzdϞZp1Zܤ2A[6J6P(,Vxcv+ߔ0ޒxCv壒5e Yyj Y;y\y؎ bm2B I2>QЄ0xKxBd O@^G&1[+Tn^ŷ12m8{ ~ ։Kj㗍b>L 758p ل;5:]6J5_]M]ꋺޤԐ~C #Q/#\n&aAh1XB^%G TcQ—7ϫt{Ӥy&׮NR 7 T M-mmN>ES@P6 T M-mmN>) (TՁV6ZA'A΃~vS}~zSN:__πݼK?+% endstream endobj 21 0 obj 14033 endobj 13 0 obj << /Type /Font /Subtype /TrueType /BaseFont /BMRWIA+Helvetica-Oblique /FontDescriptor 22 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar 121 /Widths [ 278 0 0 0 0 0 0 0 333 333 0 0 278 0 0 0 0 0 556 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 667 611 0 0 944 0 0 0 0 0 0 0 0 0 556 556 500 556 556 278 556 556 222 0 0 222 833 556 556 556 0 333 500 278 556 500 722 500 500 ] >> endobj 22 0 obj << /Type /FontDescriptor /FontName /BMRWIA+Helvetica-Oblique /Flags 96 /FontBBox [-933 -481 1571 1138] /ItalicAngle -6 /Ascent 770 /Descent -230 /CapHeight 717 /StemV 98 /XHeight 523 /StemH 85 /AvgWidth -441 /MaxWidth 1500 /FontFile2 23 0 R >> endobj 23 0 obj << /Length 24 0 R /Length1 9732 /Filter /FlateDecode >> stream xZ XTGKw#-ЬͥYdQP[Ypc%.$81Hh&,>(-m`\$53f1Ƙ,3L43/JvL>图[uj;sT{Yle5J O *j5TW48yMҷW蝼EaAW׸$zrij- <4~j'DUz koXO/vj._UHUwPN&79("zZ|2eWmÆ!dޤ'+GHnA(,UDۣ Oت"ܥ靈XbciI3=@>Nր6 CMא@:. i޵QE3XS1Bg|/#]KHsxX6W6 (eiEAζ.~kXu}X1Wgw4-REvʬes/kYֿ[dUɚê+9L&\)bg-_[ ֨bkԲ4F0{%oIB>:;πQq,Lv "snr.ڞM:{4 GL$oS2!/ r Lj_ԶRc-x3d9No)2oQ[Dä ߢwL8:n17cdBn %Z&ϒ ]O;F%ǧpU- &Ÿh'o~xHDH-'YJ.+63sN I.<<y_wRYTF\jŜz&6t#MJUjK~2?_QX.t wmGuOB,ΑK+i͢sq5ӽ\OBz^O;z9ΗVp۸6_?o cDN/~0*?W7qd8>u!VE LL ̶ ,jΓWo?jH$\ZC3iY9s|8.+*z5A| ?/q|\!l=8R#NgFq_%^SS*:)GX\\Ͼҷɩ@á}YBh6$;aǀWr\4y޺4lOYњ Y$D' "e0Ka=B~pP`wF=}Jdrֈr!1^@A=Xzk6V=W-hY3ߒz3IfIo?dKz-ZlToVOO 蠷hkVZXsk[,q&eCL,p;JW4!(e[㍖©% e(**8Sz=KIe9U.VkkY6Si9-9Vs匫XaGJKLDžД V7)KmYXpIQIG9PVRX`8Sv]7:gwϲT EP=*Ye٭:T'J)Y}[9 o ksqNfwʛPV)ڗx[ҷn].} W}Jf~_Ҋ|#,uVemm ^Z)Ϡa:[a/,1X(iT`#n%(m-Q#6҅3*?o.M!L 1䆛9|Eߢoɟߢ™4'2 ͥAQ'.hRF@*Q )QX2ڜd5g p߳%ֳRJ7i]:'A';G٥C1K$lKKP [oNFFX6/$ V)t\ϣpfySF4h*# !<~u_gk:LCx<~8hk?g–~M - yy' % ቿ“~M \0.p#<_o(9N !<~./K5pt.eGd%Aay=$I78Kr!_','fq*|NG(79@x^t) !56eڨ8L60 17= 6RwP㘎;^i4R=)sKO3 :_S ō sy/DC?H w\?O%5xCM9U6г\& R{[B0Q[b^P3['?(M+^\9)LύnjUu׎}bsij@ o~?.nwد9/ѣ*k &O )-YIMyõn^lk/{LQ+ӗc Kˆ!>,c[nzoLwZAJ -ϊMx z+ T45}߾I5vxDvyUV8dkH)AC0.e>ٍЗss ECG%\?7ɜjo婽.牧L4^8wg qq X$Å~$IaųYDb} c.Q Ñ4*c'uzp  F0^jɈ.K=B {3Kh8ET#dArQ'p0.MMu/- ʶ6Kj/nWb&OqW=|~kW]y&FRU1~=O${F5}?8 N6( \V;}J[~sig-5_p7_Z4fGU /t&Â#Mx۔ {G7C#oX"~J!6_E*.t 2c3~e)z4:egW{ ?;{9[{c*e-oYxV}H|+fozPf/OI20RapxBxIѢNp?)((ybl{ӏޝ?zؘґ~l|0 if'&- )/=%.R#NSDȁӟ_8INyCMG/IsQE[1.8!A?z7ƫ{_̋:¿:cޞOWmx,|{}+|Aޥ:dZs;ߠw2s I!/S+FUsBakjݣ _݉umaNoL+^̏ [H^.K dhNB̝SoCߍRY/x+7: [P\F&/ 1o|`(|?;{$+]gHLV*,054 r8m讞g&-<)m8+iu{J93ڛ}@> ~ނ]EdIVޣS^TiӗA̦)9~E|Y2sPz/2YP;u u'Xt 3wTqej>8ŷ"«?~Fw|>eϹshI4.!Yn}.7Ov"[ǀ2!*׺zem`g>Sy Aj}wO(C&{ݟz_l>x\ڄ۳&7&vE'^A6a)am_${G(?X[gFL{h#s8i \ gl> LL5jƳ;їxqjv o/{bM=EO޹̏{H, Ǒs')C s0sm_N*c6P7XK)M/_I5bޏVbdނ̾h㴕dy=$Oa-GD"'4>Y#g^~zOGDKhT_ޮطؿ9jT^0hUÅww3;q{ To/WJ(M,$GjN'3LRBJv`^FɚT43\l^uUqS*=a-~؛ȓ} =Gy Ά2 AP3I>tt t> endobj 25 0 obj << /Type /FontDescriptor /FontName /QJTWWU+Helvetica-Bold /Flags 32 /FontBBox [-1018 -481 1436 1159] /ItalicAngle 0 /Ascent 770 /Descent -230 /CapHeight 720 /StemV 149 /XHeight 532 /StemH 124 /AvgWidth 479 /MaxWidth 1500 /FontFile2 26 0 R >> endobj 26 0 obj << /Length 27 0 R /Length1 14640 /Filter /FlateDecode >> stream x{ xTEhUݵetw:Y/ &A@P@ ( 83DAgp:((bqPDҷߩ!B}o޽9]UV:ulutɍmH ޺hR1u.- =4}'^没~E,t]`E6Bm ^ƃ!]tyl꫷ԅGǠ칮uap~]R*!]hI[_{0`nSzzC3qx#w24d#9J2̧νcc~S@/ >56Ѝ DlX 8j<_+нx;~')|37<F6gI7Kz#Yc>e~dl3\Ok 㱜ئVX~rpufv43Fݱ8wYtV, n/.ĥx<. -x `,_/#DErU{+BSeL:2b`ev2߳v:]v[؇$n7-vsqgs|2oDaEq*2'@s^$ \Hڢ*UV>Cj?2m㑳|3WN{B00ɞѝپwF$4ZLkc1Ya2l_yKRjea9*, DZjIƷr ,j"2V*|s;nT6ĆnT 9{7QAӡ^˵AZ;. h=`ZJJQR \f9&JL FLCuner+UN*}Ka, 7i{ ? ZI׿¸R\|RC稺36Tvb|wcn你`WCuey0Ar(h OsN(\`)6P٘dAa1?8ɥ+мzʣ(4ˮYo^S*766B~L}8P_̚5Ecg'sRٙIw] R߃M(#xxx7Q!)M_b 0=7"%K CC1C!e9 =$%\߈#+1Us%pͿ~1hµ~ czc)(< Oԏ7 9PxoD᩿~1s~ Kat^3@\0_uɛ=ɛ'X_ 7+$אW|f?W|<|G]FrĽ,v@:?@]v6n*z ` B'܅R ? *H!-4 gB2ZC}W36hWXjȻ` =V?!|z%I@퇹 hdX!AjMfRB2FNQû McϕG~,7DPyYBA >=yxx{d!x%Je;,w(fézSoydxt?/@AAi3jnwEE[8G!Sy uLoM0whC8Dv܁Shylbh ! F X20ʋx!⌝α' ?:`eh :rV^-xWDz;K;ƦsэR# L$\jU&TYX_f~˪I|;jY#%;G$[׬?i["&Ömwv5p߼D}Вx 2 *4|<@b4x[n7[&SǏg3Ǐc+>,Ǫ1+Lzp5ps¨v޾[[Rq~RY=%xhf)cl]  k3m7YOg .\S(Oа<ᰁN/mPaq`2tvv/dKA&x L I7L^;Kf6 W[u۱ c?ˇ iy7,'?qf4w͵ Z *>šȶ"7}`G");KpKW%~zz=nw0OQP|b U*Qc#:`&LVk 'jt5slV1j%sh(ƐS 85)k-a0{#K09HyL 4j  <y7=#"Ĝ*񄟇MP)v[ʵR4?U1!y odf_c_%[>qOaGFDѥfm+%M(Iqz{ȡB0*eg(PM%^f8#/p7 /}f JɛS?<7.H1yd;6t/nO燖C~aƸ`շ٪`oO 4ߴt-vYFG2L|`.ľDd`# (S.LV@2ы_IGW=R_b?S.G=CSbqq_~ۥ7ďş]lI̵Fk?Tf)}>3!ĎaW&Iǀ~*%>`pD?>9|h`*+S,MOQ c4Э\-}A^~5!;fW5w^O9܍+_뫯JS̏]e^wu#j3?$%pX8rrpE; )[&P>w F){隷 nW0/y#-jjkddDEQP3Le!IEfr*^%rU &jyȩ/_X)@ ACP%|6}%X&/^Ҍz^lg}n'rtF!3t]`2.%u.h3YVd&v.+ k'_& 14Y]Ͻp.A~]orۧ[fc G?~?qO@\8zL/&InSL{ismy's4 k+-8Zk}zn϶ 9 zdԤ:HkXGYFYvD*ϱ9h'nNwPq3 S@\^Zs o@ OZ [(`q )F +{n"dǣDlBc 8&<_^rJưU\v=uj:0|>`!k:{1, Ctޡ'*7 &ڮqhW0+vo7ȼ>Ͻɼɾƽ|9͞Nf:;O3cs9\b /h^2Ԝ4WQQ'7D& ,YFSM'AQ2za#cbX'T_$`X=69m'CO^ODQ[e`BQF Ðէ(/^tWT,YWQ̬'ps99*<[|53 wf HqEd } T/*|NKq _GWn.^έ`|O0 p,rtOz63 ;VSgqq"+ɅeQeb|oH44)[΀B AH㆒xf?Li`5!n!S. 1ף!E\e9/ K'_(nkе̢ '`0 $x:d^-lzsb⥂[L>M,ɗ+& izyv\m)jGU ; $S3C뛖"b2ZUZ=8qU'JF* ӴUY;xLi.;$ 7X^;ƃ*ʣћ7 i+5٭v7uw>}.%@=`b{  2 S'MS?;1 $I#V`HM~b ܾ蹺ޥ.tW8ձ`Xd4IU۞$<`kZ^aX_7age).Wk {~&PF)dG,;ݞoM|9EPK,t%U6,bi(6M E4f 7mSQRҠfOVaz1K *y ޶%\zi]{m8)+-xdb`oxu_2.w3Av)-ޚ>Uls'@A*RT/Q/-£dVaۖ/"G6|`g}X]=oLƮ`AcG=-%vXc8W-70s=ífll3k:[}d:?fO'91 빵A3>z);QccH*Sl_Ң]Q8}~ % ~89%^žb06gK( $2P^/ ZZ2kHƷPb>oYz7s]ڻWd&~kq.NObP\^ tҁG%Cg 8(??O0?#%O'ۑO&w mv&KQg; /[(2N$. ;J &^$~I ڢA%εmxl +akJwXpԌ)߽W}U{݌1g {F?Vh ^Sm('e1|Ƀث!P.8$ zb7!.X̹fC~+}lR`)S"Qm +kB[!Ō|("\FNtQ5n]t j(^QKq"m;"*֡:܄3xAk| `u_BI CJ0@GQ! .BXWm81%>"g4~J'SMwmt2jzTçk̃|t% ~HO{/H3g 8zoh^A`_˩paD{O=xoTùйKf3!Vo7/-eg%D'!΂U #̰n!pmo{^]$A>R4 :^|"J}(&Uٻ\pJ}3xl s*S bߏ,DcN=3 ` cCg+^Y>D`?[QF7KT>ͩcTWUT{+=5G19գU,6 lv&;;$SkPpjQr}Br^fݜ8b)qHʼn;Æ`_|5(jPC ZH?=KRtYrlo]]ϑC/xgqS&Wa6yBNܪ5]=FgȐ`^>aG=d,>ېf_ċ$x;`is*~`.o ]NDΕjó"z73҈wqU!X`n;SogF 搇(ǾbuReJmS6w j:5KW.v8~Qit_BSD>P|x f5(j-w|F< [9oR4LV\疗Ƚ@;8=e~YCe_a\ xXkzN(S/$0^ ok>jq`cLIT4 Xo0Ok0fiSj-75> endobj 29 0 obj << /Length 30 0 R /Filter /FlateDecode >> stream x]PAj0{LA(!()}-,4vvwFu6^w`D\5€uL`NwVzz$%ܺу dIq݋>[4`uJ[C])GHM۞T[@DH\B1nB&JY1tp }J4Uk3mG qe2{<5F^V~ZAy| endstream endobj 30 0 obj 249 endobj 28 0 obj << /Type /FontDescriptor /FontName /OKFHOT+LucidaGrande /Flags 4 /FontBBox [-1067 -737 1641 1162] /ItalicAngle 0 /Ascent 967 /Descent -211 /CapHeight 723 /StemV 103 /XHeight 530 /StemH 77 /AvgWidth -490 /MaxWidth 1640 /FontFile2 31 0 R >> endobj 31 0 obj << /Length 32 0 R /Length1 9568 /Filter /FlateDecode >> stream xZ xSǕ>3#/ɒ_`[k0$2܀C" Da "KH~Rei{-fKinSM ۤ|`?^_{393gΜ93smo!К掍_3>nag ;;;b wlm~54 9}  [veL [w(+[gVtwv7vlk10 vn \4uQ2]ncǒ?0_|NE^n'k9Yz})ތFB;.9<)5IwIJͅG܏v*_ z0G KI>e}qv/.g7՞n=6s'MO7[jػ'c'-H6#s$[wnVڵhllCZ~ɖ3-ҒI{J/xI 1b`BĄwԂLaygff53\bfr̙D2ժB ׮En-r9Fr Qj$r M2^.<')I@$Ivjv'~SbL|0m;[SȁS`RĎ#GM<&>>>ƐPdԁG G,jJ<"1 t Kj Vby:Z} }~[Z{ ~ ılQ{M=Mx_8&';qlxN ^ʐp^Xt^q5j B *QUC(b"z&!~~ʔ]_%&W!b!'fC~n`Ȃ2^KaPLu)O=>?xXwʡP]P69= hZi4k=1XϬK̸JϬ[2ueH'4)ϡyj"HowՀ#χhh{O犜nJ¢>{4rqR>ӤcO'fz$;Ֆ_7fM|+xL3*KBWu̶ qRS]$ D|XƷ:ľF_ D@ $ 4+=n~n{^ dsAv6R u׻諠!9;AM ںaStK`Grl F~ l~`4`/B~ 8 Eаl }mehֳ uvj l7{;jp74u:FFP3h!'f!`WQ.TŬ|V3MFje6 X2 Be$tڢk.rVe/+UE4h܊Yjn͝uN+UʜNW=+;nȴ OI J$ lbJYs)bHͦ:3JEɎ|Ka7Wgg4_/,/U3-h jV6&R[6jY[=W}IFM/ukn 'YOqk"hO KkJ_EM~ΖD"3!iIDQgjD[ 3OPp.Q_٥eY̠6;جU٪J(6lN2#LW,v2I/{Xh)7ɪ]lT6A Lz]a :TC+NC@WiTS+:bئN٪SeuawDUb͋ I705J`IO nO0ۀ65>z ߀y pWr~7` Pk 7 h@alY2C+4j+A[R/ ?@Z%dSǖPy:52FjYD2ΏlOy%/K/Q"۞"lRާ!jTmꋩw&T{OW4 qz"^&/!whu2 |sOu1tZZT鴏7ǡ[5ې[@s=CIb<'e$@aM1{gF?'Rɳ굣ul:qZE"ưcc|GiݙdDk:Y@)o3bE;Ѓ8L!nnHvz gfT Mz6Z,A_k^FsU !c\^i,͵++u]:ǎ"^nϗũ~B|l_F_{0i?׾OkFzѸF1j70#f)ZZzT>"Z H-L>G|7,]Fs7t-%qk}a^ZK-c8 7NQz1q^r"7GVm~(Xu }5̟W5wN:Y3gx{iSKK'O**,s9s "p?_ɊppVRoaץh]^QS"sZUըQ+=j"M_&MAn%Y.ki+gQyjҺx9K!wY\!!9K5 %%Sj<+ڴty+jx7^RWQa?Qn Uָ܅4Vhh94 |>6y6x4w z_Ϋv{<ѸYɮֵdcisɑ LKG?/bzۃ qǝAI(2z _)"43*9 M 4ktHjycͷ5 I[)5`PU]'r`GY֍"֣X~%*ÄEeՅz89Cֳ͊~|ot{.AK:%ŢP6C= *o;%Ɇ~ׄ iv(JT0Jc^zܬ 5VM:P>1\1YfH,-D䠌 2ZzTQ.!Sٸ2щ.|bqKY1ī.H'I*o # Zdm20fI} بJYj'pE= /,ycGGn$IR BxQ3Bȑ3pT~\K8IYgl–36T.D/TU#wfU] =yF9.8 {b/9{~xp闳i_%PkXlHX bȹZ 1V?&ʄxHhc: +&]W]R];?ٷV|0sagd> endobj xref 0 41 0000000000 65535 f 0000048886 00000 n 0000007390 00000 n 0000010103 00000 n 0000000022 00000 n 0000007370 00000 n 0000007494 00000 n 0000008859 00000 n 0000032033 00000 n 0000010236 00000 n 0000010066 00000 n 0000000000 00000 n 0000042495 00000 n 0000025322 00000 n 0000007646 00000 n 0000008838 00000 n 0000008895 00000 n 0000010045 00000 n 0000010186 00000 n 0000010925 00000 n 0000011176 00000 n 0000025300 00000 n 0000025742 00000 n 0000026002 00000 n 0000032012 00000 n 0000032471 00000 n 0000032729 00000 n 0000042474 00000 n 0000043021 00000 n 0000042676 00000 n 0000043001 00000 n 0000043276 00000 n 0000048618 00000 n 0000048639 00000 n 0000048673 00000 n 0000048725 00000 n 0000048759 00000 n 0000048778 00000 n 0000048802 00000 n 0000048844 00000 n 0000048863 00000 n trailer << /Size 41 /Root 18 0 R /Info 1 0 R /ID [ ] >> startxref 49061 %%EOF itksnap-3.4.0/GUI/000077500000000000000000000000001263013355200136135ustar00rootroot00000000000000itksnap-3.4.0/GUI/Model/000077500000000000000000000000001263013355200146535ustar00rootroot00000000000000itksnap-3.4.0/GUI/Model/AbstractLayerAssociatedModel.h000066400000000000000000000201011263013355200225370ustar00rootroot00000000000000#ifndef ABSTRACTLAYERASSOCIATEDMODEL_H #define ABSTRACTLAYERASSOCIATEDMODEL_H #include "AbstractModel.h" #include "LayerAssociation.h" #include "GlobalUIModel.h" #include "IRISException.h" #include "IRISApplication.h" #include "SNAPEventListenerCallbacks.h" #include "ImageWrapperBase.h" #include "PropertyModel.h" /** This is an abstract class for a special type of UI model that can be associated with different image layers in SNAP. Examples of these models are models for contrast adjustment, colormap adjustment, etc., i.e., models that link the GUI with one image layer. Two options were available: to associate a single such model with each image layer, or to create just one model, and allow the layer for that model to be switched. I chose the second option because it reduced the number of models that have to be kept around. However, the model has to keep track of layer-specific properties, and also has to be smart about registering and unregistering for events originating from the layers. This class is templated over a properties object, which contains some layer-specific properties that the model needs to store. For example, it can be the number of histogram bins to display for the contrast dialog. The second parameter is the type of the image wrapper that can participate in the association. It can be ImageWrapperBase or one of its subclasses. */ template class AbstractLayerAssociatedModel : public AbstractModel { public: typedef AbstractLayerAssociatedModel Self; typedef AbstractModel Superclass; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkTypeMacro(AbstractLayerAssociatedModel, AbstractModel) // An event fired when the selected layer changes itkEventMacro(ActiveLayerChangedEvent, ModelUpdateEvent) itkEventMacro(LayerStructureChangedEvent, ModelUpdateEvent) FIRES(LayerStructureChangedEvent) FIRES(ActiveLayerChangedEvent) irisGetMacro(ParentModel, GlobalUIModel *) void SetParentModel(GlobalUIModel *parent) { // Store the parent model m_ParentModel = parent; // Associate the layers with properties. m_LayerProperties.SetSource(m_ParentModel->GetDriver()); // Layer changes in the parent are rebroadcast as model updates Rebroadcast(m_ParentModel, LayerChangeEvent(), LayerStructureChangedEvent()); // Set active layer to NULL this->SetLayer(NULL); } /** Set the layer with which the model is associated. This can be NULL, in which case, the model will be dissasociated from all layers. */ void SetLayer(TWrapper *layer) { // There is nothing to do if the layer is already set if(layer && m_Layer == layer) return; // Make sure the layer-specific stuff is up to date m_LayerProperties.Update(); // Unregister from the current layer if(m_LayerProperties.HasLayer(m_Layer)) { // Remove the deletion listener m_Layer->RemoveObserver(m_DeleteEventObserverTag); // Call the child's unregister method this->UnRegisterFromLayer(m_Layer, false); } // Set the layer m_Layer = layer; // Handle events. Need to be careful here, because layers are dynamically // changing, and we don't want to add more than one observer to any layer. // Note that we don't remove the observer from the old layer because when // this method is called, the old layer may have already been destroyed! if(m_Layer) { // Listen for delete events from the layer m_DeleteEventObserverTag = AddListener(m_Layer, itk::DeleteEvent(), this, &Self::LayerDeletionCallback); #ifdef SNAP_DEBUG_EVENTS if(flag_snap_debug_events) { std::cout << "DeleteEvent registration " << " layer " << m_Layer << " id " << m_Layer->GetUniqueId() << " observer " << this << std::endl << std::flush; } #endif // Do whatever needs to be done to listen to layer events this->RegisterWithLayer(m_Layer); } // Fire an event to indicate the change InvokeEvent(ActiveLayerChangedEvent()); } /** Get the layer associated with the model, or NULL if there is none */ irisGetMacro(Layer, TWrapper *) /** Get the properties associated with the current layer */ TProperties &GetProperties() { assert(m_Layer); TProperties *p = m_LayerProperties[m_Layer]; return *p; } /** This method should be implemented by the child class. It registers the child class to rebroadcast whatever events it needs from the layer with which it has been associated to the downstream objects. */ virtual void RegisterWithLayer(TWrapper *layer) = 0; /** This method should be implemented by the child class. It disconnects the chold class from the associated layer (just before breaking the association). For the Register/Unregister pair to work, the Register method implementation should retain the tag returned by the call to the Rebroadcast method. This tag can be placed in the layer-associated properties, and then used during the call to UnRegister. The second parameter to this method specifies whether the method is called in response to the layer being deleted or not. If they layer is being deleted, we are unsure about the state of the layer and we don't need to remove observers from it. . */ virtual void UnRegisterFromLayer(TWrapper *layer, bool being_deleted) = 0; /** The model has its own OnUpdate implementation, which handles changes in the layer structure. If the event bucket has a LayerChangeEvent, the model will automatically rebuild it's layer associations, and may reset the current layer to NULL if the current layer has been removed. If child models reimplement OnUpdate(), they must call AbstractLayerAssociatedModel::OnUpdate() within the reimplemented method. */ virtual void OnUpdate() { if(m_EventBucket->HasEvent(LayerChangeEvent())) { // If the layers have changed, we need to update the layer properties // object. Then we need to see if the current layer has actually been // destroyed m_LayerProperties.Update(); // Was the current layer removed? if(!m_LayerProperties.HasLayer(m_Layer)) this->SetLayer(NULL); } } /** * A boolean property model indicating whether the model is holding a * layer or not. This can be used to toggle parts of the user interface */ irisGetMacro(HasLayerModel, AbstractSimpleBooleanProperty * ) protected: AbstractLayerAssociatedModel() { // Set up the factory PropertiesFactory factory; factory.m_Model = this; m_LayerProperties.SetDelegate(factory); m_ParentModel = NULL; m_Layer = NULL; m_HasLayerModel = wrapGetterSetterPairAsProperty( this, &Self::GetHasLayerValue); } virtual ~AbstractLayerAssociatedModel() {} /** Create a property object for a new layer */ TProperties *CreateProperty(TWrapper *w) { return new TProperties(); } void LayerDeletionCallback() { // Unregister from the current layer this->UnRegisterFromLayer(m_Layer, true); // Set the layer to NULL m_Layer = NULL; // Fire an event to indicate the change InvokeEvent(ActiveLayerChangedEvent()); } // Parent model GlobalUIModel *m_ParentModel; // Currently associated layer TWrapper *m_Layer; // Tag used to manage deletion observers on the current layer unsigned long m_DeleteEventObserverTag; // A factory class for creating properties class PropertiesFactory { public: TProperties *New(TWrapper *w) { return m_Model->CreateProperty(w); } Self *m_Model; }; // Model as to whether the layer is set SmartPtr m_HasLayerModel; bool GetHasLayerValue() { return this->m_Layer != NULL; } // Association between a layer and a set of properties typedef LayerAssociation< TProperties,TWrapper,PropertiesFactory> LayerMapType; LayerMapType m_LayerProperties; }; #endif // ABSTRACTLAYERASSOCIATEDMODEL_H itksnap-3.4.0/GUI/Model/AbstractLayerInfoItemSetDomain.h000066400000000000000000000036441263013355200230320ustar00rootroot00000000000000#ifndef ABSTRACTLAYERINFOITEMSETDOMAIN_H #define ABSTRACTLAYERINFOITEMSETDOMAIN_H #include #include /** This is a base class for domain descriptions that provide some piece of information about every ITK-SNAP layer. This is used in conjunction with the PropertyModel system to provide the GUI with tables and combos that list layers, along with specific properties of interest. */ template class AbstractLayerInfoItemSetDomain : public AbstractItemSetDomain { public: // To avoid storing pointers, we cast them into size_t objects. Otherwise // this screws up some of the Qt stuff typedef size_t ValueType; /** Initializes domain that iterates through layers based on the filter */ AbstractLayerInfoItemSetDomain(IRISApplication *app = NULL, int role_filter = ALL_ROLES) { m_Driver = app; m_RoleFilter = role_filter; } LayerIterator begin() const { return m_Driver->GetCurrentImageData()->GetLayers(m_RoleFilter); } LayerIterator end() const { return m_Driver->GetCurrentImageData()->GetLayers(m_RoleFilter).MoveToEnd(); } LayerIterator find(const ValueType &value) const { return m_Driver->GetCurrentImageData()->GetLayers(m_RoleFilter).Find( reinterpret_cast(value)); } ValueType GetValue(const LayerIterator &it) const { return reinterpret_cast(it.GetLayer()); } bool operator == (const AbstractLayerInfoItemSetDomain &cmp) const { return m_Driver == cmp.m_Driver && m_RoleFilter == cmp.m_RoleFilter; } bool operator != (const AbstractLayerInfoItemSetDomain &cmp) const { return m_Driver != cmp.m_Driver || m_RoleFilter != cmp.m_RoleFilter; } virtual bool isAtomic() { return false; } protected: IRISApplication *m_Driver; int m_RoleFilter; }; #endif // ABSTRACTLAYERINFOITEMSETDOMAIN_H itksnap-3.4.0/GUI/Model/AnnotationModel.cxx000066400000000000000000000577611263013355200205120ustar00rootroot00000000000000#include "AnnotationModel.h" #include "GenericSliceModel.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "ImageAnnotationData.h" #include "GlobalUIModel.h" #include void AnnotationModel::SetParent(GenericSliceModel *model) { m_Parent = model; Rebroadcast(m_Parent->GetDriver()->GetGlobalState()->GetAnnotationModeModel(), ValueChangedEvent(), ModelUpdateEvent()); } double AnnotationModel::GetLineLength(const Vector3f &xSliceA, const Vector3f &xSliceB) { Vector2f pt1InAna = m_Parent->MapSliceToPhysicalWindow(xSliceA); Vector2f pt2InAna = m_Parent->MapSliceToPhysicalWindow(xSliceB); double length = (pt1InAna[0] - pt2InAna[0]) * (pt1InAna[0] - pt2InAna[0]) + (pt1InAna[1] - pt2InAna[1]) * (pt1InAna[1] - pt2InAna[1]); length = sqrt(length); return length; } double AnnotationModel::GetCurrentLineLength() { return GetLineLength(m_CurrentLine.first, m_CurrentLine.second); } double AnnotationModel::GetCurrentLineLengthInPixels() { Vector2f screen_offset = m_Parent->MapSliceToWindow(m_CurrentLine.first) - m_Parent->MapSliceToWindow(m_CurrentLine.second); return screen_offset.magnitude() / m_Parent->GetSizeReporter()->GetViewportPixelRatio(); } double AnnotationModel::GetAngleWithCurrentLine(const annot::LineSegmentAnnotation *lsa) { // Process the current line Vector2f v1 = m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.first) - m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.second); v1 /= sqrt(v1[0]*v1[0] + v1[1]*v1[1]); // Process the annotation Vector2f v2 = m_Parent->MapSliceToPhysicalWindow( m_Parent->MapImageToSlice(lsa->GetSegment().first)) - m_Parent->MapSliceToPhysicalWindow( m_Parent->MapImageToSlice(lsa->GetSegment().second)); v2 /= sqrt(v2[0]*v2[0] + v2[1]*v2[1]); // Compute the dot product and no need for the third components that are zeros double angle = 180.0 * acos(fabs(v1[0]*v2[0]+v1[1]*v2[1])) / vnl_math::pi; return angle; } void AnnotationModel::AdjustAngleToRoundDegree(LineSegment &line, int n_degrees) { ImageAnnotationData *adata = this->GetAnnotations(); // Map the line segment from slice coordinates to window physical, where angles are // computed Vector2f p1 = m_Parent->MapSliceToPhysicalWindow(line.first); Vector2f p2 = m_Parent->MapSliceToPhysicalWindow(line.second); // Express the vector of the line in polar coordinates double p_rad = (p2 - p1).magnitude(); double p_phase = atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180.0 / vnl_math::pi; // Current proposed adjustment Vector2f p2_rot_best = p2; double rot_best = std::numeric_limits::infinity(); // Loop over all the lines for(ImageAnnotationData::AnnotationConstIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ++it) { const annot::LineSegmentAnnotation *lsa = dynamic_cast(it->GetPointer()); if(lsa && this->IsAnnotationVisible(lsa)) { // Normalize the annotated line Vector2f q1 = m_Parent->MapSliceToPhysicalWindow( m_Parent->MapImageToSlice(lsa->GetSegment().first)); Vector2f q2 = m_Parent->MapSliceToPhysicalWindow( m_Parent->MapImageToSlice(lsa->GetSegment().second)); // Get the phase of the line double q_phase = atan2(q2[1] - q1[1], q2[0] - q1[0]) * 180.0 / vnl_math::pi; // Compute the updated phase - now the difference in phase is fractional double p_phase_round = q_phase + floor((p_phase - q_phase) / n_degrees + 0.5) * n_degrees; double p_phase_shift = fabs(p_phase_round - p_phase); // Map the rounded phase to the new p2 position Vector2f p2_prop( p1[0] + p_rad * cos(p_phase_round * vnl_math::pi / 180.0), p1[1] + p_rad * sin(p_phase_round * vnl_math::pi / 180.0)); // Compare to current best if(p_phase_shift < rot_best) { rot_best = p_phase_shift; p2_rot_best = p2_prop; } } } // Map back line.second = m_Parent->MapPhysicalWindowToSlice(p2_rot_best); } Vector3f AnnotationModel::GetAnnotationCenter(const AbstractAnnotation *annot) { const annot::LineSegmentAnnotation *lsa = dynamic_cast(annot); if(lsa) { return (m_Parent->MapImageToSlice(lsa->GetSegment().first) + m_Parent->MapImageToSlice(lsa->GetSegment().second)) * 0.5f; } return Vector3f(0.f); } double AnnotationModel::GetDistanceToLine(const Vector3f &x1, const Vector3f &x2, const Vector3d &point) { Vector2f p0 = m_Parent->MapSliceToWindow(x1); Vector2f p1 = m_Parent->MapSliceToWindow(x2); Vector2f x = m_Parent->MapSliceToWindow(to_float(point)); float alpha = dot_product(x - p0, p1 - p0) / dot_product(p1 - p0, p1 - p0); if(alpha < 0) alpha = 0; if(alpha > 1) alpha = 1; Vector2f px = p0 * (1.0f - alpha) + p1 * alpha; return (px - x).magnitude(); } double AnnotationModel::GetDistanceToLine(LineSegment &line, const Vector3d &point) { return GetDistanceToLine(line.first, line.second, point); } AnnotationMode AnnotationModel::GetAnnotationMode() const { return m_Parent->GetDriver()->GetGlobalState()->GetAnnotationMode(); } ImageAnnotationData *AnnotationModel::GetAnnotations() { return m_Parent->GetDriver()->GetCurrentImageData()->GetAnnotations(); } bool AnnotationModel::IsAnnotationVisible(const AnnotationModel::AbstractAnnotation *annot) { return annot->IsVisible( m_Parent->GetSliceDirectionInImageSpace(), m_Parent->GetSliceIndex()); } double AnnotationModel ::GetPixelDistanceToAnnotation( const AbstractAnnotation *annot, const Vector3d &point) { const annot::LineSegmentAnnotation *lsa = dynamic_cast(annot); if(lsa) { const annot::LineSegment &seg = lsa->GetSegment(); Vector3f s1 = m_Parent->MapImageToSlice(seg.first); Vector3f s2 = m_Parent->MapImageToSlice(seg.second); return GetDistanceToLine(s1, s2, point); } const annot::LandmarkAnnotation *lma = dynamic_cast(annot); if(lma) { const annot::Landmark &landmark = lma->GetLandmark(); Vector3f s1, s2; this->GetLandmarkArrowPoints(landmark, s1, s2); return GetDistanceToLine(s1, s2, point); } return std::numeric_limits::infinity(); } AnnotationModel::AbstractAnnotation * AnnotationModel::GetAnnotationUnderCursor(const Vector3d &xSlice) { ImageAnnotationData *adata = this->GetAnnotations(); // Current best annotation AbstractAnnotation *asel = NULL; double dist_min = std::numeric_limits::infinity(); double dist_thresh = 5 * m_Parent->GetSizeReporter()->GetViewportPixelRatio(); // Loop over all annotations for(ImageAnnotationData::AnnotationConstIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ++it) { AbstractAnnotation *a = *it; // Test if annotation is visible in this plane if(this->IsAnnotationVisible(a)) { double dist = GetPixelDistanceToAnnotation(a, xSlice); if(dist < dist_thresh && dist < dist_min) { asel = a; dist_min = dist; } } } return asel; } bool AnnotationModel::ProcessPushEvent(const Vector3d &xSlice, bool shift_mod) { // Get the annotation data ImageAnnotationData *adata = this->GetAnnotations(); bool handled = false; if(this->GetAnnotationMode() == ANNOTATION_RULER || this->GetAnnotationMode() == ANNOTATION_LANDMARK) { if(m_FlagDrawingLine) { // Complete drawing line m_CurrentLine.second = to_float(xSlice); handled = true; } else { m_CurrentLine.first = to_float(xSlice); m_CurrentLine.second = to_float(xSlice); m_FlagDrawingLine = true; handled = true; } } else if(this->GetAnnotationMode() == ANNOTATION_SELECT) { // Check if for any of the selected annotations, the click is close to the drag handle int handle_idx = -1; AbstractAnnotation *asel = this->GetSelectedHandleUnderCusror(xSlice, handle_idx); // Find closest annotation under the cursor if(!asel) asel = this->GetAnnotationUnderCursor(xSlice); // If the shift modifier is on, we add to the selection if(shift_mod) { // Add to the selection if(asel) asel->SetSelected(!asel->GetSelected()); } // If not clicked on a selected handle, process as a select operation else if(handle_idx < 0) { // Clear the selection for(ImageAnnotationData::AnnotationConstIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ++it) (*it)->SetSelected(false); // Select the clicked if(asel) asel->SetSelected(!asel->GetSelected()); } // Store the position of the drag-start if(asel) { m_MovingSelection = true; m_DragStart = to_float(xSlice); m_DragLast = to_float(xSlice); m_MovingSelectionHandle = handle_idx; m_MovingSelectionHandleAnnot = asel; } else { m_MovingSelection = false; } // We always consider the click as handled because otherwise it can be really annoying // for the user if they miss an annotation and that results in the cross-hairs being // moved to another location handled = true; } if(handled) InvokeEvent(ModelUpdateEvent()); return handled; } bool AnnotationModel::ProcessMoveEvent(const Vector3d &xSlice, bool shift_mod, bool drag) { ImageAnnotationData *adata = this->GetAnnotations(); bool handled = false; if(this->GetAnnotationMode() == ANNOTATION_RULER || this->GetAnnotationMode() == ANNOTATION_LANDMARK) { if(m_FlagDrawingLine) { // Accept the second point m_CurrentLine.second = to_float(xSlice); // If shift pressed, adjust the line to have a integral angle with one of existing // lines if(this->GetAnnotationMode() == ANNOTATION_RULER && shift_mod) { this->AdjustAngleToRoundDegree(m_CurrentLine, 5); } handled = true; } } else if(this->GetAnnotationMode() == ANNOTATION_SELECT && m_MovingSelection && drag) { // Compute the amount to move the annotation by Vector3f p_last = m_Parent->MapSliceToImage(m_DragLast); Vector3f p_now = m_Parent->MapSliceToImage(to_float(xSlice)); Vector3f p_delta = p_now - p_last; // Process the move command on selected annotations for(ImageAnnotationData::AnnotationIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ++it) { AbstractAnnotation *a = *it; // Test if annotation is visible in this plane if(m_MovingSelectionHandle < 0 && a->GetSelected() && this->IsAnnotationVisible(a)) { // Move the annotation by this amount a->MoveBy(p_delta); } else if(m_MovingSelectionHandle >= 0 && m_MovingSelectionHandleAnnot == a) { // Move the annotation handle by this amount this->MoveAnnotationHandle(a, m_MovingSelectionHandle, p_delta); } } // Store the update point m_DragLast = to_float(xSlice); // Event has been handled handled = true; } if(handled) this->InvokeEvent(ModelUpdateEvent()); return handled; } bool AnnotationModel::ProcessReleaseEvent(const Vector3d &xSlice, bool shift_mod) { // Handle as a drag event bool handled = this->ProcessMoveEvent(xSlice, shift_mod, true); // If drawing line, complete the line drawing, as long as the line is long enough if(m_FlagDrawingLine) { // If line is longer than a threshold of 5 pixel units, mark it as completed if(this->GetCurrentLineLengthInPixels() > 5) m_FlagDrawingLine = false; } return handled; } bool AnnotationModel::IsDrawingRuler() { return this->GetAnnotationMode() == ANNOTATION_RULER && m_FlagDrawingLine; } void AnnotationModel::AcceptLine() { // Get the length of the line in logical (non-retina) pixels // Check that the length of the segment is at least 5 screen pixels if(this->GetAnnotationMode() == ANNOTATION_RULER) { // Create the line in image space annot::LineSegment ls = std::make_pair( m_Parent->MapSliceToImage(m_CurrentLine.first), m_Parent->MapSliceToImage(m_CurrentLine.second)); // Add the line SmartPtr lsa = annot::LineSegmentAnnotation::New(); lsa->SetSegment(ls); lsa->SetPlane(m_Parent->GetSliceDirectionInImageSpace()); lsa->SetVisibleInAllPlanes(false); lsa->SetVisibleInAllSlices(false); lsa->SetColor(m_Parent->GetParentUI()->GetGlobalState()->GetAnnotationColor()); lsa->SetSelected(false); this->GetAnnotations()->AddAnnotation(lsa); m_FlagDrawingLine = false; this->InvokeEvent(ModelUpdateEvent()); } else if(this->GetAnnotationMode() == ANNOTATION_LANDMARK) { // Create the line in image space annot::Landmark lm; lm.Text = this->GetCurrentAnnotationText(); lm.Pos = m_Parent->MapSliceToImage(m_CurrentLine.first); // Set the default offset. The default offset corresponds to 5 screen pixels // to the top right. We need to map this into image units Vector2f xHeadWin = m_Parent->MapSliceToWindow(m_CurrentLine.first); Vector2f xTailWin = m_Parent->MapSliceToWindow(m_CurrentLine.second); Vector2f xHeadWinPhys = m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.first); Vector2f xTailWinPhys = m_Parent->MapSliceToPhysicalWindow(m_Parent->MapWindowToSlice(xTailWin)); lm.Offset = xTailWinPhys - xHeadWinPhys; // Add the line SmartPtr lma = annot::LandmarkAnnotation::New(); lma->SetLandmark(lm); lma->SetPlane(m_Parent->GetSliceDirectionInImageSpace()); lma->SetVisibleInAllPlanes(false); lma->SetVisibleInAllSlices(false); lma->SetColor(m_Parent->GetParentUI()->GetGlobalState()->GetAnnotationColor()); lma->SetSelected(false); this->GetAnnotations()->AddAnnotation(lma); this->InvokeEvent(ModelUpdateEvent()); } } void AnnotationModel::CancelLine() { m_FlagDrawingLine = false; this->InvokeEvent(ModelUpdateEvent()); } void AnnotationModel::SelectAllOnSlice() { ImageAnnotationData *adata = this->GetAnnotations(); for(ImageAnnotationData::AnnotationIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ++it) { AbstractAnnotation *a = *it; // Test if annotation is visible in this plane if(this->IsAnnotationVisible(a)) { a->SetSelected(true); } } this->InvokeEvent(ModelUpdateEvent()); } void AnnotationModel::DeleteSelectedOnSlice() { ImageAnnotationData *adata = this->GetAnnotations(); for(ImageAnnotationData::AnnotationIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ) { AbstractAnnotation *a = *it; // Test if annotation is visible in this plane if(a->GetSelected() && this->IsAnnotationVisible(a)) { it = adata->GetAnnotations().erase(it); } else ++it; } this->InvokeEvent(ModelUpdateEvent()); } void AnnotationModel::GoToNextAnnotation() { this->GoToNextOrPrevAnnotation(1); } void AnnotationModel::GoToPreviousAnnotation() { this->GoToNextOrPrevAnnotation(-1); } void AnnotationModel::GoToNextOrPrevAnnotation(int direction) { // Create a list of all annotations in this slice view, sorted typedef std::pair AnnotPair; std::list annot_list; typedef std::list::iterator AnnotIter; typedef std::list::reverse_iterator AnnotRevIter; // Find annotation that will serve as a reference point AbstractAnnotation *ref_annot = NULL; AbstractAnnotation *selected = NULL; // Iterate through the annotations ImageAnnotationData *adata = this->GetAnnotations(); for(ImageAnnotationData::AnnotationIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ++it) { AbstractAnnotation *a = *it; if(a->IsVisible(m_Parent->GetSliceDirectionInImageSpace())) { // Create a pair for the current annotation Vector3f ank_img = a->GetAnchorPoint(m_Parent->GetSliceDirectionInImageSpace()); Vector3f ank_slice = m_Parent->MapImageToSlice(ank_img); long hash = ank_slice[2] * 100000000l + ank_slice[1] * 10000l + ank_slice[0]; AnnotPair pair = std::make_pair(hash, a); annot_list.push_back(pair); // If the annotation is on this slice and selected, us as a reference if(this->IsAnnotationVisible(a) && a->GetSelected()) ref_annot = a; } } // Test for degenerate cases if(annot_list.size() == 1) { selected = annot_list.front().second; } else if(annot_list.size() > 1) { // Sort the annotations by slice, then by in-slice position annot_list.sort(); // Find the reference annotation if it exists if(ref_annot && direction > 0) { AnnotIter it_ref = annot_list.end(); for(AnnotIter it = annot_list.begin(); it!=annot_list.end(); ++it) if(it->second == ref_annot) it_ref = it; ++it_ref; if(it_ref == annot_list.end()) it_ref = annot_list.begin(); selected = it_ref->second; } else if(ref_annot && direction < 0) { AnnotRevIter it_ref = annot_list.rend(); for(AnnotRevIter it = annot_list.rbegin(); it!=annot_list.rend(); ++it) if(it->second == ref_annot) it_ref = it; ++it_ref; if(it_ref == annot_list.rend()) it_ref = annot_list.rbegin(); selected = it_ref->second; } else if(direction > 0) { // Start from the beginning and find something on the current slice or after it for(AnnotIter it = annot_list.begin(); it!=annot_list.end(); ++it) if(it->second->GetSliceIndex(m_Parent->GetSliceDirectionInImageSpace()) >= m_Parent->GetSliceIndex()) { selected = it->second; break; } if(!selected) selected = annot_list.front().second; } else { // Start from the beginning and find something on the current slice or after it for(AnnotRevIter it = annot_list.rbegin(); it!=annot_list.rend(); --it) if(it->second->GetSliceIndex(m_Parent->GetSliceDirectionInImageSpace()) <= m_Parent->GetSliceIndex()) { selected = it->second; break; } if(!selected) selected = annot_list.back().second; } } // Deselect everything for(ImageAnnotationData::AnnotationIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ++it) { (*it)->SetSelected(false); } // Select the clicked if(selected) { // Select the next item selected->SetSelected(true); // Go to its plane Vector3ui cursor = m_Parent->GetDriver()->GetCursorPosition(); cursor[m_Parent->GetSliceDirectionInImageSpace()] = selected->GetSliceIndex(m_Parent->GetSliceDirectionInImageSpace()); m_Parent->GetDriver()->SetCursorPosition(cursor); } // Fire event this->InvokeEvent(ModelUpdateEvent()); } bool AnnotationModel::TestPointInClickRadius(const Vector3f &xClickSlice, const Vector3f &xPointSlice, int logical_pixels) { Vector2f clickW = m_Parent->MapSliceToWindow(xClickSlice); Vector2f pointW = m_Parent->MapSliceToWindow(xPointSlice); int vppr = m_Parent->GetSizeReporter()->GetViewportPixelRatio(); return fabs(clickW[0] - pointW[0]) <= logical_pixels * vppr && fabs(clickW[1] - pointW[1]) <= logical_pixels * vppr; } void AnnotationModel::MoveAnnotationHandle( AnnotationModel::AbstractAnnotation *ann, int handle, const Vector3f &deltaPhys) { // Draw all the line segments annot::LineSegmentAnnotation *lsa = dynamic_cast(ann); if(lsa) { annot::LineSegment ls = lsa->GetSegment(); if(handle == 0) ls.first += deltaPhys; else if(handle == 1) ls.second += deltaPhys; lsa->SetSegment(ls); } // Draw all the line segments annot::LandmarkAnnotation *lma = dynamic_cast(ann); if(lma) { annot::Landmark lm = lma->GetLandmark(); if(handle == 0) { lm.Pos += deltaPhys; } else if(handle == 1) { Vector3f headXSlice = m_Parent->MapImageToSlice(lm.Pos); Vector3f tailXSlice = m_Parent->MapPhysicalWindowToSlice( m_Parent->MapSliceToPhysicalWindow(headXSlice) + lm.Offset); Vector3f tailXImage = m_Parent->MapSliceToImage(tailXSlice); Vector3f tailXImageMoved = tailXImage + deltaPhys; Vector3f tailXSliceMoved = m_Parent->MapImageToSlice(tailXImageMoved); Vector2f tailXPhysWinMoved = m_Parent->MapSliceToPhysicalWindow(tailXSliceMoved); lm.Offset = tailXPhysWinMoved - m_Parent->MapSliceToPhysicalWindow(headXSlice); } lma->SetLandmark(lm); } } annot::AbstractAnnotation * AnnotationModel::GetSelectedHandleUnderCusror(const Vector3d &xSlice, int &out_handle) { // Get the annotation data ImageAnnotationData *adata = this->GetAnnotations(); out_handle = -1; for(ImageAnnotationData::AnnotationConstIterator it = adata->GetAnnotations().begin(); it != adata->GetAnnotations().end(); ++it) { if(this->IsAnnotationVisible(*it) && (*it)->GetSelected()) { // Draw all the line segments annot::LineSegmentAnnotation *lsa = dynamic_cast(it->GetPointer()); if(lsa) { // Draw the line Vector3f p1 = m_Parent->MapImageToSlice(lsa->GetSegment().first); Vector3f p2 = m_Parent->MapImageToSlice(lsa->GetSegment().second); // Test if either of these points is close to the cursor if(this->TestPointInClickRadius(to_float(xSlice), p1, 5)) out_handle = 0; else if(this->TestPointInClickRadius(to_float(xSlice), p2, 5)) out_handle = 1; } annot::LandmarkAnnotation *lma = dynamic_cast(it->GetPointer()); if(lma) { Vector3f xHeadSlice, xTailSlice; annot::Landmark lm = lma->GetLandmark(); this->GetLandmarkArrowPoints(lm, xHeadSlice, xTailSlice); // Test if either of these points is close to the cursor if(this->TestPointInClickRadius(to_float(xSlice), xHeadSlice, 5)) out_handle = 0; else if(this->TestPointInClickRadius(to_float(xSlice), xTailSlice, 5)) out_handle = 1; } if(out_handle >= 0) return it->GetPointer(); } } return NULL; } bool AnnotationModel::CheckState(AnnotationModel::UIState state) { switch(state) { case AnnotationModel::UIF_LINE_MODE: return this->GetAnnotationMode() == ANNOTATION_RULER; break; case AnnotationModel::UIF_LANDMARK_MODE: return this->GetAnnotationMode() == ANNOTATION_LANDMARK; break; case AnnotationModel::UIF_LINE_MODE_DRAWING: // return this->IsDrawingRuler(); return this->GetFlagDrawingLine(); break; case AnnotationModel::UIF_EDITING_MODE: return this->GetAnnotationMode() == ANNOTATION_SELECT; break; } return false; } bool AnnotationModel::IsHoveringOverAnnotation(const Vector3d &xSlice) { return (this->GetAnnotationUnderCursor(xSlice) != NULL); } void AnnotationModel::GetLandmarkArrowPoints(const annot::Landmark &lm, Vector3f &outHeadXSlice, Vector3f &outTailXSlice) { // Get the head coordinates in slice units outHeadXSlice = m_Parent->MapImageToSlice(lm.Pos); // Get the tail coordinate in slice units outTailXSlice = m_Parent->MapPhysicalWindowToSlice( m_Parent->MapSliceToPhysicalWindow(outHeadXSlice) + lm.Offset); } AnnotationModel::AnnotationModel() { m_FlagDrawingLine = false; Rebroadcast(this, ModelUpdateEvent(), StateMachineChangeEvent()); } AnnotationModel::~AnnotationModel() { } itksnap-3.4.0/GUI/Model/AnnotationModel.h000066400000000000000000000100151263013355200201140ustar00rootroot00000000000000#ifndef ANNOTATIONMODEL_H #define ANNOTATIONMODEL_H #include "AbstractModel.h" #include "PropertyModel.h" #include "SNAPCommon.h" #include "IRISException.h" #include "GlobalState.h" #include "ImageAnnotationData.h" #include #include #include class GenericSliceModel; /** * @brief The UI model for slice annotation */ class AnnotationModel : public AbstractModel { public: typedef annot::AbstractAnnotation AbstractAnnotation; typedef annot::LineSegmentAnnotation LineSegmentAnnotation; typedef annot::LandmarkAnnotation LandmarkAnnotation; typedef annot::LineSegment LineSegment; irisITKObjectMacro(AnnotationModel, AbstractModel) // States enum UIState { UIF_LINE_MODE, UIF_LINE_MODE_DRAWING, UIF_LANDMARK_MODE, UIF_EDITING_MODE }; FIRES(StateMachineChangeEvent) irisGetMacro(Parent, GenericSliceModel *) void SetParent(GenericSliceModel *model); /** Get the line drawing state */ irisGetMacro(FlagDrawingLine, bool) /** Get the current line */ irisGetMacro(CurrentLine, const LineSegment &) /** Whether we are moving something now */ irisIsMacro(MovingSelection) /** Get the physical length of line segment */ double GetLineLength(const Vector3f &xSliceA, const Vector3f &xSliceB); /** Get the physical length of current line */ double GetCurrentLineLength(); /** Get the length of the current line in logical (non-retina) screen pixel units */ double GetCurrentLineLengthInPixels(); /** Compute angle between two lines */ double GetAngleWithCurrentLine(const annot::LineSegmentAnnotation *lsa); /** Helper function - get current annotation mode from GlobalState */ AnnotationMode GetAnnotationMode() const; /** Helper function - get annotation data from IRISApplication */ ImageAnnotationData *GetAnnotations(); /** Test if an annotation is visible in this slice */ bool IsAnnotationVisible(const AbstractAnnotation *annot); bool ProcessPushEvent(const Vector3d &xSlice, bool shift_mod); bool ProcessMoveEvent(const Vector3d &xSlice, bool shift_mod, bool drag); bool ProcessReleaseEvent(const Vector3d &xSlice, bool shift_mod); bool IsDrawingRuler(); void AcceptLine(); void CancelLine(); void SelectAllOnSlice(); void DeleteSelectedOnSlice(); void GoToNextAnnotation(); void GoToPreviousAnnotation(); bool CheckState(UIState state); bool IsHoveringOverAnnotation(const Vector3d &xSlice); /** Set the text assigned to the current annotation */ irisGetSetMacro(CurrentAnnotationText, std::string) Vector3f GetAnnotationCenter(const AbstractAnnotation *annot); void GetLandmarkArrowPoints(const annot::Landmark &lm, Vector3f &outHeadXSlice, Vector3f &outTailXSlice); protected: AnnotationModel(); virtual ~AnnotationModel(); // Parent model GenericSliceModel *m_Parent; // Current state for line drawing bool m_FlagDrawingLine; LineSegment m_CurrentLine; // Motion-related variables Vector3f m_DragStart, m_DragLast; bool m_MovingSelection; int m_MovingSelectionHandle; annot::AbstractAnnotation *m_MovingSelectionHandleAnnot; // Text assigned to the currently drawn annotation std::string m_CurrentAnnotationText; double GetDistanceToLine(const Vector3f &x1, const Vector3f &x2, const Vector3d &point); double GetDistanceToLine(LineSegment &line, const Vector3d &point); double GetPixelDistanceToAnnotation(const AbstractAnnotation *annot, const Vector3d &point); void AdjustAngleToRoundDegree(LineSegment &ls, int n_degrees); AbstractAnnotation *GetAnnotationUnderCursor(const Vector3d &xSlice); void GoToNextOrPrevAnnotation(int direction); annot::AbstractAnnotation *GetSelectedHandleUnderCusror(const Vector3d &xSlice, int &out_handle); bool TestPointInClickRadius(const Vector3f &xClickSlice, const Vector3f &xPointSlice, int logical_pixels); // Apply move command to annotation handle void MoveAnnotationHandle(AbstractAnnotation *ann, int handle, const Vector3f &deltaPhys); }; #endif // ANNOTATIONMODEL_H itksnap-3.4.0/GUI/Model/CollectionModel.cxx000066400000000000000000000001051263013355200204470ustar00rootroot00000000000000#include "CollectionModel.h" CollectionModel::CollectionModel() { } itksnap-3.4.0/GUI/Model/CollectionModel.h000066400000000000000000000155571263013355200201150ustar00rootroot00000000000000#ifndef COLLECTIONMODEL_H #define COLLECTIONMODEL_H #include "AbstractModel.h" /** * A model representing a collection of items. Each item has a unique value * (like a key in a database) and a descriptor (object describing the item's * properties). The model supports iteration. The model can be wrapped around * an existing stl container, or it can subclass from an stl container. * * The model emits events when there are changes in the structure of the * collection, as well as when there are changes in the descriptors of the * collection. * * The model is meant to interface with GUI list/combo/table widgets. */ template class AbstractCollectionModel : public AbstractModel { public: typedef TIterator const_iterator; typedef TVal ValueType; typedef TDesc DescriptorType; FIRES(CollectionStructureChangedEvent) FIRES(CollectionDataChangedEvent) virtual const_iterator begin() const = 0; virtual const_iterator end() const = 0; virtual const_iterator find(const TVal &value) const = 0; virtual TVal GetValue(const const_iterator &it) const = 0; virtual TDesc GetDescription(const const_iterator &it) const = 0; virtual void SetDescription(const const_iterator &it, const TDesc &desc) const {} virtual ~AbstractItemSetDomain() {} }; /** This is an implementation of the domain that wraps around an stl::map from values to descriptors. The map is not stored in the domain, but referenced from another object to avoid duplicating data. */ template class STLMapWrapperModel : public AbstractCollectionModel::const_iterator> { public: typedef typename std::map MapType; typedef typename MapType::const_iterator const_iterator; typedef AbstractCollectionModel Superclass; typedef STLMapWrapperModel Self; itkNewMacro(Self) itkTypeMacro(STLMapWrapperModel, AbstractCollectionModel) const_iterator begin() const { assert(m_SourceMap); return m_SourceMap->begin(); } const_iterator end() const { assert(m_SourceMap); return m_SourceMap->end(); } const_iterator find(const TVal &value) const { assert(m_SourceMap); return m_SourceMap->find(value); } TVal GetValue(const const_iterator &it) const { return it->first; } TDesc GetDescription(const const_iterator &it) const { return it->second; } void SetWrappedMap(const MapType *refmap) { m_SourceMap = refmap; } virtual bool operator == (const Self &cmp) const { return m_SourceMap == cmp.m_SourceMap; } virtual bool operator != (const Self &cmp) const { return m_SourceMap != cmp.m_SourceMap; } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. virtual bool isAtomic() { return false; } protected: STLMapWrapperModel() { m_SourceMap = NULL; } STLMapWrapperModel(const MapType *refmap) { m_SourceMap = refmap; } virtual ~STLMapWrapperModel() {} const MapType *m_SourceMap; }; /** This is an implementation of the domain that wraps around an stl::vector of descriptors. TVal should be an integer type that can be used as an index (int, unsigned int, enum, etc) */ template class STLVectorWrapperModel : public AbstractCollectionModel::const_iterator> { public: typedef STLVectorWrapperModel Self; typedef typename std::vector VectorType; typedef typename VectorType::const_iterator const_iterator; typedef AbstractCollectionModel Superclass; itkNewMacro(Self) itkTypeMacro(STLMapWrapperModel, AbstractCollectionModel) const_iterator begin() const { assert(m_SourceVector); return m_SourceVector->begin(); } const_iterator end() const { assert(m_SourceVector); return m_SourceVector->end(); } const_iterator find(const TVal &value) const { assert(m_SourceVector); return m_SourceVector->begin() + value; } TVal GetValue(const const_iterator &it) const { assert(m_SourceVector); return it - m_SourceVector->begin(); } TDesc GetDescription(const const_iterator &it) const { assert(m_SourceVector); return *it; } virtual bool operator == (const Self &cmp) const { return m_SourceVector == cmp.m_SourceVector; } virtual bool operator != (const Self &cmp) const { return m_SourceVector != cmp.m_SourceVector; } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. virtual bool isAtomic() { return false; } protected: STLVectorWrapperModel() { m_SourceVector = NULL; } STLVectorWrapperModel(const VectorType *refvec) { m_SourceVector = refvec; } virtual ~STLVectorWrapperModel() {} const VectorType *m_SourceVector; }; /** This is an item domain implementation that is just an stl::map, i.e., it owns the data, as opposed to STLMapWrapperItemSetDomain, which references the data from another map. This implementation is useful for small domains where there is no cost in passing the domain by value. */ template class ConcreteItemCollectionModel : public AbstractCollectionModel::const_iterator> { public: typedef std::map MapType; typedef typename MapType::const_iterator const_iterator; typedef ConcreteItemCollectionModel Self; typedef AbstractCollectionModel Superclass; itkNewMacro(Self) itkTypeMacro(ConcreteItemCollectionModel, AbstractCollectionModel) const_iterator begin() const { return m_Map.begin(); } const_iterator end() const { return m_Map.end(); } const_iterator find(const TVal &value) const { return m_Map.find(value); } TVal GetValue(const const_iterator &it) const { return it->first; } TDesc GetDescription(const const_iterator &it) const { return it->second; } // Standard stl::map operator TDesc & operator [] (const TVal &key) { return m_Map[key]; } const TDesc & operator [] (const TVal &key) const { return m_Map[key]; } virtual bool operator == (const Self &cmp) const { return m_Map == cmp.m_Map; } virtual bool operator != (const Self &cmp) const { return m_Map != cmp.m_Map; } // An atomic domain holds its own state, so it is possible to compare two // atomic domains to determine if they are the same or different. Domains // that store references to external objects are not atomic. virtual bool isAtomic() { return true; } protected: SimpleItemSetDomain() : Superclass() { } MapType m_Map; }; #endif // COLLECTIONMODEL_H itksnap-3.4.0/GUI/Model/ColorLabelPropertyModel.cxx000066400000000000000000000073661263013355200221570ustar00rootroot00000000000000#include "ColorLabelPropertyModel.h" #include void ConcreteColorLabelPropertyModel ::Initialize(ColorLabelTable *clt) { // Ititialize the domain representation DomainType dom(&clt->GetValidLabels()); this->SetDomain(dom); // We should also listen to events from the label table, and rebroadcast // as changes to the domain. Note that there are two types of changes to the // label table, one that is a reconfiguration and another that is a property // change. These map to different kinds of domain change events. Rebroadcast(clt, SegmentationLabelConfigurationChangeEvent(), DomainChangedEvent()); Rebroadcast(clt, SegmentationLabelPropertyChangeEvent(), DomainDescriptionChangedEvent()); } DrawOverLabelItemSetIterator ::DrawOverLabelItemSetIterator(ColorLabelTable *table) { m_LabelIter = table->GetValidLabels().begin(); m_PointedMode = PAINT_OVER_ALL; } DrawOverLabelItemSetIterator& DrawOverLabelItemSetIterator ::operator ++() { // Iterate over modes if(m_PointedMode == PAINT_OVER_ALL) m_PointedMode = PAINT_OVER_VISIBLE; else if(m_PointedMode == PAINT_OVER_VISIBLE) m_PointedMode = PAINT_OVER_ONE; else ++m_LabelIter; return *this; } bool DrawOverLabelItemSetIterator ::operator ==(const Self &comp) { return (m_PointedMode == comp.m_PointedMode) && (m_LabelIter == comp.m_LabelIter); } bool DrawOverLabelItemSetIterator ::operator !=(const Self &comp) { return (m_PointedMode != comp.m_PointedMode) || (m_LabelIter != comp.m_LabelIter); } DrawOverLabelItemSetDomain ::DrawOverLabelItemSetDomain() { m_LabelTable = NULL; } void DrawOverLabelItemSetDomain ::Initialize(ColorLabelTable *clt) { this->m_LabelTable = clt; } DrawOverLabelItemSetDomain::const_iterator DrawOverLabelItemSetDomain::begin() const { const_iterator it(m_LabelTable); return it; } DrawOverLabelItemSetDomain::const_iterator DrawOverLabelItemSetDomain::end() const { const_iterator it(m_LabelTable); it.m_PointedMode = PAINT_OVER_ONE; it.m_LabelIter = m_LabelTable->end(); return it; } DrawOverLabelItemSetDomain::const_iterator DrawOverLabelItemSetDomain::find(const DrawOverFilter &value) const { const_iterator it(m_LabelTable); it.m_PointedMode = value.CoverageMode; if(value.CoverageMode == PAINT_OVER_ONE) it.m_LabelIter = m_LabelTable->GetValidLabels().find(value.DrawOverLabel); return it; } DrawOverFilter DrawOverLabelItemSetDomain::GetValue(const const_iterator &it) const { DrawOverFilter filter; filter.CoverageMode = it.m_PointedMode; filter.DrawOverLabel = it.m_LabelIter->first; return filter; } ColorLabel DrawOverLabelItemSetDomain::GetDescription(const const_iterator &it) const { // For the override modes, return a dummy color label if(it.m_PointedMode != PAINT_OVER_ONE) return ColorLabel(); else return it.m_LabelIter->second; } bool DrawOverLabelItemSetDomain::operator == ( const DrawOverLabelItemSetDomain &refdom) { return m_LabelTable == refdom.m_LabelTable; } bool DrawOverLabelItemSetDomain::operator != ( const DrawOverLabelItemSetDomain &refdom) { return m_LabelTable != refdom.m_LabelTable; } void ConcreteDrawOverFilterPropertyModel ::Initialize(ColorLabelTable *clt) { // Ititialize the domain representation DomainType dom; dom.Initialize(clt); this->SetDomain(dom); // We should also listen to events from the label table, and rebroadcast // as changes to the domain. Note that there are two types of changes to the // label table, one that is a reconfiguration and another that is a property // change. These map to different kinds of domain change events. Rebroadcast(clt, SegmentationLabelConfigurationChangeEvent(), DomainChangedEvent()); Rebroadcast(clt, SegmentationLabelPropertyChangeEvent(), DomainDescriptionChangedEvent()); } itksnap-3.4.0/GUI/Model/ColorLabelPropertyModel.h000066400000000000000000000065361263013355200216020ustar00rootroot00000000000000#ifndef COLORLABELPROPERTYMODEL_H #define COLORLABELPROPERTYMODEL_H #include #include #include /* This module contains property models having to do with selection of color labels from the color label table. */ // Define the domain that wraps around a std::map of color labels typedef STLMapWrapperItemSetDomain ColorLabelItemSetDomain; // Base class typedef for the concrete implementation below typedef ConcretePropertyModel ConcreteColorLabelPropertyModelBase; /** This is an ITK-SNAP model that internally stores a color label and provides a set of options, correponding to currently available color labels */ class ConcreteColorLabelPropertyModel : public ConcretePropertyModel { public: // Standard ITK stuff irisITKObjectMacro(ConcreteColorLabelPropertyModel, ConcreteColorLabelPropertyModelBase) // Domain typedef typedef ColorLabelItemSetDomain DomainType; /** Set the color label table, from which this model constructs its domain representation */ void Initialize(ColorLabelTable *clt); }; /** This iterator presents all the draw-over options in a linear fashion. It first go through the wildcard coverage modes (PAINT_OVER_ALL, PAINT_OVER_VISIBLE) and then throught the valid labels */ class DrawOverLabelItemSetIterator { public: typedef DrawOverLabelItemSetIterator Self; Self& operator ++(); bool operator == (const Self &ref); bool operator != (const Self &ref); friend class DrawOverLabelItemSetDomain; private: DrawOverLabelItemSetIterator(ColorLabelTable *table); CoverageModeType m_PointedMode; ColorLabelTable::ValidLabelConstIterator m_LabelIter; }; // A custom domain for working with draw-over compound class DrawOverLabelItemSetDomain : public AbstractItemSetDomain< DrawOverFilter, ColorLabel, DrawOverLabelItemSetIterator> { public: typedef DrawOverLabelItemSetIterator const_iterator; DrawOverLabelItemSetDomain(); virtual ~DrawOverLabelItemSetDomain() {} void Initialize(ColorLabelTable *clt); virtual const_iterator begin() const; virtual const_iterator end() const; virtual const_iterator find(const DrawOverFilter &value) const; virtual DrawOverFilter GetValue(const const_iterator &it) const; virtual ColorLabel GetDescription(const const_iterator &it) const; bool operator == (const DrawOverLabelItemSetDomain &refdom); bool operator != (const DrawOverLabelItemSetDomain &refdom); bool isAtomic() { return false; } private: ColorLabelTable *m_LabelTable; }; // Base class for the following model typedef ConcretePropertyModel ConcreteDrawOverFilterPropertyModelBase; /** This is an ITK-SNAP model that internally stores a draw-over state atomic object. */ class ConcreteDrawOverFilterPropertyModel : public ConcreteDrawOverFilterPropertyModelBase { public: // Standard ITK stuff irisITKObjectMacro(ConcreteDrawOverFilterPropertyModel, ConcreteDrawOverFilterPropertyModelBase) // Domain typedef typedef DrawOverLabelItemSetDomain DomainType; /** Set the color label table, from which this model constructs its domain representation */ void Initialize(ColorLabelTable *clt); }; #endif // COLORLABELPROPERTYMODEL_H itksnap-3.4.0/GUI/Model/ColorLabelQuickListModel.cxx000066400000000000000000000075561263013355200222440ustar00rootroot00000000000000#include "ColorLabelQuickListModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "ColorLabelTable.h" #include ColorLabelQuickListModel::ColorLabelQuickListModel() { m_ActiveComboModel = wrapGetterSetterPairAsProperty( this, &Self::GetActiveComboValueAndRange, &Self::SetActiveComboValue); } ColorLabelQuickListModel::~ColorLabelQuickListModel() { } /* void ColorLabelQuickListModel::ComputeRecent() { // Get the color label table ColorLabelTable *clt = m_Parent->GetDriver()->GetColorLabelTable(); // We use a pair of label id, access time to represent each label typedef std::pair LabelPair; // Build the priority queue for keeping track of the most recent typedef std::priority_queue HeapType; HeapType heap; // Go through all the items in the label list, maintaining the smallest // items in the queue for(ColorLabelTable::ValidLabelConstIterator it = clt->begin(); it != clt->end(); ++it) { // Encode the label as (timestamp, -id) for sorting LabelPair pair = std::make_pair(-it->second.GetTimeStamp(), (int) it->first); // If the queue is not full, push the pair if(heap.size() < m_MaxStoredLabels) { heap.push(pair); } else if(heap.top() < pair) { heap.pop(); heap.push(pair); } } // Now the list has the most recent labels unsigned int nItems = std::min(m_MaxStoredLabels, (unsigned int) heap.size()); m_RecentLabels.resize(nItems); for(int i = 1; i <= nItems; i++) { m_RecentLabels[nItems-i] = heap.top().second; heap.pop(); } // Now we sort the list by label id. The effect of this is that the order // of the labels in the list does not change as the user picks labels from // the quick list std::sort(m_RecentLabels.begin(), m_RecentLabels.end()); } */ void ColorLabelQuickListModel::SetParentModel(GlobalUIModel *parent) { m_Parent = parent; m_LabelHistory = m_Parent->GetDriver()->GetLabelUseHistory(); // Rebroadcast the segmentation and color label update events as // update events from this model Rebroadcast(m_Parent->GetDriver()->GetColorLabelTable(), SegmentationLabelConfigurationChangeEvent(), ModelUpdateEvent()); Rebroadcast(m_LabelHistory, itk::ModifiedEvent(), ModelUpdateEvent()); // The active label model needs to rebroadcast events from the active drawing // label in GlobalState m_ActiveComboModel->RebroadcastFromSourceProperty( m_Parent->GetDriver()->GetGlobalState()->GetDrawingColorLabelModel()); m_ActiveComboModel->RebroadcastFromSourceProperty( m_Parent->GetDriver()->GetGlobalState()->GetDrawOverFilterModel()); } void ColorLabelQuickListModel::OnUpdate() { if(!m_EventBucket->IsEmpty()) { // Update the table of recent labels m_RecentCombos.clear(); int nItems = m_LabelHistory->GetSize(); for(int i = 0; i < nItems; i++) m_RecentCombos.push_back(m_LabelHistory->GetHistoryEntry(i)); // The last item is always the clear label m_RecentCombos.push_back(std::make_pair(0, DrawOverFilter())); } } bool ColorLabelQuickListModel::GetActiveComboValueAndRange(int &value) { // Record the current state LabelCombo state = std::make_pair( m_Parent->GetGlobalState()->GetDrawingColorLabel(), m_Parent->GetGlobalState()->GetDrawOverFilter()); // See if any of the active labels matches the current state for(int i = 0; i < m_RecentCombos.size(); i++) { if(m_RecentCombos[i] == state) { value = i; return true; } } // Nothing matched, return false return false; } void ColorLabelQuickListModel::SetActiveComboValue(int value) { assert(value < m_RecentCombos.size()); LabelCombo state = m_RecentCombos[value]; m_Parent->GetGlobalState()->SetDrawingColorLabel(state.first); m_Parent->GetGlobalState()->SetDrawOverFilter(state.second); } itksnap-3.4.0/GUI/Model/ColorLabelQuickListModel.h000066400000000000000000000026641263013355200216640ustar00rootroot00000000000000#ifndef COLORLABELQUICKLISTMODEL_H #define COLORLABELQUICKLISTMODEL_H #include #include class GlobalUIModel; /** * This model provides a list of the recently used color labels in ITK-SNAP. * It can be used to create quick palettes of labels. */ class ColorLabelQuickListModel : public AbstractModel { public: irisITKObjectMacro(ColorLabelQuickListModel, AbstractModel) // Foreground/background label combination typedef LabelUseHistory::Entry LabelCombo; typedef std::vector ComboList; /** Assign a parent model to this model */ void SetParentModel(GlobalUIModel *parent); /** Get the list of combos to include */ irisGetMacro(RecentCombos, const ComboList &) /** This model describes the active label combination in the quick list */ irisSimplePropertyAccessMacro(ActiveCombo, int) protected: // The parent model GlobalUIModel *m_Parent; // The label history object LabelUseHistory *m_LabelHistory; // Cached list of active combos ComboList m_RecentCombos; // Model for the active label SmartPtr m_ActiveComboModel; bool GetActiveComboValueAndRange(int &value); void SetActiveComboValue(int value); ColorLabelQuickListModel(); ~ColorLabelQuickListModel(); // Respond to an update in the model virtual void OnUpdate(); // Compute the recent labels void ComputeRecent(); }; #endif // COLORLABELQUICKLISTMODEL_H itksnap-3.4.0/GUI/Model/ColorMapModel.cxx000066400000000000000000000410301263013355200200720ustar00rootroot00000000000000#include "ColorMapModel.h" #include "LayerAssociation.txx" #include "NumericPropertyToggleAdaptor.h" #include "ColorMapPresetManager.h" #include // This compiles the LayerAssociation for the color map template class LayerAssociation; ColorMapModel::ColorMapModel() { // Set up the models m_MovingControlPositionModel = wrapGetterSetterPairAsProperty( this, &Self::GetMovingControlPositionValueAndRange, &Self::SetMovingControlPosition); // Get the component model for opacity m_MovingControlOpacityModel = wrapGetterSetterPairAsProperty( this, &Self::GetMovingControlOpacityValueAndRange, &Self::SetMovingControlOpacity); m_MovingControlSideModel = wrapGetterSetterPairAsProperty( this, &Self::GetMovingControlSide, &Self::SetMovingControlSide); m_MovingControlContinuityModel = wrapGetterSetterPairAsProperty( this, &Self::GetMovingControlType, &Self::SetMovingControlType); m_MovingControlIndexModel = wrapGetterSetterPairAsProperty( this, &Self::GetMovingControlIndexValueAndRange, &Self::SetMovingControlIndex); m_LayerOpacityModel = wrapGetterSetterPairAsProperty( this, &Self::GetLayerOpacityValueAndRange, &Self::SetLayerOpacity); m_LayerVisibilityModel = NewNumericPropertyToggleAdaptor(m_LayerOpacityModel.GetPointer(), 0., 50.); // Create the color map preset manager m_PresetManager = NULL; // The model update events should also be rebroadcast as state changes Rebroadcast(this, ModelUpdateEvent(), StateMachineChangeEvent()); } ColorMap* ColorMapModel::GetColorMap(ImageWrapperBase *layer) { // Get the display mapping cast to a type that supports colormap return layer->GetDisplayMapping()->GetColorMap(); } ColorMap* ColorMapModel::GetColorMap() { return this->GetColorMap(this->GetLayer()); } bool ColorMapModel ::IsControlSelected(int cp, Side side) { ColorMap *cm = this->GetColorMap(); ColorMapLayerProperties &p = this->GetProperties(); if(p.GetSelectedControlIndex() == cp) { if(cm->GetCMPoint(cp).m_Type == ColorMap::CONTINUOUS) return true; else return p.GetSelectedControlSide() == side; } else return false; } bool ColorMapModel ::SetSelection(int cp, Side side) { ColorMap *cm = this->GetColorMap(); ColorMapLayerProperties &p = this->GetProperties(); int cp_current = p.GetSelectedControlIndex(); Side side_current = p.GetSelectedControlSide(); // Check if the control point has changed bool changed = (cp != cp_current || side != side_current); // Check the validity of the new selection if(cp >= 0) { bool disc = cm->GetCMPoint(cp).m_Type == ColorMap::DISCONTINUOUS; assert((disc && side != ColorMapLayerProperties::NA) || (!disc && side == ColorMapLayerProperties::NA)); } else { assert(side == ColorMapLayerProperties::NA); } // Set the new selection if(changed) { p.SetSelectedControlIndex(cp); p.SetSelectedControlSide(side); InvokeEvent(ModelUpdateEvent()); } return changed; } void ColorMapModel::RegisterWithLayer(ImageWrapperBase *layer) { // Register for the model events ColorMap *cm = this->GetColorMap(layer); ColorMapLayerProperties &p = GetProperties(); // Rebroadcast wrapper change events p.SetLayerObserverTag( Rebroadcast(layer,WrapperDisplayMappingChangeEvent(), ModelUpdateEvent())); // Update the cached preset value if(cm) p.SetSelectedPreset(m_PresetManager->QueryPreset(cm)); } void ColorMapModel::UnRegisterFromLayer(ImageWrapperBase *layer, bool being_deleted) { if(!being_deleted) { unsigned long tag; ColorMapLayerProperties &p = GetProperties(); if((tag = p.GetLayerObserverTag())) { layer->RemoveObserver(tag); } } } bool ColorMapModel::ProcessMousePressEvent(const Vector3d &x) { assert(m_ViewportReporter && m_ViewportReporter->CanReportSize()); Vector2ui vp = m_ViewportReporter->GetLogicalViewportSize(); // Reference to the color map ColorMap *cm = this->GetColorMap(); // Check if the press occurs near a control point for(size_t i = 0; i < cm->GetNumberOfCMPoints(); i++) { ColorMap::CMPoint p = cm->GetCMPoint(i); double dx = fabs(x[0] - p.m_Index); double dy0 = fabs(x[1] - p.m_RGBA[0][3] / 255.0); double dy1 = fabs(x[1] - p.m_RGBA[1][3] / 255.0); if(dx / 1.2 < 5.0 / vp[0]) { if(dy0 / 1.2 < 5.0 / vp[1]) { // We return 0 when the selected point changes to avoid dragging if(p.m_Type == ColorMap::CONTINUOUS) this->SetSelection(i, ColorMapLayerProperties::NA); else this->SetSelection(i, ColorMapLayerProperties::LEFT); return 1; } else if (dy1 / 1.2 < 5.0 / vp[1]) { this->SetSelection(i, ColorMapLayerProperties::RIGHT); return true; } } } // No selection has been made, so we insert a new point if(x[0] > 0.0 && x[0] < 1.0) { size_t sel = cm->InsertInterpolatedCMPoint(x[0]); this->SetSelection(sel, ColorMapLayerProperties::NA); return true; } return false; } bool ColorMapModel::ProcessMouseDragEvent(const Vector3d &x) { // Reference to the color map ColorMap *cm = this->GetColorMap(); ColorMapLayerProperties &p = this->GetProperties(); int isel = p.GetSelectedControlIndex(); Side side = p.GetSelectedControlSide(); // Nothing happens if zero is selected if(isel < 0) return false; // Get the selected point ColorMap::CMPoint psel = cm->GetCMPoint(isel); // Get the new alpha and index double j = x[0]; double a = x[1] * 255; // Clip the new index if(isel == 0 || isel == (int)cm->GetNumberOfCMPoints()-1) { // The first and last point can not be moved left or right j = psel.m_Index; } else { // Other points are constrained by neighbors ColorMap::CMPoint p0 = cm->GetCMPoint(isel-1); ColorMap::CMPoint p1 = cm->GetCMPoint(isel+1); if(j < p0.m_Index) j = p0.m_Index; if(j > p1.m_Index) j = p1.m_Index; } // Update the index of the point psel.m_Index = j; // Clip the new alpha if(a < 0) a = 0; if(a > 255) a = 255; // Assign the alpha if(side != ColorMapLayerProperties::RIGHT) psel.m_RGBA[0][3] = (unsigned char) a; if(side != ColorMapLayerProperties::LEFT) psel.m_RGBA[1][3] = (unsigned char) a; // Redraw cm->UpdateCMPoint(isel, psel); return true; } bool ColorMapModel::ProcessMouseReleaseEvent(const Vector3d &x) { return true; } void ColorMapModel::OnUpdate() { Superclass::OnUpdate(); // If the preset changed or the color map changed, we should update the // currently selected preset if(m_Layer && this->GetColorMap()) { if(m_EventBucket->HasEvent(itk::ModifiedEvent(), m_PresetManager) || m_EventBucket->HasEvent(WrapperDisplayMappingChangeEvent())) { ColorMapLayerProperties &p = this->GetProperties(); p.SetSelectedPreset(m_PresetManager->QueryPreset(this->GetColorMap())); } } } bool ColorMapModel ::GetMovingControlPositionValueAndRange( double &value, NumericValueRange *range) { // When no layer is set, the value is invalid if(!m_Layer) return false; ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); int idx = p.GetSelectedControlIndex(); if(idx >= 0) { value = cmap->GetCMPoint(idx).m_Index; if(range) { range->StepSize = 0.01; if(idx == 0) { range->Minimum = 0.0; range->Maximum = 0.0; } else if (idx == (int) cmap->GetNumberOfCMPoints()-1) { range->Minimum = 1.0; range->Maximum = 1.0; } else { range->Minimum = cmap->GetCMPoint(idx - 1).m_Index; range->Maximum = cmap->GetCMPoint(idx + 1).m_Index; } } return true; } else return false; } void ColorMapModel ::SetMovingControlPosition(double value) { ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); int idx = p.GetSelectedControlIndex(); assert(idx >= 0); ColorMap::CMPoint pt = cmap->GetCMPoint(idx); pt.m_Index = value; cmap->UpdateCMPoint(idx, pt); } bool ColorMapModel::GetSelectedRGBA(ColorMap::RGBAType &rgba) { // When no layer is set, the value is invalid if(!m_Layer) return false; ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); int idx = p.GetSelectedControlIndex(); Side side = p.GetSelectedControlSide(); if(idx >= 0) { ColorMap::CMPoint pt = cmap->GetCMPoint(idx); int iside = (pt.m_Type == ColorMap::DISCONTINUOUS && side == ColorMapLayerProperties::RIGHT) ? 1 : 0; rgba = pt.m_RGBA[iside]; return true; } else return false; } void ColorMapModel::SetSelectedRGBA(ColorMap::RGBAType rgba) { ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); int idx = p.GetSelectedControlIndex(); Side side = p.GetSelectedControlSide(); assert(idx >= 0); // Assign to left, right, or both sides ColorMap::CMPoint pt = cmap->GetCMPoint(idx); if(pt.m_Type == ColorMap::CONTINUOUS || side == ColorMapLayerProperties::LEFT) pt.m_RGBA[0] = rgba; if(pt.m_Type == ColorMap::CONTINUOUS || side == ColorMapLayerProperties::RIGHT) pt.m_RGBA[1] = rgba; cmap->UpdateCMPoint(idx, pt); } Vector3d ColorMapModel::GetSelectedColor() { ColorMap::RGBAType rgba; if(this->GetSelectedRGBA(rgba)) return Vector3d(rgba[0] / 255., rgba[1] / 255., rgba[2] / 255.); else return Vector3d(0,0,0); } void ColorMapModel::SetSelectedColor(Vector3d rgb) { ColorMap::RGBAType rgba; if(this->GetSelectedRGBA(rgba)) { rgba[0] = (unsigned char)(255.0 * rgb[0]); rgba[1] = (unsigned char)(255.0 * rgb[1]); rgba[2] = (unsigned char)(255.0 * rgb[2]); this->SetSelectedRGBA(rgba); } } bool ColorMapModel ::GetMovingControlOpacityValueAndRange( double &value, NumericValueRange *range) { // When no layer is set, the value is invalid if(!m_Layer) return false; ColorMap::RGBAType rgba; if(this->GetSelectedRGBA(rgba)) { value = rgba[3] / 255.0; if(range) { range->Set(0.0, 1.0, 0.01); } return true; } else return false; } void ColorMapModel ::SetMovingControlOpacity(double value) { ColorMap::RGBAType rgba; if(this->GetSelectedRGBA(rgba)) { rgba[3] = (unsigned char)(255.0 * value); this->SetSelectedRGBA(rgba); } } bool ColorMapModel ::CheckState(ColorMapModel::UIState state) { // All flags are false if no layer is loaded if(this->GetLayer() == NULL || this->GetColorMap() == NULL) return false; // Otherwise get the properties ColorMapLayerProperties &p = this->GetProperties(); int idx = p.GetSelectedControlIndex(); int npts = (int) this->GetColorMap()->GetNumberOfCMPoints(); switch(state) { case UIF_LAYER_ACTIVE: return true; case UIF_CONTROL_SELECTED: return idx >= 0; case UIF_CONTROL_SELECTED_IS_NOT_ENDPOINT: return idx > 0 && idx < npts - 1; case UIF_CONTROL_SELECTED_IS_DISCONTINUOUS: return idx >= 0 && this->GetColorMap()->GetCMPoint(idx).m_Type == ColorMap::DISCONTINUOUS; case UIF_PRESET_SELECTED: return p.GetSelectedPreset().first != ColorMapPresetManager::PRESET_NONE; case UIF_USER_PRESET_SELECTED: return p.GetSelectedPreset().first == ColorMapPresetManager::PRESET_USER; } return false; } bool ColorMapModel ::GetMovingControlSide(Side &value) { // When no layer is set, the value is invalid if(!m_Layer) return false; ColorMapLayerProperties &p = this->GetProperties(); int idx = p.GetSelectedControlIndex(); if(idx >= 0) { value = p.GetSelectedControlSide(); return true; } return false; } void ColorMapModel ::SetMovingControlSide(Side value) { ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); int idx = p.GetSelectedControlIndex(); assert(idx >= 0); ColorMap::CMPoint pt = cmap->GetCMPoint(idx); assert(pt.m_Type == ColorMap::DISCONTINUOUS); p.SetSelectedControlSide(value); InvokeEvent(ModelUpdateEvent()); } bool ColorMapModel::GetMovingControlType(Continuity &value) { // When no layer is set, the value is invalid if(!m_Layer) return false; ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); int idx = p.GetSelectedControlIndex(); if(idx >= 0) { value = cmap->GetCMPoint(idx).m_Type; return true; } else return false; } void ColorMapModel::SetMovingControlType(Continuity value) { ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); int idx = p.GetSelectedControlIndex(); Side side = p.GetSelectedControlSide(); assert(idx >= 0); ColorMap::CMPoint pt = cmap->GetCMPoint(idx); if(value != pt.m_Type) { pt.m_Type = value; if(value == ColorMap::CONTINUOUS) { p.SetSelectedControlSide(ColorMapLayerProperties::NA); if(side == ColorMapLayerProperties::LEFT) pt.m_RGBA[1] = pt.m_RGBA[0]; else pt.m_RGBA[0] = pt.m_RGBA[1]; } else { p.SetSelectedControlSide(ColorMapLayerProperties::LEFT); } cmap->UpdateCMPoint(idx, pt); InvokeEvent(ModelUpdateEvent()); } } // TODO: this copies the names needlessly void ColorMapModel::GetPresets( ColorMapModel::PresetList &system, ColorMapModel::PresetList &user) { system = m_PresetManager->GetSystemPresets(); user = m_PresetManager->GetUserPresets(); } void ColorMapModel::SelectPreset(const std::string &preset) { // Is this a system preset? if(preset.length()) m_PresetManager->SetToPreset(this->GetColorMap(), preset); // Clear the selection this->GetProperties().SetSelectedControlIndex(-1); this->GetProperties().SetSelectedControlSide(ColorMapLayerProperties::NA); this->InvokeEvent(ModelUpdateEvent()); } void ColorMapModel::SetParentModel(GlobalUIModel *parent) { Superclass::SetParentModel(parent); // Get the pointer to the system interface m_System = m_ParentModel->GetDriver()->GetSystemInterface(); m_PresetManager = m_ParentModel->GetDriver()->GetColorMapPresetManager(); // Rebroadcast modifications in the preset manager as our own events Rebroadcast(m_PresetManager, itk::ModifiedEvent(), PresetUpdateEvent()); } #include void ColorMapModel::SaveAsPreset(std::string name) { // Save preset m_PresetManager->SaveAsPreset(this->GetColorMap(), name); } void ColorMapModel::DeletePreset(std::string name) { // Delete the preset m_PresetManager->DeletePreset(name); } std::string ColorMapModel::GetSelectedPreset() { return this->GetProperties().GetSelectedPreset().second; } bool ColorMapModel::GetMovingControlIndexValueAndRange( int &value, NumericValueRange *range) { // When no layer is set, the value is invalid if(!m_Layer) return false; ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); int idx = p.GetSelectedControlIndex(); if(idx >= 0) { value = idx + 1; if(range) range->Set(1, cmap->GetNumberOfCMPoints(), 1); return true; } return false; } void ColorMapModel::SetMovingControlIndex(int value) { ColorMapLayerProperties &p = this->GetProperties(); ColorMap *cmap = this->GetColorMap(); ColorMap::CMPoint pt = cmap->GetCMPoint(value - 1); Side newside = ColorMapLayerProperties::NA; if(pt.m_Type == ColorMap::DISCONTINUOUS) { // Pick the side that is closest to the current selection if(value < p.GetSelectedControlIndex()) newside = ColorMapLayerProperties::RIGHT; else newside = ColorMapLayerProperties::LEFT; } this->SetSelection(value - 1, newside); } void ColorMapModel::DeleteSelectedControl() { ColorMapLayerProperties &p = this->GetProperties(); int sel = p.GetSelectedControlIndex(); ColorMap *cmap = this->GetColorMap(); if(sel > 0 && sel < (int)(cmap->GetNumberOfCMPoints() - 1)) { // Delete the point cmap->DeleteCMPoint(sel); // Update the selection this->SetMovingControlIndex(sel); } } bool ColorMapModel ::GetLayerOpacityValueAndRange( double &value, NumericValueRange *range) { if(!m_Layer) return false; value = (double) (255.0 * m_Layer->GetAlpha()); if(range) { range->Set(0, 255, 1); } return true; } void ColorMapModel::SetLayerOpacity(double value) { assert(m_Layer); m_Layer->SetAlpha(value / 255.0); } itksnap-3.4.0/GUI/Model/ColorMapModel.h000066400000000000000000000156521263013355200175320ustar00rootroot00000000000000#ifndef COLORMAPMODEL_H #define COLORMAPMODEL_H #include "AbstractLayerAssociatedModel.h" #include "PropertyModel.h" #include #include "ColorMap.h" #include "ColorMapPresetManager.h" class ColorMap; class SystemInterface; class ColorMapLayerProperties { public: irisGetSetMacro(LayerObserverTag, unsigned long) ColorMapLayerProperties() { m_SelectedControlIndex = -1; m_SelectedControlSide = NA; m_LayerObserverTag = 0; m_SelectedPreset = std::make_pair(ColorMapPresetManager::PRESET_NONE, std::string()); } virtual ~ColorMapLayerProperties() {} enum Side {LEFT = 0, RIGHT, NA}; irisGetSetMacro(SelectedControlIndex, int) irisGetSetMacro(SelectedControlSide, Side) irisGetSetMacro(SelectedPreset, ColorMapPresetManager::PresetMatch) protected: // Control point being edited int m_SelectedControlIndex; // Side of the control point, if discontinuous Side m_SelectedControlSide; // Cached information about the currently selected preset ColorMapPresetManager::PresetMatch m_SelectedPreset; // Whether or not we are already listening to events from this layer unsigned long m_ColorMapObserverTag, m_LayerObserverTag; }; typedef AbstractLayerAssociatedModel< ColorMapLayerProperties, ImageWrapperBase> ColorMapModelBase; /** The UI model for color map editing */ class ColorMapModel : public ColorMapModelBase { public: irisITKObjectMacro(ColorMapModel, ColorMapModelBase) typedef ColorMapLayerProperties::Side Side; typedef ColorMap::CMPointType Continuity; typedef std::vector PresetList; // This event only affects this model itkEventMacro(PresetUpdateEvent, IRISEvent) // This event is fired when the presets are changed FIRES(PresetUpdateEvent) /** States in which the model can be, which allow the activation and deactivation of various widgets in the interface */ enum UIState { UIF_LAYER_ACTIVE, UIF_CONTROL_SELECTED, UIF_CONTROL_SELECTED_IS_NOT_ENDPOINT, UIF_CONTROL_SELECTED_IS_DISCONTINUOUS, UIF_PRESET_SELECTED, UIF_USER_PRESET_SELECTED }; void SetParentModel(GlobalUIModel *parent); /** Check the state flags above */ bool CheckState(UIState state); // Implementation of virtual functions from parent class void RegisterWithLayer(ImageWrapperBase *layer); void UnRegisterFromLayer(ImageWrapperBase *layer, bool being_deleted); /** Before using the model, it must be coupled with a size reporter */ irisGetSetMacro(ViewportReporter, ViewportSizeReporter *) /** Process colormap box interaction events */ bool ProcessMousePressEvent(const Vector3d &x); bool ProcessMouseDragEvent(const Vector3d &x); bool ProcessMouseReleaseEvent(const Vector3d &x); /** Update the model in response to upstream events */ virtual void OnUpdate(); typedef AbstractPropertyModel ContinuityValueModel; typedef AbstractPropertyModel SideValueModel; /** The model for setting moving control point position */ irisGetMacro(MovingControlPositionModel, AbstractRangedDoubleProperty *) /** The model for setting moving control point opacity */ irisGetMacro(MovingControlOpacityModel, AbstractRangedDoubleProperty *) /** The model for setting moving control point continuity */ irisGetMacro(MovingControlContinuityModel, ContinuityValueModel *) /** The model for setting moving control point side */ irisGetMacro(MovingControlSideModel, SideValueModel *) /** The index of the moving control point */ irisGetMacro(MovingControlIndexModel, AbstractRangedIntProperty *) /** The overall opacity of the selected layer */ irisGetMacro(LayerOpacityModel, AbstractRangedDoubleProperty *) /** The overall visibility of the selected layer */ irisGetMacro(LayerVisibilityModel, AbstractSimpleBooleanProperty *) /** Get the color map in associated layer */ ColorMap *GetColorMap(); /** Check whether a particular control point is selected */ bool IsControlSelected(int cp, Side side); /** Set the selected control point, return true if selection changed as the result */ bool SetSelection(int cp, Side side = ColorMapLayerProperties::NA); /** Delete the selected control */ void DeleteSelectedControl(); /** Get the color of the selected point */ Vector3d GetSelectedColor(); /** Set the color of the selected point */ void SetSelectedColor(Vector3d rgb); /** Get the list of color map presets */ void GetPresets(PresetList &system, PresetList &user); /** Get the preset manager object */ irisGetMacro(PresetManager, ColorMapPresetManager *) /** Select one of the presets. The index is into the combined list of system and user presets */ void SelectPreset(const std::string &preset); /** Save the current state as a preset */ void SaveAsPreset(std::string name); /** Delete a selected preset */ void DeletePreset(std::string name); /** Get the currently selected preset */ std::string GetSelectedPreset(); protected: ColorMapModel(); SmartPtr m_MovingControlPositionModel; SmartPtr m_MovingControlOpacityModel; SmartPtr m_MovingControlSideModel; SmartPtr m_MovingControlContinuityModel; SmartPtr m_MovingControlIndexModel; SmartPtr m_LayerOpacityModel; SmartPtr m_LayerVisibilityModel; // A pointer to the system interface object SystemInterface *m_System; // A size reporter delegate (notifies the model when the size of the // corresponding widget changes). ViewportSizeReporter *m_ViewportReporter; // Color map preset manager ColorMapPresetManager *m_PresetManager; // Colormap presets // PresetList m_PresetSystem, m_PresetUser; // Get the RGBA for selected point bool GetSelectedRGBA(ColorMap::RGBAType &rgba); void SetSelectedRGBA(ColorMap::RGBAType rgba); // Control point position bool GetMovingControlPositionValueAndRange( double &value, NumericValueRange *range); void SetMovingControlPosition(double value); // Control point opacity bool GetMovingControlOpacityValueAndRange( double &value, NumericValueRange *range); void SetMovingControlOpacity(double value); // Control point style bool GetMovingControlType(Continuity &value); void SetMovingControlType(Continuity value); // Control point style bool GetMovingControlSide(Side &value); void SetMovingControlSide(Side value); // Selected control point index bool GetMovingControlIndexValueAndRange( int &value, NumericValueRange *range); void SetMovingControlIndex(int value); // Opacity of the selected layer bool GetLayerOpacityValueAndRange( double &value, NumericValueRange *range); void SetLayerOpacity(double value); // Extract a colormap from the layer if it has one ColorMap *GetColorMap(ImageWrapperBase *layer); }; #endif // COLORMAPMODEL_H itksnap-3.4.0/GUI/Model/CursorInspectionModel.cxx000066400000000000000000000122521263013355200216730ustar00rootroot00000000000000#include "CursorInspectionModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "ColorLabelTable.h" #include "IntensityCurveModel.h" #include "ColorMapModel.h" #include "DisplayLayoutModel.h" #include #include /** The domain used to present intensity information about the voxel under the cursor TODO: eventually, it would be nice to have a tree display for multi-component data, so that we can see the values of all components at once. */ CurrentVoxelInfoItemSetDomain ::CurrentVoxelInfoItemSetDomain(GlobalUIModel *model, int role_filter) : Superclass(model ? model->GetDriver() : NULL, role_filter), m_Model(model) { } LayerCurrentVoxelInfo CurrentVoxelInfoItemSetDomain ::GetDescription(const LayerIterator &it) const { // The output structure LayerCurrentVoxelInfo vox; // Set the name vox.LayerName = it.GetLayer()->GetNickname().c_str(); // Make sure that the layer is initialized if(it.GetLayer()->IsInitialized()) { // Get the intensity under the cursor for this layer vnl_vector v; ImageWrapperBase::DisplayPixelType disprgb; it.GetLayer()->GetVoxelUnderCursorDisplayedValueAndAppearance(v, disprgb); // Use good old sprintf! char buffer[64]; if(v.size() == 1) { sprintf(buffer, "%.4g", v[0]); } else if(v.size() == 3) { sprintf(buffer, "%.4g,%.4g,%.4g", v[0], v[1], v[2]); } vox.IntensityValue = buffer; // Get the displayed color vox.Color = Vector3ui(disprgb[0], disprgb[1], disprgb[2]); // Is the layer selected? bool stacked = m_Model->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->GetValue() == LAYOUT_STACKED; vox.isSelectedGroundLayer = stacked && ( it.GetLayer()->GetUniqueId() == m_Driver->GetGlobalState()->GetSelectedLayerId()); // Is the layer sticky? vox.isSticky = it.GetLayer()->IsSticky(); // Return the description return vox; } else { vox.IntensityValue = ""; vox.Color = Vector3ui(0); vox.isSelectedGroundLayer = false; vox.isSticky = false; } return vox; } CursorInspectionModel::CursorInspectionModel() { // Create the child models m_LabelUnderTheCursorIdModel = wrapGetterSetterPairAsProperty( this, &CursorInspectionModel::GetLabelUnderTheCursorIdValue); m_LabelUnderTheCursorTitleModel = wrapGetterSetterPairAsProperty( this, &CursorInspectionModel::GetLabelUnderTheCursorTitleValue); // Create the child model for the intensity list m_VoxelAtCursorModel = ConcreteLayerVoxelAtCursorModel::New(); } void CursorInspectionModel::SetParentModel(GlobalUIModel *parent) { // Set the parent m_Parent = parent; // Get the app IRISApplication *app = parent->GetDriver(); // Initialize the layer voxel list model int role = MAIN_ROLE | OVERLAY_ROLE | SNAP_ROLE; CurrentVoxelInfoItemSetDomain dom(parent, role); m_VoxelAtCursorModel->SetDomain(dom); // Make this model listen to events that affect its domain m_VoxelAtCursorModel->Rebroadcast( app, LayerChangeEvent(), DomainChangedEvent()); m_VoxelAtCursorModel->Rebroadcast( this, ModelUpdateEvent(), DomainDescriptionChangedEvent()); m_VoxelAtCursorModel->Rebroadcast( app, WrapperDisplayMappingChangeEvent(), DomainDescriptionChangedEvent()); m_VoxelAtCursorModel->Rebroadcast( app, WrapperMetadataChangeEvent(), DomainDescriptionChangedEvent()); m_VoxelAtCursorModel->Rebroadcast( app->GetGlobalState()->GetSelectedLayerIdModel(), ValueChangedEvent(), DomainDescriptionChangedEvent()); m_VoxelAtCursorModel->Rebroadcast( m_Parent->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel(), ValueChangedEvent(), DomainDescriptionChangedEvent()); // Rebroadcast events from the parent as model update events. This could // have a little more granularity, but for the moment, mapping all these // events to a ModelUpdateEvent seems fine. Rebroadcast(app, CursorUpdateEvent(), ModelUpdateEvent()); Rebroadcast(app, LayerChangeEvent(), ModelUpdateEvent()); Rebroadcast(app->GetColorLabelTable(), SegmentationLabelChangeEvent(), ModelUpdateEvent()); Rebroadcast(app, SegmentationChangeEvent(), ModelUpdateEvent()); } bool CursorInspectionModel::GetLabelUnderTheCursorIdValue(LabelType &value) { IRISApplication *app = m_Parent->GetDriver(); GenericImageData *id = app->GetCurrentImageData(); if(id->IsSegmentationLoaded()) { value = id->GetSegmentation()->GetVoxel(app->GetCursorPosition()); return true; } return false; } bool CursorInspectionModel::GetLabelUnderTheCursorTitleValue(std::string &value) { IRISApplication *app = m_Parent->GetDriver(); GenericImageData *id = m_Parent->GetDriver()->GetCurrentImageData(); if(id->IsSegmentationLoaded()) { LabelType label = id->GetSegmentation()->GetVoxel(app->GetCursorPosition()); value = app->GetColorLabelTable()->GetColorLabel(label).GetLabel(); return true; } return false; } AbstractRangedUIntVec3Property * CursorInspectionModel::GetCursorPositionModel() const { return m_Parent->GetCursorPositionModel(); } itksnap-3.4.0/GUI/Model/CursorInspectionModel.h000066400000000000000000000053201263013355200213160ustar00rootroot00000000000000#ifndef CURSORINSPECTIONMODEL_H #define CURSORINSPECTIONMODEL_H #include #include #include #include class GlobalUIModel; class IRISApplication; /** This structure describes the intensity information at a cursor location in a particular layer - which is displayed in the table */ struct LayerCurrentVoxelInfo { std::string LayerName; std::string IntensityValue; Vector3ui Color; bool isSelectedGroundLayer; bool isSticky; }; /** A definition of the item set domain for the table that shows current under the cursor intensity values */ class CurrentVoxelInfoItemSetDomain : public AbstractLayerInfoItemSetDomain { public: typedef AbstractLayerInfoItemSetDomain Superclass; // Define the domain with the specificed filter CurrentVoxelInfoItemSetDomain( GlobalUIModel *model = NULL, int role_filter = ALL_ROLES); // Define the description method LayerCurrentVoxelInfo GetDescription(const LayerIterator &it) const; protected: GlobalUIModel *m_Model; }; // A concrete model encapsulating the above domain typedef ConcretePropertyModel ConcreteLayerVoxelAtCursorModel; /** This little model handles the logic for the cursor inspection page */ class CursorInspectionModel : public AbstractModel { public: irisITKObjectMacro(CursorInspectionModel, AbstractModel) void SetParentModel(GlobalUIModel *parent); irisGetMacro(Parent, GlobalUIModel*) /** Get the model for the label under the cursor */ irisGetMacro(LabelUnderTheCursorIdModel, AbstractSimpleLabelTypeProperty*) /** Get the model for the label description under the cursor */ irisGetMacro(LabelUnderTheCursorTitleModel, AbstractSimpleStringProperty*) /** Get the model for the cursor location */ AbstractRangedUIntVec3Property *GetCursorPositionModel() const; // The model for a table of intensity values at cursor irisGetMacro(VoxelAtCursorModel, ConcreteLayerVoxelAtCursorModel *) protected: CursorInspectionModel(); private: // Label under the cursor SmartPtr m_LabelUnderTheCursorIdModel; bool GetLabelUnderTheCursorIdValue(LabelType &value); // Title of the label under the cursor SmartPtr m_LabelUnderTheCursorTitleModel; bool GetLabelUnderTheCursorTitleValue(std::string &value); // A pointer to the parent's cusror model AbstractRangedUIntVec3Property *m_CursorPositionModel; // The model for the intensity table SmartPtr m_VoxelAtCursorModel; // Parent GlobalUIModel *m_Parent; }; #endif // CURSORINSPECTIONMODEL_H itksnap-3.4.0/GUI/Model/DisplayLayoutModel.cxx000066400000000000000000000226701263013355200211720ustar00rootroot00000000000000#include "DisplayLayoutModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "GenericImageData.h" #include DisplayLayoutModel::DisplayLayoutModel() { // Initialize the view layout to four views m_ViewPanelLayoutModel = ConcreteViewPanelLayoutProperty::New(); m_ViewPanelLayoutModel->SetValue(VIEW_ALL); // Rebroadcast relevant events from child models Rebroadcast(m_ViewPanelLayoutModel, ValueChangedEvent(), ViewPanelLayoutChangeEvent()); // Set up the boolean visibility models for(int panel = 0; panel < 4; panel++) { // Create the derived property for panel visibility m_ViewPanelVisibilityModel[panel] = wrapIndexedGetterSetterPairAsProperty( this, panel, &Self::GetNthViewPanelVisibilityValue); // The derived model must react to changes in view panel layout m_ViewPanelVisibilityModel[panel]->Rebroadcast( this, ViewPanelLayoutChangeEvent(), ValueChangedEvent()); // Create a derived property for panel expand action m_ViewPanelExpandButtonActionModel[panel] = wrapIndexedGetterSetterPairAsProperty( this, panel, &Self::GetNthViewPanelExpandButtonActionValue); // The derived model must react to changes in view panel layout m_ViewPanelExpandButtonActionModel[panel]->Rebroadcast( this, ViewPanelLayoutChangeEvent(), ValueChangedEvent()); } // The tiling model m_SliceViewLayerTilingModel = wrapGetterSetterPairAsProperty( this, &Self::GetSliceViewLayerTilingValue); // The derived model must react to changes to the internal values m_SliceViewLayerTilingModel->Rebroadcast( this, LayerLayoutChangeEvent(), ValueChangedEvent()); // The number of ground levels model m_NumberOfGroundLevelLayersModel = wrapGetterSetterPairAsProperty( this, &Self::GetNumberOfGroundLevelLayersValue); // The derived model must react to changes to the internal values m_NumberOfGroundLevelLayersModel->Rebroadcast( this, LayerLayoutChangeEvent(), ValueChangedEvent()); // Thumbnail size m_ThumbnailRelativeSizeModel = NewRangedConcreteProperty(16.0, 0.0, 40.0, 1.0); Rebroadcast(m_ThumbnailRelativeSizeModel, ValueChangedEvent(), LayerLayoutChangeEvent()); } void DisplayLayoutModel::SetParentModel(GlobalUIModel *parentModel) { m_ParentModel = parentModel; // We need to rebroadcast the change in the display-to-anatomy mapping // as one of our own events, which will in turn be rebroadcast as value // change events in the derived models. Rebroadcast(m_ParentModel->GetDriver(), DisplayToAnatomyCoordinateMappingChangeEvent(), ViewPanelLayoutChangeEvent()); // We need to be notified when the number of overlays changes Rebroadcast(m_ParentModel->GetDriver(), LayerChangeEvent(), LayerLayoutChangeEvent()); // and when the tiled/stacked mode changes Rebroadcast(m_ParentModel->GetGlobalState()->GetSliceViewLayerLayoutModel(), ValueChangedEvent(), LayerLayoutChangeEvent()); // We also need notification when the layer stickiness changes Rebroadcast(m_ParentModel->GetDriver(), WrapperVisibilityChangeEvent(), LayerLayoutChangeEvent()); } AbstractPropertyModel * DisplayLayoutModel::GetSliceViewLayerLayoutModel() const { return m_ParentModel->GetGlobalState()->GetSliceViewLayerLayoutModel(); } void DisplayLayoutModel::ToggleSliceViewLayerLayout() { LayerLayout ll = this->GetSliceViewLayerLayoutModel()->GetValue(); if(ll == LAYOUT_TILED) { this->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_STACKED); } else { this->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_TILED); } } void DisplayLayoutModel::ActivateNextLayerInTiledMode() { // Make a list of ids that qualify std::vector ids; this->GetGroundLevelLayerIds(ids); if(ids.size() > 1) { std::vector::iterator it = std::find(ids.begin(), ids.end(), m_ParentModel->GetGlobalState()->GetSelectedLayerId()); if(it != ids.end()) { std::rotate(ids.begin(), it, ids.end()); m_ParentModel->GetGlobalState()->SetSelectedLayerId(ids[1]); } } } void DisplayLayoutModel::ActivatePrevLayerInTiledMode() { // Make a list of ids that qualify std::vector ids; this->GetGroundLevelLayerIds(ids); if(ids.size() > 1) { std::vector::iterator it = std::find(ids.begin(), ids.end(), m_ParentModel->GetGlobalState()->GetSelectedLayerId()); if(it != ids.end()) { std::rotate(ids.begin(), it, ids.end()); m_ParentModel->GetGlobalState()->SetSelectedLayerId(ids.back()); } } } bool DisplayLayoutModel ::GetNthViewPanelVisibilityValue(int panel, bool &value) { // The current layout ViewPanelLayout layout = m_ViewPanelLayoutModel->GetValue(); // If the current mode is four views, then every view is visible if(layout == VIEW_ALL) { value = true; } // If asking about the 3D window, there is no need to check display orientation else if(panel == 3) { value = (layout == VIEW_3D); } // Otherwise, we need to know the orientation of the panel in question else { IRISApplication *driver = m_ParentModel->GetDriver(); int wAxial = driver->GetDisplayWindowForAnatomicalDirection(ANATOMY_AXIAL); int wCoronal = driver->GetDisplayWindowForAnatomicalDirection(ANATOMY_CORONAL); int wSagittal = driver->GetDisplayWindowForAnatomicalDirection(ANATOMY_SAGITTAL); value = (layout == VIEW_AXIAL && panel == wAxial) || (layout == VIEW_CORONAL && panel == wCoronal) || (layout == VIEW_SAGITTAL && panel == wSagittal); } return true; } bool DisplayLayoutModel::GetNthViewPanelExpandButtonActionValue( int panel, DisplayLayoutModel::ViewPanelLayout &value) { // The current layout ViewPanelLayout layout = m_ViewPanelLayoutModel->GetValue(); // When the mode is 4-views, the action is to expand to the individual view if(layout == VIEW_ALL) { IRISApplication *driver = m_ParentModel->GetDriver(); if(panel == (int) driver->GetDisplayWindowForAnatomicalDirection(ANATOMY_AXIAL)) value = VIEW_AXIAL; else if (panel == (int) driver->GetDisplayWindowForAnatomicalDirection(ANATOMY_CORONAL)) value = VIEW_CORONAL; else if (panel == (int) driver->GetDisplayWindowForAnatomicalDirection(ANATOMY_SAGITTAL)) value = VIEW_SAGITTAL; else value = VIEW_3D; } // Otherwise, the action is to return back to 4 views else { value = VIEW_ALL; } return true; } bool DisplayLayoutModel::GetSliceViewLayerTilingValue(Vector2ui &value) { value = m_LayerTiling; return true; } void DisplayLayoutModel::UpdateSliceViewTiling() { GenericImageData *id = m_ParentModel->GetDriver()->GetCurrentImageData(); // In stacked layout, there is only one layer to draw if(m_ParentModel->GetGlobalState()->GetSliceViewLayerLayout() == LAYOUT_STACKED) { m_LayerTiling.fill(1); } // Also, when there is no data, the layout is 1x1 else if(!m_ParentModel->GetDriver()->IsMainImageLoaded()) { m_LayerTiling.fill(1); } // Otherwise, we need to figure out in a smart way... But for now, we // should just use some default tiling scheme else { // Count the number of non-sticky layers, always counting main layer as 1 int n = 0; for(LayerIterator it = id->GetLayers(); !it.IsAtEnd(); ++it) { if(it.GetRole() == MAIN_ROLE || !it.GetLayer()->IsSticky()) n++; } // A simple algorithm to solve min(ab) s.t. ab >= n, k >= a/b >= 1, where // k is an aspect ratio value // TODO: replace this with a smart algorithm that uses current window size // and the amount of white space wasted in the currently visible views? m_LayerTiling.fill(n); double k = 2.01; for(unsigned int a = 1; a <= n; a++) { unsigned int b0 = (unsigned int)(ceil(a/k)); for(unsigned int b = b0; b <= a; b++) { if(a * b >= n) { if(a * b < m_LayerTiling[1] * m_LayerTiling[0]) { m_LayerTiling[1] = a; m_LayerTiling[0] = b; } } } } } } bool DisplayLayoutModel::GetNumberOfGroundLevelLayersValue(int &value) { if(!m_ParentModel->GetDriver()->IsMainImageLoaded()) return false; value = 0; GenericImageData *id = m_ParentModel->GetDriver()->GetCurrentImageData(); for(LayerIterator it = id->GetLayers(); !it.IsAtEnd(); ++it) { if(it.GetRole() == MAIN_ROLE || !it.GetLayer()->IsSticky()) value++; } return true; } bool DisplayLayoutModel::GetGroundLevelLayerIds(std::vector &ids) { if(!m_ParentModel->GetDriver()->IsMainImageLoaded()) return false; ids.clear(); GenericImageData *id = m_ParentModel->GetDriver()->GetCurrentImageData(); for(LayerIterator it = id->GetLayers(); !it.IsAtEnd(); ++it) { if(it.GetRole() == MAIN_ROLE || !it.GetLayer()->IsSticky()) ids.push_back(it.GetLayer()->GetUniqueId()); } return true; } void DisplayLayoutModel::OnUpdate() { // If there has been a layer change event, we need to recompute the tiling // model if(m_EventBucket->HasEvent(LayerChangeEvent()) || m_EventBucket->HasEvent(ValueChangedEvent(), m_ParentModel->GetGlobalState()->GetSliceViewLayerLayoutModel()) || m_EventBucket->HasEvent(WrapperVisibilityChangeEvent()) ) { this->UpdateSliceViewTiling(); } } itksnap-3.4.0/GUI/Model/DisplayLayoutModel.h000066400000000000000000000114321263013355200206110ustar00rootroot00000000000000#ifndef DISPLAYLAYOUTMODEL_H #define DISPLAYLAYOUTMODEL_H class GlobalUIModel; #include "PropertyModel.h" #include "GlobalState.h" /** * @brief A model that manages display layout properties. This model is * just a collection of specific properties */ class DisplayLayoutModel : public AbstractModel { public: // ITK macros irisITKObjectMacro(DisplayLayoutModel, AbstractModel) // Parent event type for all events fired by this model itkEventMacro(DisplayLayoutChangeEvent, IRISEvent) // Event fired when the layout of the slice panels changes itkEventMacro(ViewPanelLayoutChangeEvent, DisplayLayoutChangeEvent) // Event fired when the layout of the overlays in a slice panel changes itkEventMacro(LayerLayoutChangeEvent, DisplayLayoutChangeEvent) // This model fires a ModelUpdateEvent whenever there is a change in the // display layout FIRES(ViewPanelLayoutChangeEvent) FIRES(LayerLayoutChangeEvent) /** Layout of the SNAP slice views */ enum ViewPanelLayout { VIEW_ALL = 0, VIEW_AXIAL, VIEW_CORONAL, VIEW_SAGITTAL, VIEW_3D }; typedef AbstractPropertyModel AbstractViewPanelLayoutProperty; /** Get the parent model */ irisGetMacro(ParentModel, GlobalUIModel *) /** Assign the parent model */ void SetParentModel(GlobalUIModel *parentModel); /** Model managing the view panel layouts */ irisSimplePropertyAccessMacro(ViewPanelLayout, ViewPanelLayout) /** * Read-only boolean property models for the visibility of any specific * view panel (0-2 are slice windows, 3 is the 3D window). */ AbstractSimpleBooleanProperty *GetViewPanelVisibilityModel(int view) const { return m_ViewPanelVisibilityModel[view]; } /** * A model handing the layout of the layers in a slice view. This gives * the number of rows and columns into which the slice views are broken * up when displaying overlays. When this is 1x1, the overlays are painted * on top of each other using transparency. Otherwise, each overlay is shown * in its own cell. */ irisSimplePropertyAccessMacro(SliceViewLayerTiling, Vector2ui) /** * Model describing the number of 'ground-level' layers, i.e. layers that * are drawn on their own, rather than overlayed on other layers */ irisSimplePropertyAccessMacro(NumberOfGroundLevelLayers, int) /** * Get the list of ground level ids */ bool GetGroundLevelLayerIds(std::vector &ids); /** * Model describing the relative size of the thumbnails in thumbnail view, * in percent. */ irisRangedPropertyAccessMacro(ThumbnailRelativeSize, double) /** * A model for the layout of the layers in a slice view. This model sets * the stacked/tiled state. If the state is set to tiled, the number of * tiles will be updated as the number of loaded images changes. This model * actually just returns the model with the same name in the global state */ AbstractPropertyModel *GetSliceViewLayerLayoutModel() const; /** * Toggle the stack/tiled state */ void ToggleSliceViewLayerLayout(); /** * Activate next or previous layer */ void ActivateNextLayerInTiledMode(); void ActivatePrevLayerInTiledMode(); /** * Read-only boolean property models that dictate what icon should be * displayed in the expand/contract buttons in each slice view. There * are four of these models (one for each slice view), and they are of * the type ViewPanelLayout() */ AbstractViewPanelLayoutProperty *GetViewPanelExpandButtonActionModel(int view) const { return m_ViewPanelExpandButtonActionModel[view]; } protected: GlobalUIModel *m_ParentModel; typedef ConcretePropertyModel ConcreteViewPanelLayoutProperty; SmartPtr m_ViewPanelLayoutModel; SmartPtr m_ViewPanelVisibilityModel[4]; SmartPtr m_ViewPanelExpandButtonActionModel[4]; bool GetNthViewPanelVisibilityValue(int panel, bool &value); SmartPtr m_SliceViewLayerTilingModel; bool GetSliceViewLayerTilingValue(Vector2ui &value); bool GetNthViewPanelExpandButtonActionValue(int panel, ViewPanelLayout &value); // Update the tiling based on the number of layouts in memory void UpdateSliceViewTiling(); // The current tiling dimensons Vector2ui m_LayerTiling; // The number of 'ground-level' layers, i.e. layers that are drawn on their own // than overlayed on other layers SmartPtr m_NumberOfGroundLevelLayersModel; bool GetNumberOfGroundLevelLayersValue(int &value); // Thumbnail size SmartPtr m_ThumbnailRelativeSizeModel; virtual void OnUpdate(); DisplayLayoutModel(); virtual ~DisplayLayoutModel() {} }; #endif // DISPLAYLAYOUTMODEL_H itksnap-3.4.0/GUI/Model/Generic3DModel.cxx000066400000000000000000000300771263013355200201320ustar00rootroot00000000000000#include "Generic3DModel.h" #include "Generic3DRenderer.h" #include "GlobalUIModel.h" #include "IRISException.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "ImageWrapperBase.h" #include "MeshManager.h" #include "Window3DPicker.h" #include "vtkRenderWindow.h" #include "vtkRendererCollection.h" #include "vtkRenderWindowInteractor.h" #include "vtkPointData.h" #include "itkMutexLockHolder.h" #include "MeshOptions.h" // All the VTK stuff #include "vtkPolyData.h" #include Generic3DModel::Generic3DModel() { // Initialize the matrix to nil m_WorldMatrix.set_identity(); // Create the spray points vtkSmartPointer pts = vtkSmartPointer::New(); m_SprayPoints = vtkSmartPointer::New(); m_SprayPoints->SetPoints(pts); // Create the renderer m_Renderer = Generic3DRenderer::New(); // Continuous update model m_ContinuousUpdateModel = NewSimpleConcreteProperty(false); // Scalpel m_ScalpelStatus = SCALPEL_LINE_NULL; // Reset clear time m_ClearTime = 0; } #include "itkImage.h" void Generic3DModel::Initialize(GlobalUIModel *parent) { // Store the parent m_ParentUI = parent; m_Driver = parent->GetDriver(); // Update our geometry model OnImageGeometryUpdate(); // Initialize the renderer m_Renderer->SetModel(this); // Listen to the layer change events Rebroadcast(m_Driver, MainImageDimensionsChangeEvent(), ModelUpdateEvent()); // Listen to segmentation change events Rebroadcast(m_Driver, SegmentationChangeEvent(), StateMachineChangeEvent()); Rebroadcast(m_Driver, LevelSetImageChangeEvent(), StateMachineChangeEvent()); // Rebroadcast model change events as state changes Rebroadcast(this, ModelUpdateEvent(), StateMachineChangeEvent()); Rebroadcast(this, SprayPaintEvent(), StateMachineChangeEvent()); Rebroadcast(this, ScalpelEvent(), StateMachineChangeEvent()); Rebroadcast(m_ParentUI->GetGlobalState()->GetToolbarMode3DModel(), ValueChangedEvent(), StateMachineChangeEvent()); Rebroadcast(m_ParentUI->GetGlobalState()->GetMeshOptions(), ChildPropertyChangedEvent(), StateMachineChangeEvent()); } bool Generic3DModel::CheckState(Generic3DModel::UIState state) { if(!m_ParentUI->GetDriver()->IsMainImageLoaded()) return false; ToolbarMode3DType mode = m_ParentUI->GetGlobalState()->GetToolbarMode3D(); switch(state) { case UIF_MESH_DIRTY: { if(m_Driver->GetMeshManager()->IsMeshDirty()) return true; if(m_Driver->GetMeshManager()->GetBuildTime() <= this->m_ClearTime) return true; return false; } case UIF_MESH_ACTION_PENDING: { if(mode == SPRAYPAINT_MODE) return m_SprayPoints->GetNumberOfPoints() > 0; else if (mode == SCALPEL_MODE) return m_ScalpelStatus == SCALPEL_LINE_COMPLETED; else return false; } case UIF_CAMERA_STATE_SAVED: { return m_Renderer->IsSavedCameraStateAvailable(); } case UIF_FLIP_ENABLED: { return mode == SCALPEL_MODE && m_ScalpelStatus == SCALPEL_LINE_COMPLETED; } } return false; } Generic3DModel::Mat4d &Generic3DModel::GetWorldMatrix() { return m_WorldMatrix; } Vector3d Generic3DModel::GetCenterOfRotation() { return affine_transform_point(m_WorldMatrix, m_Driver->GetCursorPosition()); } void Generic3DModel::ResetView() { m_Renderer->ResetView(); } void Generic3DModel::SaveCameraState() { m_Renderer->SaveCameraState(); InvokeEvent(StateMachineChangeEvent()); } void Generic3DModel::RestoreCameraState() { m_Renderer->RestoreSavedCameraState(); } #include "MeshExportSettings.h" #include "vtkVRMLExporter.h" void Generic3DModel::ExportMesh(const MeshExportSettings &settings) { // Update the mesh this->UpdateSegmentationMesh(m_ParentUI->GetProgressCommand()); // Prevent concurrent access to this method and mesh update itk::MutexLockHolder mholder(m_MutexLock); // Certain formats require a VTK exporter and use a render window. They // are handled directly in this code, rather than in the Guided code. // TODO: it would make sense to unify this functionality in GuidedMeshIO GuidedMeshIO mesh_io; Registry reg_format = settings.GetMeshFormat(); if(mesh_io.GetFileFormat(reg_format) == GuidedMeshIO::FORMAT_VRML) { // Create the exporter vtkSmartPointer exporter = vtkSmartPointer::New(); exporter->SetFileName(settings.GetMeshFileName().c_str()); exporter->SetInput(m_Renderer->GetRenderWindow()); exporter->Update(); return; } // Export the mesh m_ParentUI->GetDriver()->ExportSegmentationMesh( settings, m_ParentUI->GetProgressCommand()); } vtkPolyData *Generic3DModel::GetSprayPoints() const { return m_SprayPoints.GetPointer(); } void Generic3DModel::OnUpdate() { // If we experienced a change in main image, we have to respond! if(m_EventBucket->HasEvent(MainImageDimensionsChangeEvent())) { // There is no more mesh to render - until the user does something! // m_Mesh->DiscardVTKMeshes(); // Clear the spray points m_SprayPoints->GetPoints()->Reset(); m_SprayPoints->Modified(); // The geometry has changed this->OnImageGeometryUpdate(); } } void Generic3DModel::OnImageGeometryUpdate() { // Update the world matrix and other stored variables if(m_Driver->IsMainImageLoaded()) { ImageWrapperBase *main = m_Driver->GetCurrentImageData()->GetMain(); m_WorldMatrix = main->GetNiftiSform(); m_WorldMatrixInverse = main->GetNiftiInvSform(); } else { m_WorldMatrix.set_identity(); m_WorldMatrixInverse.set_identity(); } } #include "itkMutexLockHolder.h" void Generic3DModel::UpdateSegmentationMesh(itk::Command *callback) { // Prevent concurrent access to this method itk::MutexLockHolder mholder(m_MutexLock); try { // Generate all the mesh objects m_MeshUpdating = true; m_Driver->GetMeshManager()->UpdateVTKMeshes(callback); m_MeshUpdating = false; InvokeEvent(ModelUpdateEvent()); } catch(vtkstd::bad_alloc &) { throw IRISException("Out of memory during mesh computation"); } catch(IRISException & IRISexc) { throw IRISexc; } } bool Generic3DModel::IsMeshUpdating() { return m_MeshUpdating; } bool Generic3DModel::AcceptAction() { ToolbarMode3DType mode = m_ParentUI->GetGlobalState()->GetToolbarMode3D(); IRISApplication *app = m_ParentUI->GetDriver(); // Accept the current action if(mode == SPRAYPAINT_MODE) { // Merge all the spray points into the segmentation app->BeginSegmentationUpdate("3D spray paint"); for(int i = 0; i < m_SprayPoints->GetNumberOfPoints(); i++) { double *x = m_SprayPoints->GetPoint(i); Vector3ui pos( static_cast(x[0]), static_cast(x[1]), static_cast(x[2])); app->UpdateSegmentationVoxel(pos); } // Clear the spray points m_SprayPoints->GetPoints()->Reset(); m_SprayPoints->Modified(); InvokeEvent(SprayPaintEvent()); // Return true if anything changed return app->EndSegmentationUpdate() > 0; } else if(mode == SCALPEL_MODE && m_ScalpelStatus == SCALPEL_LINE_COMPLETED) { // Get the plane origin and normal in world coordinates Vector3d xw = m_Renderer->GetScalpelPlaneOrigin(); Vector3d nw = m_Renderer->GetScalpelPlaneNormal(); // Map these properties into the image coordinates Vector3d xi = affine_transform_point(m_WorldMatrixInverse, xw); Vector3d ni = affine_transform_vector(m_WorldMatrixInverse, nw); // Use the driver to relabel the plane app->BeginSegmentationUpdate("3D scalpel"); app->RelabelSegmentationWithCutPlane(ni, dot_product(xi, ni)); int nMod = app->EndSegmentationUpdate(); // Reset the scalpel state, but only if the operation was successful if(nMod > 0) { m_ScalpelStatus = SCALPEL_LINE_NULL; InvokeEvent(ScalpelEvent()); return true; } else return false; } return true; } void Generic3DModel::CancelAction() { ToolbarMode3DType mode = m_ParentUI->GetGlobalState()->GetToolbarMode3D(); if(mode == SPRAYPAINT_MODE) { // Clear the spray points m_SprayPoints->GetPoints()->Reset(); m_SprayPoints->Modified(); InvokeEvent(SprayPaintEvent()); } else if(mode == SCALPEL_MODE && m_ScalpelStatus == SCALPEL_LINE_COMPLETED) { // Reset the scalpel state m_ScalpelStatus = SCALPEL_LINE_NULL; InvokeEvent(ScalpelEvent()); } } void Generic3DModel::FlipAction() { ToolbarMode3DType mode = m_ParentUI->GetGlobalState()->GetToolbarMode3D(); if(mode == SCALPEL_MODE && m_ScalpelStatus == SCALPEL_LINE_COMPLETED) { m_Renderer->FlipScalpelPlaneNormal(); } } void Generic3DModel::ClearRenderingAction() { m_Renderer->ClearRendering(); m_ClearTime = m_Driver->GetMeshManager()->GetBuildTime(); InvokeEvent(ModelUpdateEvent()); } #include "ImageRayIntersectionFinder.h" #include "SNAPImageData.h" /** These classes are used internally for m_Ray intersection testing */ class LabelImageHitTester { public: LabelImageHitTester(const ColorLabelTable *table = NULL) { m_LabelTable = table; } int operator()(LabelType label) const { const ColorLabel &cl = m_LabelTable->GetColorLabel(label); return (cl.IsVisible() && cl.IsVisibleIn3D()) ? 1 : 0; } private: const ColorLabelTable *m_LabelTable; }; class SnakeImageHitTester { public: int operator()(float levelSetValue) const { return levelSetValue <= 0 ? 1 : 0; } }; bool Generic3DModel::IntersectSegmentation(int vx, int vy, Vector3i &hit) { // World coordinate of the click position and direction Vector3d x_world, d_world; m_Renderer->ComputeRayFromClick(vx, vy, x_world, d_world); // Convert these to image coordinates Vector3d x_image = affine_transform_point(m_WorldMatrixInverse, x_world); Vector3d d_image = affine_transform_vector(m_WorldMatrixInverse, d_world); int result = 0; if(m_Driver->IsSnakeModeLevelSetActive()) { typedef ImageRayIntersectionFinder RayCasterType; RayCasterType caster; result = caster.FindIntersection( m_ParentUI->GetDriver()->GetSNAPImageData()->GetSnake()->GetImage(), x_image, d_image, hit); } else { typedef ImageRayIntersectionFinder RayCasterType; RayCasterType caster; LabelImageHitTester tester(m_ParentUI->GetDriver()->GetColorLabelTable()); caster.SetHitTester(tester); result = caster.FindIntersection( m_ParentUI->GetDriver()->GetCurrentImageData()->GetSegmentation()->GetImage(), x_image, d_image, hit); } return (result == 1); } bool Generic3DModel::PickSegmentationVoxelUnderMouse(int px, int py) { // Find the voxel under the cursor Vector3i hit; if(this->IntersectSegmentation(px, py, hit)) { Vector3ui cursor = to_unsigned_int(hit); itk::ImageRegion<3> region = m_Driver->GetCurrentImageData()->GetImageRegion(); if(region.IsInside(to_itkIndex(cursor))) { m_Driver->SetCursorPosition(cursor); return true; } } return false; } bool Generic3DModel::SpraySegmentationVoxelUnderMouse(int px, int py) { // Find the voxel under the cursor Vector3i hit; if(this->IntersectSegmentation(px, py, hit)) { itk::ImageRegion<3> region = m_Driver->GetCurrentImageData()->GetImageRegion(); if(region.IsInside(to_itkIndex(hit))) { m_SprayPoints->GetPoints()->InsertNextPoint(hit[0], hit[1], hit[2]); m_SprayPoints->Modified(); this->InvokeEvent(SprayPaintEvent()); return true; } } return false; } void Generic3DModel::SetScalpelStartPoint(int px, int py) { m_ScalpelEnd[0] = m_ScalpelStart[0] = px; m_ScalpelEnd[1] = m_ScalpelStart[1] = py; m_ScalpelStatus = SCALPEL_LINE_STARTED; this->InvokeEvent(ScalpelEvent()); } void Generic3DModel::SetScalpelEndPoint(int px, int py, bool complete) { m_ScalpelEnd[0] = px; m_ScalpelEnd[1] = py; if(complete) m_ScalpelStatus = SCALPEL_LINE_COMPLETED; this->InvokeEvent(ScalpelEvent()); } itksnap-3.4.0/GUI/Model/Generic3DModel.h000066400000000000000000000102601263013355200175470ustar00rootroot00000000000000#ifndef GENERIC3DMODEL_H #define GENERIC3DMODEL_H #include "AbstractModel.h" #include "PropertyModel.h" #include "vtkSmartPointer.h" #include "SNAPEvents.h" #include "itkMutexLock.h" class GlobalUIModel; class IRISApplication; class MeshManager; class Generic3DRenderer; class vtkPolyData; class MeshExportSettings; namespace itk { class Command; } class Generic3DModel : public AbstractModel { public: irisITKObjectMacro(Generic3DModel, AbstractModel) // Special events itkEventMacro(SprayPaintEvent, IRISEvent) itkEventMacro(ScalpelEvent, IRISEvent) FIRES(SprayPaintEvent) FIRES(ScalpelEvent) FIRES(StateMachineChangeEvent) // Matrix for various transforms typedef vnl_matrix_fixed Mat4d; Generic3DModel(); // States pertaining to this model enum UIState { UIF_MESH_DIRTY = 0, UIF_MESH_ACTION_PENDING, UIF_CAMERA_STATE_SAVED, UIF_FLIP_ENABLED }; // State of scalpel drawging enum ScalpelStatus { SCALPEL_LINE_NULL = 0, SCALPEL_LINE_STARTED, SCALPEL_LINE_COMPLETED }; // Set the parent model void Initialize(GlobalUIModel *parent); // Check the state bool CheckState(UIState state); // A flag indicating that the mesh should be continually updated // TODO: replace this with an update in a background thread irisSimplePropertyAccessMacro(ContinuousUpdate, bool) // Tell the model to update the segmentation mesh void UpdateSegmentationMesh(itk::Command *callback); // Reentrant function to check if mesh is being constructed in another thread bool IsMeshUpdating(); // Accept the current drawing operation bool AcceptAction(); // Cancel the current drawing operation void CancelAction(); // Flip the direction of the normal for the scalpel operation void FlipAction(); // Clear the rendering void ClearRenderingAction(); // Position cursor at the screen position under the cursor bool PickSegmentationVoxelUnderMouse(int px, int py); // Add a spraypaint bubble at the screen position under the cursor bool SpraySegmentationVoxelUnderMouse(int px, int py); // Set the endpoints of the scalpel line void SetScalpelStartPoint(int px, int py); irisGetMacro(ScalpelStart, Vector2i) // Set the endpoints of the scalpel line void SetScalpelEndPoint(int px, int py, bool complete); irisGetMacro(ScalpelEnd, Vector2i) irisGetSetMacro(ScalpelStatus, ScalpelStatus) // Get the parent model irisGetMacro(ParentUI, GlobalUIModel *) // Get the renderer irisGetMacro(Renderer, Generic3DRenderer *) // Get the transform from image space to world coordinates Mat4d &GetWorldMatrix(); // Get the center of rotation for the 3D window Vector3d GetCenterOfRotation(); // Reset the viewpoint void ResetView(); // Save the camera state void SaveCameraState(); // Restore the camera state void RestoreCameraState(); // Export the 3D model void ExportMesh(const MeshExportSettings &settings); // Get the spray points vtkPolyData *GetSprayPoints() const; protected: // Respond to updates void OnUpdate(); // Do this when main image geometry has changed void OnImageGeometryUpdate(); // Find the labeled voxel under the cursor bool IntersectSegmentation(int vx, int vy, Vector3i &hit); // Parent (where the global UI state is stored) GlobalUIModel *m_ParentUI; // Renderer SmartPtr m_Renderer; // Helps to have a pointer to the iris application IRISApplication *m_Driver; // World matrix - a copy of the NIFTI transform in the main image, // updated on the event main image changes Mat4d m_WorldMatrix, m_WorldMatrixInverse; // Set of spraypainted points in image coordinates vtkSmartPointer m_SprayPoints; // On-screen endpoints of the scalpel line Vector2i m_ScalpelStart, m_ScalpelEnd; // State of the scalpel drawing ScalpelStatus m_ScalpelStatus; // Continuous update model SmartPtr m_ContinuousUpdateModel; // Is the mesh updating bool m_MeshUpdating; // Time of the last mesh clear operation unsigned long m_ClearTime; // A mutex lock to allow background processing of mesh updates itk::SimpleFastMutexLock m_MutexLock; }; #endif // GENERIC3DMODEL_H itksnap-3.4.0/GUI/Model/GenericSliceModel.cxx000066400000000000000000000646541263013355200207330ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #include #include #include #include #include #include #include #include #include GenericSliceModel::GenericSliceModel() { // Copy parent pointers m_ParentUI = NULL; m_Driver = NULL; // Set the window ID m_Id = -1; // Initalize the margin m_Margin = 2; // Initialize the zoom management m_ManagedZoom = false; // The slice is not yet initialized m_SliceInitialized = false; // Viewport size reporter is NULL m_SizeReporter = NULL; // Create submodels m_SliceIndexModel = wrapGetterSetterPairAsProperty( this, &Self::GetSliceIndexValueAndDomain, &Self::SetSlideIndexValue); m_CurrentComponentInSelectedLayerModel = wrapGetterSetterPairAsProperty( this, &Self::GetCurrentComponentInSelectedLayerValueAndDomain, &Self::SetCurrentComponentInSelectedLayerValue); // Nothing is hovered over m_HoveredImageLayerIdModel = NewSimpleConcreteProperty(-1ul); m_HoveredImageIsThumbnailModel = NewSimpleConcreteProperty(false); } void GenericSliceModel::Initialize(GlobalUIModel *model, int index) { // Copy parent pointers m_ParentUI = model; m_Driver = model->GetDriver(); // Set the window ID m_Id = index; // The slice is not yet initialized m_SliceInitialized = false; // Listen to events that require action from this object Rebroadcast(m_Driver, LayerChangeEvent(), ModelUpdateEvent()); // Listen to changes in the layout of the slice view into cells. When // this change occurs, we have to modify the size of the slice views DisplayLayoutModel *dlm = m_ParentUI->GetDisplayLayoutModel(); Rebroadcast(dlm, DisplayLayoutModel::LayerLayoutChangeEvent(), ModelUpdateEvent()); // Listen to cursor update events and rebroadcast them for the child model m_SliceIndexModel->Rebroadcast(model, CursorUpdateEvent(), ValueChangedEvent()); // Also listen for changes in the selected layer AbstractSimpleULongProperty *selLayerModel = m_Driver->GetGlobalState()->GetSelectedLayerIdModel(); Rebroadcast(selLayerModel, ValueChangedEvent(), ModelUpdateEvent()); // The current component in selected layer model depends both on the selected model // and on the layer metadata changes m_CurrentComponentInSelectedLayerModel->Rebroadcast(selLayerModel, ValueChangedEvent(), DomainChangedEvent()); m_CurrentComponentInSelectedLayerModel->Rebroadcast(m_Driver, WrapperMetadataChangeEvent(), DomainChangedEvent()); m_CurrentComponentInSelectedLayerModel->Rebroadcast(model, LayerChangeEvent(), DomainChangedEvent()); } void GenericSliceModel ::SetSizeReporter(ViewportSizeReporter *reporter) { m_SizeReporter = reporter; // Rebroadcast the events from the reporter downstream to force an update Rebroadcast(m_SizeReporter, ViewportSizeReporter::ViewportResizeEvent(), ModelUpdateEvent()); // We also rebroadcast as a special type of event that the slice coordinator // is going to listen to Rebroadcast(m_SizeReporter, ViewportSizeReporter::ViewportResizeEvent(), ViewportResizeEvent()); } void GenericSliceModel::OnUpdate() { // Has there been a change in the image dimensions? if(m_EventBucket->HasEvent(MainImageDimensionsChangeEvent())) { // Do a complete initialization this->InitializeSlice(m_Driver->GetCurrentImageData()); } // TODO: what is the ValueChangeEvent here??? else if(m_EventBucket->HasEvent(ViewportSizeReporter::ViewportResizeEvent()) || m_EventBucket->HasEvent(DisplayLayoutModel::LayerLayoutChangeEvent()) || m_EventBucket->HasEvent(ValueChangedEvent())) { // Recompute the viewport layout and dimensions this->UpdateViewportLayout(); // We only react to the viewport resize if the zoom is not managed by the // coordinator. When zoom is managed, the coordinator will take care of // computing the optimal zoom and resetting the view if(this->IsSliceInitialized() && !m_ManagedZoom) { // Check if the zoom should be changed in response to this operation. This // is so if the zoom is currently equal to the optimal zoom, and there is // no linked zoom bool rezoom = (m_ViewZoom == m_OptimalZoom); // Just recompute the optimal zoom factor this->ComputeOptimalZoom(); // Keep zoom optimal if before it was optimal if(rezoom) this->SetViewZoom(m_OptimalZoom); } } } void GenericSliceModel::ComputeOptimalZoom() { // Should be fully initialized assert(IsSliceInitialized()); // Compute slice size in spatial coordinates Vector2f worldSize( m_SliceSize[0] * m_SliceSpacing[0], m_SliceSize[1] * m_SliceSpacing[1]); // Set the view position (position of the center of the image?) m_OptimalViewPosition = worldSize * 0.5f; // Reduce the width and height of the slice by the margin Vector2ui szCanvas = this->GetCanvasSize(); // Compute the ratios of window size to slice size Vector2f ratios( szCanvas(0) / worldSize(0), szCanvas(1) / worldSize(1)); // The zoom factor is the bigger of these ratios, the number of pixels // on the screen per millimeter in world space m_OptimalZoom = ratios.min_value(); } GenericSliceModel ::~GenericSliceModel() { } void GenericSliceModel ::InitializeSlice(GenericImageData *imageData) { // Store the image data pointer m_ImageData = imageData; // Quit if there is no image loaded if (!m_ImageData->IsMainLoaded()) { m_SliceInitialized = false; return; } // Store the transforms between the display and image spaces m_ImageToDisplayTransform = imageData->GetImageGeometry().GetImageToDisplayTransform(m_Id); m_DisplayToImageTransform = imageData->GetImageGeometry().GetDisplayToImageTransform(m_Id); m_DisplayToAnatomyTransform = imageData->GetImageGeometry().GetAnatomyToDisplayTransform(m_Id).Inverse(); // Get the volume extents & voxel scale factors Vector3ui imageSizeInImageSpace = m_ImageData->GetVolumeExtents(); Vector3f imageScalingInImageSpace = to_float(m_ImageData->GetImageSpacing()); // Initialize quantities that depend on the image and its transform for(unsigned int i = 0; i < 3; i++) { // Get the direction in image space that corresponds to the i'th // direction in slice space m_ImageAxes[i] = m_DisplayToImageTransform.GetCoordinateIndexZeroBased(i); // Record the size and scaling of the slice m_SliceSize[i] = imageSizeInImageSpace[m_ImageAxes[i]]; m_SliceSpacing[i] = imageScalingInImageSpace[m_ImageAxes[i]]; // TODO: Reverse sign by orientation? } // We have been initialized m_SliceInitialized = true; // Update the viewport dimensions UpdateViewportLayout(); // Compute the optimal zoom for this slice ComputeOptimalZoom(); // Fire a modified event, forcing a repaint of the window InvokeEvent(ModelUpdateEvent()); } Vector2i GenericSliceModel ::GetOptimalCanvasSize() { // Compute slice size in spatial coordinates Vector2i optSize( (int) ceil(m_SliceSize[0] * m_SliceSpacing[0] * m_ViewZoom + 2 * m_Margin), (int) ceil(m_SliceSize[1] * m_SliceSpacing[1] * m_ViewZoom + 2 * m_Margin)); return optSize; } void GenericSliceModel ::ResetViewToFit() { // Should be fully initialized assert(IsSliceInitialized()); // The zoom factor is the bigger of these ratios, the number of pixels // on the screen per millimeter in world space SetViewZoom(m_OptimalZoom); SetViewPosition(m_OptimalViewPosition); } Vector3f GenericSliceModel ::MapSliceToImage(const Vector3f &xSlice) { assert(IsSliceInitialized()); // Get corresponding position in display space return m_DisplayToImageTransform.TransformPoint(xSlice); } /** * Map a point in image coordinates to slice coordinates */ Vector3f GenericSliceModel ::MapImageToSlice(const Vector3f &xImage) { assert(IsSliceInitialized()); // Get corresponding position in display space return m_ImageToDisplayTransform.TransformPoint(xImage); } Vector2f GenericSliceModel ::MapSliceToWindow(const Vector3f &xSlice) { assert(IsSliceInitialized()); // Adjust the slice coordinates by the scaling amounts Vector2f uvScaled( xSlice(0) * m_SliceSpacing(0),xSlice(1) * m_SliceSpacing(1)); // Compute the window coordinates Vector2ui size = this->GetCanvasSize(); Vector2f uvWindow = m_ViewZoom * (uvScaled - m_ViewPosition) + Vector2f(0.5f * size[0], 0.5f * size[1]); // That's it, the projection matrix is set up in the scaled-slice coordinates return uvWindow; } Vector3f GenericSliceModel ::MapWindowToSlice(const Vector2f &uvWindow) { assert(IsSliceInitialized() && m_ViewZoom > 0); // Compute the scaled slice coordinates Vector2ui size = this->GetCanvasSize(); Vector2f winCenter(0.5f * size[0],0.5f * size[1]); Vector2f uvScaled = m_ViewPosition + (uvWindow - winCenter) / m_ViewZoom; // The window coordinates are already in the scaled-slice units Vector3f uvSlice( uvScaled(0) / m_SliceSpacing(0), uvScaled(1) / m_SliceSpacing(1), this->GetCursorPositionInSliceCoordinates()[2]); // Return this vector return uvSlice; } Vector3f GenericSliceModel ::MapWindowOffsetToSliceOffset(const Vector2f &uvWindowOffset) { assert(IsSliceInitialized() && m_ViewZoom > 0); Vector2f uvScaled = uvWindowOffset / m_ViewZoom; // The window coordinates are already in the scaled-slice units Vector3f uvSlice( uvScaled(0) / m_SliceSpacing(0), uvScaled(1) / m_SliceSpacing(1), 0); // Return this vector return uvSlice; } Vector2f GenericSliceModel ::MapSliceToPhysicalWindow(const Vector3f &xSlice) { assert(IsSliceInitialized()); // Compute the physical window coordinates Vector2f uvPhysical; uvPhysical[0] = xSlice[0] * m_SliceSpacing[0]; uvPhysical[1] = xSlice[1] * m_SliceSpacing[1]; return uvPhysical; } Vector3f GenericSliceModel ::MapPhysicalWindowToSlice(const Vector2f &uvPhysical) { assert(IsSliceInitialized()); Vector3f xSlice; xSlice[0] = uvPhysical[0] / m_SliceSpacing[0]; xSlice[1] = uvPhysical[1] / m_SliceSpacing[1]; xSlice[2] = this->GetCursorPositionInSliceCoordinates()[2]; return xSlice; } void GenericSliceModel ::ResetViewPosition() { // Compute slice size in spatial coordinates Vector2f worldSize( m_SliceSize[0] * m_SliceSpacing[0], m_SliceSize[1] * m_SliceSpacing[1]); // Set the view position (position of the center of the image?) m_ViewPosition = worldSize * 0.5f; // Update view InvokeEvent(SliceModelGeometryChangeEvent()); } void GenericSliceModel ::SetViewPositionRelativeToCursor(Vector2f offset) { // Get the crosshair position Vector3ui xCursorInteger = m_Driver->GetCursorPosition(); // Shift the cursor position by by 0.5 in order to have it appear // between voxels Vector3f xCursorImage = to_float(xCursorInteger) + Vector3f(0.5f); // Get the cursor position on the slice Vector3f xCursorSlice = MapImageToSlice(xCursorImage); // Subtract from the view position Vector2f vp; vp[0] = offset[0] + xCursorSlice[0] * m_SliceSpacing[0]; vp[1] = offset[1] + xCursorSlice[1] * m_SliceSpacing[1]; SetViewPosition(vp); } Vector2f GenericSliceModel ::GetViewPositionRelativeToCursor() { // Get the crosshair position Vector3ui xCursorInteger = m_Driver->GetCursorPosition(); // Shift the cursor position by by 0.5 in order to have it appear // between voxels Vector3f xCursorImage = to_float(xCursorInteger) + Vector3f(0.5f); // Get the cursor position on the slice Vector3f xCursorSlice = MapImageToSlice(xCursorImage); // Subtract from the view position Vector2f offset; offset[0] = m_ViewPosition[0] - xCursorSlice[0] * m_SliceSpacing[0]; offset[1] = m_ViewPosition[1] - xCursorSlice[1] * m_SliceSpacing[1]; return offset; } void GenericSliceModel::CenterViewOnCursor() { Vector2f offset; offset.fill(0.0f); this->SetViewPositionRelativeToCursor(offset); } void GenericSliceModel::SetViewZoom(float zoom) { assert(zoom > 0); m_ViewZoom = zoom; this->Modified(); this->InvokeEvent(SliceModelGeometryChangeEvent()); } void GenericSliceModel::ZoomInOrOut(float factor) { float oldzoom = m_ViewZoom; float newzoom = oldzoom * factor; if( (oldzoom < m_OptimalZoom && newzoom > m_OptimalZoom) || (oldzoom > m_OptimalZoom && newzoom < m_OptimalZoom) ) { newzoom = m_OptimalZoom; } SetViewZoom(newzoom); } /* GenericSliceModel * GenericSliceModel ::GenericSliceModel() { SliceWindowCoordinator *swc = m_ParentUI->GetSliceCoordinator(); return swc->GetWindow( (m_Id+1) % 3); } */ bool GenericSliceModel ::IsThumbnailOn() { const GlobalDisplaySettings *gds = m_ParentUI->GetGlobalDisplaySettings(); return gds->GetFlagDisplayZoomThumbnail() && (m_ViewZoom > m_OptimalZoom); } const SliceViewportLayout::SubViewport *GenericSliceModel::GetHoveredViewport() { for(int i = 0; i < m_ViewportLayout.vpList.size(); i++) { const SliceViewportLayout::SubViewport *vpcand = &m_ViewportLayout.vpList[i]; if(vpcand->layer_id == this->GetHoveredImageLayerId() && (vpcand->isThumbnail == this->GetHoveredImageIsThumbnail())) return vpcand; } return NULL; } Vector3f GenericSliceModel::GetCursorPositionInSliceCoordinates() { Vector3ui cursorImageSpace = m_Driver->GetCursorPosition(); Vector3f cursorDisplaySpace = m_ImageToDisplayTransform.TransformPoint( to_float(cursorImageSpace) + Vector3f(0.5f)); return cursorDisplaySpace; } unsigned int GenericSliceModel::GetSliceIndex() { Vector3ui cursorImageSpace = m_Driver->GetCursorPosition(); return cursorImageSpace[m_ImageAxes[2]]; } void GenericSliceModel::UpdateSliceIndex(unsigned int newIndex) { Vector3ui cursorImageSpace = m_Driver->GetCursorPosition(); cursorImageSpace[m_ImageAxes[2]] = newIndex; m_Driver->SetCursorPosition(cursorImageSpace); } void GenericSliceModel::ComputeThumbnailProperties() { // Get the global display settings const GlobalDisplaySettings *gds = m_ParentUI->GetGlobalDisplaySettings(); // The thumbnail will occupy a specified fraction of the target canvas float xFraction = 0.01f * gds->GetZoomThumbnailSizeInPercent(); // But it must not exceed a predefined size in pixels in either dimension float xThumbMax = gds->GetZoomThumbnailMaximumSize(); // Recompute the fraction based on maximum size restriction Vector2ui size = this->GetCanvasSize(); float xNewFraction = xFraction; if( size[0] * xNewFraction > xThumbMax ) xNewFraction = xThumbMax * 1.0f / size[0]; if( size[1] * xNewFraction > xThumbMax ) xNewFraction = xThumbMax * 1.0f / size[1]; // Set the position and size of the thumbnail, in pixels m_ThumbnailZoom = xNewFraction * m_OptimalZoom; m_ZoomThumbnailPosition.fill(5); m_ZoomThumbnailSize[0] = (int)(m_SliceSize[0] * m_SliceSpacing[0] * m_ThumbnailZoom); m_ZoomThumbnailSize[1] = (int)(m_SliceSize[1] * m_SliceSpacing[1] * m_ThumbnailZoom); } unsigned int GenericSliceModel::GetNumberOfSlices() const { return m_SliceSize[2]; } /* void GenericSliceModel::OnSourceDataUpdate() { this->InitializeSlice(m_Driver->GetCurrentImageData()); } */ void GenericSliceModel::SetViewPosition(Vector2f pos) { if(m_ViewPosition != pos) { m_ViewPosition = pos; InvokeEvent(SliceModelGeometryChangeEvent()); } } /* GenericSliceWindow::EventHandler ::EventHandler(GenericSliceWindow *parent) : InteractionMode(parent->GetCanvas()) { m_Parent = parent; } void GenericSliceWindow::EventHandler ::Register() { m_Driver = m_Parent->m_Driver; m_ParentUI = m_Parent->m_ParentUI; m_GlobalState = m_Parent->m_GlobalState; } #include int GenericSliceWindow::OnDragAndDrop(const FLTKEvent &event) { // Check if it is a real file if(event.Id == FL_PASTE) { if(itksys::SystemTools::FileExists(Fl::event_text(), true)) { m_ParentUI->OpenDraggedContent(Fl::event_text(), true); return 1; } return 0; } else return 1; } */ unsigned int GenericSliceModel ::MergeSliceSegmentation(itk::Image *drawing) { // Z position of slice float zpos = this->GetCursorPositionInSliceCoordinates()[2]; return m_Driver->UpdateSegmentationWithSliceDrawing( drawing, m_DisplayToImageTransform, zpos, "Polygon Drawing"); } Vector2ui GenericSliceModel::GetSize() { Vector2ui viewport = m_SizeReporter->GetViewportSize(); DisplayLayoutModel *dlm = m_ParentUI->GetDisplayLayoutModel(); Vector2ui layout = dlm->GetSliceViewLayerTilingModel()->GetValue(); unsigned int rows = layout[0], cols = layout[1]; return Vector2ui(viewport[0] / cols, viewport[1] / rows); } Vector2ui GenericSliceModel::GetCanvasSize() { assert(m_ViewportLayout.vpList.size() > 0); assert(!m_ViewportLayout.vpList.front().isThumbnail); return m_ViewportLayout.vpList.front().size; } void GenericSliceModel::GetNonThumbnailViewport(Vector2ui &pos, Vector2ui &size) { // Initialize to the entire view pos.fill(0); size = m_SizeReporter->GetViewportSize(); DisplayLayoutModel *dlm = this->GetParentUI()->GetDisplayLayoutModel(); LayerLayout tiling = dlm->GetSliceViewLayerLayoutModel()->GetValue(); // Are thumbnails being used? // TODO: this should be done through a state variable if(tiling == LAYOUT_STACKED && dlm->GetNumberOfGroundLevelLayersModel()->GetValue() > 1) { for(int i = 0; i < m_ViewportLayout.vpList.size(); i++) { const SliceViewportLayout::SubViewport &sv = m_ViewportLayout.vpList[i]; if(!sv.isThumbnail) { pos = sv.pos; size = sv.size; break; } } } } ImageWrapperBase *GenericSliceModel::GetThumbnailedLayerAtPosition(int x, int y) { bool isThumb; ImageWrapperBase *layer = this->GetContextLayerAtPosition(x, y, isThumb); return isThumb ? layer : NULL; } ImageWrapperBase *GenericSliceModel::GetContextLayerAtPosition(int x, int y, bool &outIsThumbnail) { x *= m_SizeReporter->GetViewportPixelRatio(); y *= m_SizeReporter->GetViewportPixelRatio(); for(int i = 0; i < m_ViewportLayout.vpList.size(); i++) { const SliceViewportLayout::SubViewport &sv = m_ViewportLayout.vpList[i]; if(x >= sv.pos[0] && y >= sv.pos[1] && x < sv.pos[0] + sv.size[0] && y < sv.pos[1] + sv.size[1]) { outIsThumbnail = sv.isThumbnail; return m_Driver->GetCurrentImageData()->FindLayer(sv.layer_id, false); } } return NULL; } bool GenericSliceModel ::GetSliceIndexValueAndDomain(int &value, NumericValueRange *domain) { if(!m_Driver->IsMainImageLoaded()) return false; value = this->GetSliceIndex(); if(domain) { domain->Set(0, this->GetNumberOfSlices()-1, 1); } return true; } void GenericSliceModel::SetSlideIndexValue(int value) { this->UpdateSliceIndex(value); } bool GenericSliceModel ::GetCurrentComponentInSelectedLayerValueAndDomain( unsigned int &value, NumericValueRange *domain) { // Make sure there is a layer selected and it's a multi-component layer if(!m_Driver->IsMainImageLoaded()) return false; ImageWrapperBase *layer = m_Driver->GetCurrentImageData()->FindLayer( m_Driver->GetGlobalState()->GetSelectedLayerId(), false); if(!layer || layer->GetNumberOfComponents() <= 1) return false; // Make sure the display mode is to scroll through components AbstractMultiChannelDisplayMappingPolicy *dpolicy = static_cast(layer->GetDisplayMapping()); // Get the current display mode MultiChannelDisplayMode mode = dpolicy->GetDisplayMode(); // Mode must be single component if(!mode.IsSingleComponent()) return false; // Finally we can return a value and range value = mode.SelectedComponent; if(domain) domain->Set(0, layer->GetNumberOfComponents()-1, 1); return true; } void GenericSliceModel::SetCurrentComponentInSelectedLayerValue(unsigned int value) { // Get the target layer ImageWrapperBase *layer = m_Driver->GetCurrentImageData()->FindLayer( m_Driver->GetGlobalState()->GetSelectedLayerId(), false); assert(layer); // Get the target policy AbstractMultiChannelDisplayMappingPolicy *dpolicy = static_cast(layer->GetDisplayMapping()); MultiChannelDisplayMode mode = dpolicy->GetDisplayMode(); assert(mode.IsSingleComponent()); // Update the mode mode.SelectedComponent = value; dpolicy->SetDisplayMode(mode); } void GenericSliceModel::UpdateViewportLayout() { // Get the information about how the viewport is split into sub-viewports DisplayLayoutModel *dlm = this->GetParentUI()->GetDisplayLayoutModel(); Vector2ui layout = dlm->GetSliceViewLayerTilingModel()->GetValue(); int nrows = (int) layout[0]; int ncols = (int) layout[1]; // Number of ground-level layers - together with the tiling, this determines // the behavior of the display int n_base_layers = dlm->GetNumberOfGroundLevelLayersModel()->GetValue(); // Get the dimensions of the main viewport (in real pixels, not logical pixels) unsigned int w = m_SizeReporter->GetViewportSize()[0]; unsigned int h = m_SizeReporter->GetViewportSize()[1]; // Get the current image data GenericImageData *id = this->GetDriver()->GetCurrentImageData(); // Clear the viewport array m_ViewportLayout.vpList.clear(); // Is there anything to do? if(!this->GetDriver()->IsMainImageLoaded()) return; // Is tiling being used if(nrows == 1 && ncols == 1) { // There is no tiling. One base layer is emphasized if(n_base_layers == 1) { // There is only one base layer (main). It's viewport occupies the whole screen SliceViewportLayout::SubViewport vp; vp.pos = Vector2ui(0, 0); vp.size = Vector2ui(w, h); vp.isThumbnail = false; vp.layer_id = id->GetMain()->GetUniqueId(); m_ViewportLayout.vpList.push_back(vp); } else { // We are in thumbnail mode. Draw the selected layer big and all the ground-level // layers small, as thumbnails. unsigned int margin = 4; // The preferred width of the thumbnails (without margin) int k = n_base_layers; // This is a complicated calculation to make sure it all fits double max_thumb_size = dlm->GetThumbnailRelativeSize() / 100.0; double thumb_wd = std::min(max_thumb_size * w - 2 * margin, (h - (1.0 + k) * margin) * (w - 2.0 * margin) / ((h - margin) * (1.0 + k))); double thumb_hd = h * thumb_wd / (w - thumb_wd - 2.0 * margin); // Round down the thumb sizes unsigned int thumb_w = (unsigned int) thumb_wd; unsigned int thumb_h = (unsigned int) thumb_hd; // Set the bottom of the first thumbnail unsigned int thumb_y = h - thumb_h - margin + 1; // Go through eligible layers for(LayerIterator it = id->GetLayers(); !it.IsAtEnd(); ++it) { if(it.GetRole() == MAIN_ROLE || !it.GetLayer()->IsSticky()) { // Is this the visible layer? if(this->GetDriver()->GetGlobalState()->GetSelectedLayerId() == it.GetLayer()->GetUniqueId()) { SliceViewportLayout::SubViewport vp; vp.layer_id = it.GetLayer()->GetUniqueId(); vp.pos = Vector2ui(0, 0); vp.size = Vector2ui(w - thumb_w - 2 * margin, h); vp.isThumbnail = false; // Notice we are sticking this viewport in the beginning! It's primary. m_ViewportLayout.vpList.insert(m_ViewportLayout.vpList.begin(), vp); } // Either way, add the layer to the thumbnail region SliceViewportLayout::SubViewport vp; vp.layer_id = it.GetLayer()->GetUniqueId(); vp.pos = Vector2ui(w - thumb_w - margin, thumb_y); vp.size = Vector2ui(thumb_w, thumb_h); vp.isThumbnail = true; m_ViewportLayout.vpList.push_back(vp); thumb_y -= thumb_h + margin; } } } } else { float cell_w = w / ncols; float cell_h = h / nrows; for(int irow = 0; irow < nrows; irow++) for(int icol = 0; icol < ncols; icol++) if(m_ViewportLayout.vpList.size() < n_base_layers) { SliceViewportLayout::SubViewport vp; vp.pos = Vector2ui(icol * cell_w, (nrows - 1 - irow) * cell_h); vp.size = Vector2ui(cell_w, cell_h); vp.isThumbnail = false; vp.layer_id = this->GetLayerForNthTile(irow, icol)->GetUniqueId(); m_ViewportLayout.vpList.push_back(vp); } } } ImageWrapperBase *GenericSliceModel::GetLayerForNthTile(int row, int col) { // Number of divisions DisplayLayoutModel *dlm = this->GetParentUI()->GetDisplayLayoutModel(); Vector2ui layout = dlm->GetSliceViewLayerTilingModel()->GetValue(); int nrows = (int) layout[0], ncols = (int) layout[1]; // This code is used if the layout is actually tiled if(ncols > 1 || nrows > 1) { // How many layers to go until we get to the one we want to paint? int togo = row * ncols + col; // Skip all layers until we get to the sticky layer we want to paint for(LayerIterator it(this->GetImageData()); !it.IsAtEnd(); ++it) { if(it.GetRole() == MAIN_ROLE || !it.GetLayer()->IsSticky()) { if(togo == 0) return it.GetLayer(); togo--; } } } else { for(LayerIterator it(this->GetImageData()); !it.IsAtEnd(); ++it) { if(it.GetLayer() && it.GetLayer()->GetUniqueId() == this->GetDriver()->GetGlobalState()->GetSelectedLayerId()) { return it.GetLayer(); } } } return NULL; } itksnap-3.4.0/GUI/Model/GenericSliceModel.h000066400000000000000000000326541263013355200203530ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef GENERICSLICEMODEL_H #define GENERICSLICEMODEL_H #include #include #include #include #include "AbstractModel.h" #include "ImageWrapper.h" #include "UIReporterDelegates.h" #include "PropertyModel.h" class GlobalUIModel; class IRISApplication; class GenericImageData; class GenericSliceModel; // An event fired when the geometry of the slice view changes itkEventMacro(SliceModelImageDimensionsChangeEvent, IRISEvent) itkEventMacro(SliceModelGeometryChangeEvent, IRISEvent) /** * A structure describing viewport organization in a SNAP slice view. * The viewport can be in a tiled state or in a main/thumbnail state. * * This class contains a list of viewports with corresponding IDs, as * well as the 'primary' viewport, based on which zoom computations are * made. The primary viewport is always the first viewport in the list */ struct SliceViewportLayout { public: struct SubViewport { // Size and position of the viewport Vector2ui pos, size; // Index of the associated image layer unsigned long layer_id; // Whether this is a thumbnail sub-view or a primary view bool isThumbnail; }; // List of subviewports std::vector vpList; }; /** \class GenericSliceModel \brief Describes the state of the slice panel showing an orthogonal projection of a dataset in ITK-SNAP This class holds the state of the slice viewer widget. It contains information about the slice currently being shown, the mapping of coordinates between slice space and image space, and other bits of information. Ideally, you should be able to store and retrieve the state of this object between sessions. This class is meant to be used with arbitrary GUI environments. It is unaware of Qt, FLTK, etc. */ class GenericSliceModel : public AbstractModel { public: irisITKObjectMacro(GenericSliceModel, AbstractModel) itkEventMacro(ViewportResizeEvent, IRISEvent) FIRES(ModelUpdateEvent) FIRES(SliceModelGeometryChangeEvent) FIRES(ViewportResizeEvent) // irisDeclareEventObserver(ReinitializeEvent) /** * Initializer: takes global UI model and slice ID as input */ void Initialize(GlobalUIModel *model, int index); /** Set the viewport reporter (and listen to viewport events) */ void SetSizeReporter(ViewportSizeReporter *reporter); /** Get viewport reporter */ irisGetMacro(SizeReporter, ViewportSizeReporter *) /** Update if necessary */ virtual void OnUpdate(); /** * Initialize the slice view with image data */ void InitializeSlice(GenericImageData *data); /** * Reset the view parameters of the window (zoom, view position) to * defaults */ virtual void ResetViewToFit(); /** * Map a point in window coordinates to a point in slice coordinates * (Window coordinates are the ones stored in FLTKEvent.xSpace) */ Vector3f MapWindowToSlice(const Vector2f &xWindow); /** * Map an offset in window coordinates to an offset in slice coordinates */ Vector3f MapWindowOffsetToSliceOffset(const Vector2f &xWindowOffset); /** * Map a point in slice coordinates to a point in window coordinates * (Window coordinates are the ones stored in FLTKEvent.xSpace) */ Vector2f MapSliceToWindow(const Vector3f &xSlice); /** * Map a point in slice coordinates to a point in PHYISCAL window coordinates */ Vector2f MapSliceToPhysicalWindow(const Vector3f &xSlice); /** * Map a point in PHYISCAL window coordinates to a point in slice coordinates */ Vector3f MapPhysicalWindowToSlice(const Vector2f &uvPhysical); /** * Map a point in slice coordinates to a point in the image coordinates */ Vector3f MapSliceToImage(const Vector3f &xSlice); /** * Map a point in image coordinates to slice coordinates */ Vector3f MapImageToSlice(const Vector3f &xImage); /** * Get the cursor position in slice coordinates, shifted to the center * of the voxel */ Vector3f GetCursorPositionInSliceCoordinates(); /** * Get the slice index for this window */ unsigned int GetSliceIndex(); /** * Get the model that handles slice index information */ irisGetMacro(SliceIndexModel, AbstractRangedIntProperty *) /** * Get the model than handles the selected component (timepoint) in the * currently selected image */ irisRangedPropertyAccessMacro(CurrentComponentInSelectedLayer, unsigned int) /** * Set the index of the slice in the current view. This method will * update the cursor in the IRISApplication object */ void UpdateSliceIndex(unsigned int newIndex); /** * Get the number of slices available in this view */ unsigned int GetNumberOfSlices() const; /** Return the image axis along which this window shows slices */ size_t GetSliceDirectionInImageSpace() { return m_ImageAxes[2]; } /** Reset the view position to center of the image */ void ResetViewPosition (); /** Return the offset from the center of the viewport to the cursor position * in slice units (#voxels * spacing). This is used to synchronize panning * across SNAP sessions */ Vector2f GetViewPositionRelativeToCursor(); /** Set the offset from the center of the viewport to the cursor position */ void SetViewPositionRelativeToCursor(Vector2f offset); /** Center the view on the image crosshairs */ void CenterViewOnCursor(); /** Set the zoom factor (number of pixels on the screen per millimeter in * image space */ void SetViewZoom(float zoom); /** * Zoom in/out by a specified factor. This method will 'stop' at the optimal * zoom if it's between the old zoom and the new zoom */ void ZoomInOrOut(float factor); /** Get the zoom factor (number of pixels on the screen per millimeter in * image space */ irisGetMacro(ViewZoom,float) /** Computes the zoom that gives the best fit for the window */ void ComputeOptimalZoom(); /** Compute the optimal zoom (best fit) */ irisGetMacro(OptimalZoom,float) /** Set the zoom management flag */ irisSetMacro(ManagedZoom,bool) /** Set the view position **/ void SetViewPosition(Vector2f); /** Get the view position **/ irisGetMacro(ViewPosition, Vector2f) /** Get the slice spacing in the display space orientation */ irisGetMacro(SliceSpacing,Vector3f) /** Get the slice spacing in the display space orientation */ irisGetMacro(SliceSize,Vector3i) /** The id (slice direction) of this slice model */ irisGetMacro(Id, int) /** Get the physical size of the window (updated from widget via events) */ Vector2ui GetSize(); /** * Get the size of the canvas on which the slice will be rendered. When the * view is in tiled mode, this reports the size of one of the tiles. When the * new is in main/thumbnail mode, this reports the size of the main view */ Vector2ui GetCanvasSize(); /** Has the slice model been initialized with image data? */ irisIsMacro(SliceInitialized) irisGetMacro(ParentUI, GlobalUIModel *) irisGetMacro(Driver, IRISApplication *) irisGetMacro(ImageData, GenericImageData *) irisGetMacro(ZoomThumbnailPosition, Vector2i) irisGetMacro(ZoomThumbnailSize, Vector2i) irisGetMacro(ThumbnailZoom, float) irisGetMacro(ImageToDisplayTransform, const ImageCoordinateTransform &) irisGetMacro(DisplayToAnatomyTransform, const ImageCoordinateTransform &) irisGetMacro(DisplayToImageTransform, const ImageCoordinateTransform &) irisGetMacro(ViewportLayout, const SliceViewportLayout &) /** * Get the viewport for decoration. This is either the entire viewport, * or when the viewport is broken into thumbnail part and main part, the * main part */ void GetNonThumbnailViewport(Vector2ui &pos, Vector2ui &size); /** * Get the image layer for a context menu request, or NULL if requesting a * context menu at a position should not be possible. TODO: this is kind of * a weird place to house this code. */ ImageWrapperBase *GetThumbnailedLayerAtPosition(int x, int y); /** * Get the layer that is in context for a position in the window. This can be * a thumbnail layer or a regular layer. The third parameter is a boolean flag * indicating whether the layer is a thumbnail or not. */ ImageWrapperBase *GetContextLayerAtPosition(int x, int y, bool &outIsThumbnail); /** Get the layer in a given tile, when using tiled views */ ImageWrapperBase *GetLayerForNthTile(int row, int col); /** Compute the canvas size needed to display slice at current zoom factor */ Vector2i GetOptimalCanvasSize(); /** This method computes the thumbnail properties (size, zoom) */ void ComputeThumbnailProperties(); // Check whether the thumbnail should be drawn or not bool IsThumbnailOn(); /** A model representing the ID of the layer over which the mouse is hovering */ irisSimplePropertyAccessMacro(HoveredImageLayerId, unsigned long) /** Whether the hovered image layer id is shown in thumbnail mode */ irisSimplePropertyAccessMacro(HoveredImageIsThumbnail, bool) /** Get the viewport corresponding to the hovered layer */ const SliceViewportLayout::SubViewport *GetHoveredViewport(); /** Merges a binary segmentation drawn on a slice into the main segmentation in SNAP. Returns the number of voxels changed. This method might be better placed in IRISApplication */ unsigned int MergeSliceSegmentation( itk::Image *drawing); protected: GenericSliceModel(); ~GenericSliceModel(); // Parent (where the global UI state is stored) GlobalUIModel *m_ParentUI; // Top-level logic object IRISApplication *m_Driver; // Pointer to the image data GenericImageData *m_ImageData; // Viewport size reporter (communicates with the UI about viewport size) ViewportSizeReporter *m_SizeReporter; // Description of how the main viewport is divided into parts SliceViewportLayout m_ViewportLayout; // Window id, equal to the direction in display space along which the // window shows slices int m_Id; // The index of the image space axes corresponding to the u,v,w of the // window (computed by applying a transform to the DisplayAxes) int m_ImageAxes[3]; // The transform from image coordinates to display coordinates ImageCoordinateTransform m_ImageToDisplayTransform; // The transform from display coordinates to image coordinates ImageCoordinateTransform m_DisplayToImageTransform; // The transform from display coordinates to patient coordinates ImageCoordinateTransform m_DisplayToAnatomyTransform; // Dimensions of the current slice (the third component is the size // of the image in the slice direction) Vector3i m_SliceSize; // Pixel dimensions for the slice. (the third component is the pixel // width in the slice direction) Vector3f m_SliceSpacing; // Position of visible window in slice space coordinates Vector2f m_ViewPosition; // The view position where the slice wants to be Vector2f m_OptimalViewPosition; // The number of screen pixels per mm of image float m_ViewZoom; // The zoom level at which the slice fits snugly into the window float m_OptimalZoom; // Flag indicating whether the window's zooming is managed externally // by the SliceWindowCoordinator bool m_ManagedZoom; // The default screen margin (area into which we do not paint) at // least in default zoom unsigned int m_Margin; // The position and size of the zoom thumbnail. These are in real pixel // units on retina screens, not logical pixels. Vector2i m_ZoomThumbnailPosition, m_ZoomThumbnailSize; // The zoom level in the thumbnail double m_ThumbnailZoom; // State of the model (whether it's been initialized) bool m_SliceInitialized; /** Hovered over layer id */ SmartPtr m_HoveredImageLayerIdModel; SmartPtr m_HoveredImageIsThumbnailModel; /** Access the next window in the slice pipeline */ GenericSliceModel *GetNextSliceWindow(); SmartPtr m_SliceIndexModel; bool GetSliceIndexValueAndDomain(int &value, NumericValueRange *domain); void SetSlideIndexValue(int value); SmartPtr m_CurrentComponentInSelectedLayerModel; bool GetCurrentComponentInSelectedLayerValueAndDomain(unsigned int &value, NumericValueRange *domain); void SetCurrentComponentInSelectedLayerValue(unsigned int value); /** Update the state of the viewport based on current layout settings */ void UpdateViewportLayout(); }; #endif // GENERICSLICEMODEL_H itksnap-3.4.0/GUI/Model/GlobalPreferencesModel.cxx000066400000000000000000000147521263013355200217530ustar00rootroot00000000000000 #include "GlobalPreferencesModel.h" #include "MeshOptions.h" #include "GlobalUIModel.h" #include "GlobalState.h" #include "DefaultBehaviorSettings.h" GlobalPreferencesModel::GlobalPreferencesModel() { // Create all the property containers owned by the model. These hold copies // of the property containers stored in the system. m_DefaultBehaviorSettings = DefaultBehaviorSettings::New(); m_GlobalDisplaySettings = GlobalDisplaySettings::New(); m_MeshOptions = MeshOptions::New(); m_CheckForUpdateModel = wrapGetterSetterPairAsProperty( this, &Self::GetCheckForUpdateValue, &Self::SetCheckForUpdateValue); m_CheckForUpdateModel->RebroadcastFromSourceProperty( m_DefaultBehaviorSettings->GetCheckForUpdatesModel()); // Current appearance element m_ActiveUIElement = SNAPAppearanceSettings::CROSSHAIRS; m_ActiveUIElementModel = wrapGetterSetterPairAsProperty( this, &Self::GetActiveUIElementValue, &Self::SetActiveUIElementValue); // All appearance elements m_ActiveUIElementAppearance = OpenGLAppearanceElement::New(); for(int i = 0; i < SNAPAppearanceSettings::ELEMENT_COUNT; i++) m_ElementAppearance[i] = OpenGLAppearanceElement::New(); // Layout labels for(int j = 0; j < 3; j++) { m_LayoutLabelModel[j] = wrapIndexedGetterSetterPairAsProperty( this, j, &Self::GetLayoutLabelIndexedValue); m_LayoutLabelModel[j]->RebroadcastFromSourceProperty( m_GlobalDisplaySettings->GetSliceLayoutModel()); } } bool GlobalPreferencesModel::GetCheckForUpdateValue(bool &outValue) { switch(m_DefaultBehaviorSettings->GetCheckForUpdates()) { case DefaultBehaviorSettings::UPDATE_YES: outValue = true; return true; case DefaultBehaviorSettings::UPDATE_NO: outValue = false; return true; case DefaultBehaviorSettings::UPDATE_UNKNOWN: return false; } return false; } void GlobalPreferencesModel::SetCheckForUpdateValue(bool inValue) { m_DefaultBehaviorSettings->SetCheckForUpdates( inValue ? DefaultBehaviorSettings::UPDATE_YES : DefaultBehaviorSettings::UPDATE_NO); } bool GlobalPreferencesModel::GetActiveUIElementValue(GlobalPreferencesModel::UIElement &value) { value = m_ActiveUIElement; return true; } void GlobalPreferencesModel::SetActiveUIElementValue(GlobalPreferencesModel::UIElement value) { if(m_ActiveUIElement != value) { // Locally store the current settings edited by the user if(m_ActiveUIElement != SNAPAppearanceSettings::ELEMENT_COUNT) m_ElementAppearance[m_ActiveUIElement]->DeepCopy(m_ActiveUIElementAppearance); // Update the element m_ActiveUIElement = value; // Update the apperance shown to the user (unless this is a bad value) if(m_ActiveUIElement != SNAPAppearanceSettings::ELEMENT_COUNT) m_ActiveUIElementAppearance->DeepCopy(m_ElementAppearance[m_ActiveUIElement]); // The state has changed this->InvokeEvent(StateMachineChangeEvent()); } } bool GlobalPreferencesModel::GetLayoutLabelIndexedValue(int index, std::string &value) { static std::map idxmap; if(!idxmap.size()) { idxmap[GlobalDisplaySettings::LAYOUT_ACS] = "ACS"; idxmap[GlobalDisplaySettings::LAYOUT_ASC] = "ASC"; idxmap[GlobalDisplaySettings::LAYOUT_CAS] = "CAS"; idxmap[GlobalDisplaySettings::LAYOUT_CSA] = "CSA"; idxmap[GlobalDisplaySettings::LAYOUT_SAC] = "SAC"; idxmap[GlobalDisplaySettings::LAYOUT_SCA] = "SCA"; } GlobalDisplaySettings::UISliceLayout lo = m_GlobalDisplaySettings->GetSliceLayout(); char letter = idxmap[lo][index]; switch(letter) { case 'A' : value = "Axial"; break; case 'S' : value = "Sagittal"; break; case 'C' : value = "Coronal"; break; } return true; } bool GlobalPreferencesModel::CheckState(GlobalPreferencesModel::UIState state) { switch(state) { case GlobalPreferencesModel::UIF_VALID_UI_ELEMENT_SELECTED: return m_ActiveUIElement != SNAPAppearanceSettings::ELEMENT_COUNT; } return false; } void GlobalPreferencesModel::SetParentModel(GlobalUIModel *parent) { m_ParentModel = parent; } void GlobalPreferencesModel::InitializePreferences() { // Pull the preferences from the system GlobalState *gs = m_ParentModel->GetGlobalState(); SNAPAppearanceSettings *as = m_ParentModel->GetAppearanceSettings(); // Default behaviors m_DefaultBehaviorSettings->DeepCopy(gs->GetDefaultBehaviorSettings()); // Global display prefs m_GlobalDisplaySettings->DeepCopy(m_ParentModel->GetGlobalDisplaySettings()); // Mesh options m_MeshOptions->DeepCopy(gs->GetMeshOptions()); // Appearance of all the elements for(int i = 0; i < SNAPAppearanceSettings::ELEMENT_COUNT; i++) m_ElementAppearance[i]->DeepCopy(as->GetUIElement(i)); // Apperance of selected element if(m_ActiveUIElement != SNAPAppearanceSettings::ELEMENT_COUNT) m_ActiveUIElementAppearance->DeepCopy(as->GetUIElement(m_ActiveUIElement)); } void GlobalPreferencesModel::ApplyPreferences() { // Pull the preferences from the system GlobalState *gs = m_ParentModel->GetGlobalState(); SNAPAppearanceSettings *as = m_ParentModel->GetAppearanceSettings(); // Default behaviors gs->GetDefaultBehaviorSettings()->DeepCopy(m_DefaultBehaviorSettings); // Global display prefs m_ParentModel->SetGlobalDisplaySettings(m_GlobalDisplaySettings); // Mesh options gs->GetMeshOptions()->DeepCopy(m_MeshOptions); // If the current element is valid, accept it if(m_ActiveUIElement != SNAPAppearanceSettings::ELEMENT_COUNT) m_ElementAppearance[m_ActiveUIElement]->DeepCopy(m_ActiveUIElementAppearance); // Appearance of all the elements for(int i = 0; i < SNAPAppearanceSettings::ELEMENT_COUNT; i++) as->GetUIElement(i)->DeepCopy(m_ElementAppearance[i]); } void GlobalPreferencesModel::ResetCurrentElement() { assert(m_ActiveUIElement != SNAPAppearanceSettings::ELEMENT_COUNT); SNAPAppearanceSettings *as = m_ParentModel->GetAppearanceSettings(); m_ActiveUIElementAppearance->DeepCopy( as->GetUIElementDefaultSettings(m_ActiveUIElement)); } void GlobalPreferencesModel::ResetAllElements() { assert(m_ActiveUIElement != SNAPAppearanceSettings::ELEMENT_COUNT); SNAPAppearanceSettings *as = m_ParentModel->GetAppearanceSettings(); for(int i = 0; i < SNAPAppearanceSettings::ELEMENT_COUNT; i++) { m_ElementAppearance[i]->DeepCopy(as->GetUIElementDefaultSettings(i)); } if(m_ActiveUIElement != SNAPAppearanceSettings::ELEMENT_COUNT) m_ActiveUIElementAppearance->DeepCopy(m_ElementAppearance[m_ActiveUIElement]); } itksnap-3.4.0/GUI/Model/GlobalPreferencesModel.h000066400000000000000000000100641263013355200213700ustar00rootroot00000000000000#ifndef GLOBALPREFERENCESMODEL_H #define GLOBALPREFERENCESMODEL_H #include "PropertyModel.h" #include "ColorMap.h" #include "SNAPAppearanceSettings.h" class MeshOptions; class GlobalUIModel; class DefaultBehaviorSettings; /** * This model exposes the different global preferences to the GUI. It is set * up for a dialog in which the user must press Apply for the changes to * be propagated to the GUI. So this model caches the values the preferences * until the apply function is called */ class GlobalPreferencesModel : public AbstractModel { public: irisITKObjectMacro(GlobalPreferencesModel, AbstractModel) // Typedefs for interpolation mode enum InterpolationMode { NEAREST = 0, LINEAR }; typedef SimpleItemSetDomain InterpolationDomain; // Typedefs for appearance typedef SNAPAppearanceSettings::UIElements UIElement; // State flags enum UIState { UIF_VALID_UI_ELEMENT_SELECTED }; bool CheckState(UIState state); // Default behaviors and permissions irisGetMacro(DefaultBehaviorSettings, DefaultBehaviorSettings *) // The model controlling whether updates are enabled or disabled must // be exposed to the GUI as a boolean model, whereas internally it is // of an ENUM type irisSimplePropertyAccessMacro(CheckForUpdate, bool) // Screen layout irisGetMacro(GlobalDisplaySettings, GlobalDisplaySettings *) // Other slice display properties irisSimplePropertyAccessMacro(DefaultColorMapPreset, std::string) // Current UI element whose apperance is being edited irisSimplePropertyAccessMacro(ActiveUIElement, UIElement) // Apperance of the current element irisGetMacro(ActiveUIElementAppearance, OpenGLAppearanceElement *) // Mesh options irisGetMacro(MeshOptions, MeshOptions *) // Screen layout labels AbstractSimpleStringProperty *GetLayoutLabelModel(int i) { return m_LayoutLabelModel[i]; } /** * Set the parent model */ void SetParentModel(GlobalUIModel *parent); irisGetMacro(ParentModel, GlobalUIModel *) /** * Initialize the internal cached properties based on current system state. * This method should be called when opening the properties dialog, or on revert */ void InitializePreferences(); /** * Update the system state with the current cached preferences */ void ApplyPreferences(); /** Reset visual element to default */ void ResetCurrentElement(); /** Reset all visual elements to default */ void ResetAllElements(); protected: GlobalPreferencesModel(); // Default behaviors and permissions (copy of the system's settings) SmartPtr m_DefaultBehaviorSettings; // Updates model SmartPtr m_CheckForUpdateModel; bool GetCheckForUpdateValue(bool &outValue); void SetCheckForUpdateValue(bool inValue); // Color map preset model (tricky) SmartPtr m_DefaultColorMapPresetModel; // Current appearance element UIElement m_ActiveUIElement; // The model for the appearance element. This is a model that wraps around the // getter and setter below, since changes to the active element require additional // changes to this class. SmartPtr > m_ActiveUIElementModel; bool GetActiveUIElementValue(UIElement &value); void SetActiveUIElementValue(UIElement value); // The cached apperance settings for the active element SmartPtr m_ActiveUIElementAppearance; // The apperance settings for all elements (held until user presses apply) SmartPtr m_ElementAppearance[SNAPAppearanceSettings::ELEMENT_COUNT]; // Mesh options (we keep a copy) SmartPtr m_MeshOptions; // Copy of the system's global display preferences (thumbnail, etc) SmartPtr m_GlobalDisplaySettings; // Layout label SmartPtr m_LayoutLabelModel[3]; bool GetLayoutLabelIndexedValue(int index, std::string &value); // Parent model GlobalUIModel *m_ParentModel; }; #endif // GLOBALPREFERENCESMODEL_H itksnap-3.4.0/GUI/Model/GlobalUIModel.cxx000066400000000000000000000674211263013355200200300ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #include "GlobalUIModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PolygonSettingsModel.h" #include #include #include #include "NumericPropertyToggleAdaptor.h" #include "HistoryManager.h" #include "MeshExportModel.h" #include "GlobalPreferencesModel.h" #include "MeshOptions.h" #include "DefaultBehaviorSettings.h" #include "SNAPAppearanceSettings.h" #include "ImageIOWizardModel.h" #include "IntensityCurveInterface.h" #include "ColorLabelQuickListModel.h" #include #include #include // Enable this model to be used with the flag engine template class SNAPUIFlag; GlobalUIModel::GlobalUIModel() : AbstractModel() { // Create the appearance settings objects m_AppearanceSettings = SNAPAppearanceSettings::New(); // Global display settings m_GlobalDisplaySettings = GlobalDisplaySettings::New(); // Create the IRIS application login m_Driver = IRISApplication::New(); // Display layout model m_DisplayLayoutModel = DisplayLayoutModel::New(); m_DisplayLayoutModel->SetParentModel(this); // Paintbrush settings m_PaintbrushSettingsModel = PaintbrushSettingsModel::New(); m_PaintbrushSettingsModel->SetParentModel(this); // Create the slice models for (unsigned int i = 0; i < 3; i++) { m_SliceModel[i] = GenericSliceModel::New(); m_SliceModel[i]->Initialize(this, i); m_CursorNavigationModel[i] = OrthogonalSliceCursorNavigationModel::New(); m_CursorNavigationModel[i]->SetParent(m_SliceModel[i]); m_PolygonDrawingModel[i] = PolygonDrawingModel::New(); m_PolygonDrawingModel[i]->SetParent(m_SliceModel[i]); m_SnakeROIModel[i] = SnakeROIModel::New(); m_SnakeROIModel[i]->SetParent(m_SliceModel[i]); m_PaintbrushModel[i] = PaintbrushModel::New(); m_PaintbrushModel[i]->SetParent(m_SliceModel[i]); m_AnnotationModel[i] = AnnotationModel::New(); m_AnnotationModel[i]->SetParent(m_SliceModel[i]); } // Polygon settings m_PolygonSettingsModel = PolygonSettingsModel::New(); m_PolygonSettingsModel->SetParentModel(this); // Connect them together with the coordinator m_SliceCoordinator = SliceWindowCoordinator::New(); m_SliceCoordinator->SetParentModel(this); // Intensity curve model m_IntensityCurveModel = IntensityCurveModel::New(); m_IntensityCurveModel->SetParentModel(this); // Color map model m_ColorMapModel = ColorMapModel::New(); m_ColorMapModel->SetParentModel(this); // Image info model m_ImageInfoModel = ImageInfoModel::New(); m_ImageInfoModel->SetParentModel(this); // Component selection m_LayerGeneralPropertiesModel = LayerGeneralPropertiesModel::New(); m_LayerGeneralPropertiesModel->SetParentModel(this); // Layer selections m_LoadedLayersSelectionModel = LayerSelectionModel::New(); m_LoadedLayersSelectionModel->SetParentModel(this); m_LoadedLayersSelectionModel->SetRoleFilter( MAIN_ROLE | OVERLAY_ROLE | SNAP_ROLE); // 3D model m_Model3D = Generic3DModel::New(); m_Model3D->Initialize(this); // Label editor model m_LabelEditorModel = LabelEditorModel::New(); m_LabelEditorModel->SetParentModel(this); // Reorient image model m_ReorientImageModel = ReorientImageModel::New(); m_ReorientImageModel->SetParentModel(this); // Cursor inspection m_CursorInspectionModel = CursorInspectionModel::New(); m_CursorInspectionModel->SetParentModel(this); // Snake model m_SnakeWizardModel = SnakeWizardModel::New(); m_SnakeWizardModel->SetParentModel(this); // Snake ROI resampling model m_SnakeROIResampleModel = SnakeROIResampleModel::New(); m_SnakeROIResampleModel->SetParentModel(this); // Synchronization model m_SynchronizationModel = SynchronizationModel::New(); m_SynchronizationModel->SetParentModel(this); // Snake parameter model m_SnakeParameterModel = SnakeParameterModel::New(); m_SnakeParameterModel->SetParentModel(this); // Mesh export model m_MeshExportModel = MeshExportModel::New(); m_MeshExportModel->SetParentModel(this); // Global prefs model m_GlobalPreferencesModel = GlobalPreferencesModel::New(); m_GlobalPreferencesModel->SetParentModel(this); // Quick list of color labels m_ColorLabelQuickListModel = ColorLabelQuickListModel::New(); m_ColorLabelQuickListModel->SetParentModel(this); // Set up the cursor position model m_CursorPositionModel = wrapGetterSetterPairAsProperty( this, &Self::GetCursorPositionValueAndRange, &Self::SetCursorPosition); // The model needs to rebroadcast cusror change events as value changes. This // is because unlike other more specific models, GlobalUIModel does not fire // ModelUpdateEvent objects. m_CursorPositionModel->Rebroadcast( this, CursorUpdateEvent(), ValueChangedEvent()); m_CursorPositionModel->Rebroadcast( m_Driver, MainImageDimensionsChangeEvent(), DomainChangedEvent()); // ROI size and index models m_SnakeROIIndexModel = wrapGetterSetterPairAsProperty( this, &Self::GetSnakeROIIndexValueAndRange, &Self::SetSnakeROIIndexValue); m_SnakeROIIndexModel->Rebroadcast( m_Driver->GetGlobalState()->GetSegmentationROISettingsModel(), ValueChangedEvent(), ValueChangedEvent()); m_SnakeROIIndexModel->Rebroadcast( m_Driver, MainImageDimensionsChangeEvent(), DomainChangedEvent()); m_SnakeROISizeModel = wrapGetterSetterPairAsProperty( this, &Self::GetSnakeROISizeValueAndRange, &Self::SetSnakeROISizeValue); m_SnakeROISizeModel->Rebroadcast( m_Driver->GetGlobalState()->GetSegmentationROISettingsModel(), ValueChangedEvent(), ValueChangedEvent()); m_SnakeROISizeModel->Rebroadcast( m_Driver, MainImageDimensionsChangeEvent(), DomainChangedEvent()); m_SnakeROISeedWithCurrentSegmentationModel = wrapGetterSetterPairAsProperty( this, &Self::GetSnakeROISeedWithCurrentSegmentationValue, &Self::SetSnakeROISeedWithCurrentSegmentationValue); m_SnakeROISeedWithCurrentSegmentationModel->RebroadcastFromSourceProperty( m_Driver->GetGlobalState()->GetSegmentationROISettingsModel()); // Segmentation opacity models m_SegmentationOpacityModel = wrapGetterSetterPairAsProperty( this, &Self::GetSegmentationOpacityValueAndRange, &Self::SetSegmentationOpacityValue); m_SegmentationOpacityModel->RebroadcastFromSourceProperty( m_Driver->GetGlobalState()->GetSegmentationAlphaModel()); m_SegmentationVisibilityModel = NewNumericPropertyToggleAdaptor(m_SegmentationOpacityModel.GetPointer(), 0, 50); // Listen to state changes from the slice coordinator Rebroadcast(m_SliceCoordinator, LinkedZoomUpdateEvent(), LinkedZoomUpdateEvent()); Rebroadcast(m_SliceCoordinator, LinkedZoomUpdateEvent(), StateMachineChangeEvent()); // Rebroadcast cursor change events Rebroadcast(m_Driver, CursorUpdateEvent(), CursorUpdateEvent()); // Rebroadcast image layer change events Rebroadcast(m_Driver, LayerChangeEvent(), LayerChangeEvent()); Rebroadcast(m_Driver, LayerChangeEvent(), StateMachineChangeEvent()); // Rebroadcast image layer change events Rebroadcast(m_Driver, WrapperMetadataChangeEvent(), StateMachineChangeEvent()); // Rebroadcast toolbar model change events (TODO: needed?) Rebroadcast(m_Driver->GetGlobalState()->GetToolbarModeModel(), ValueChangedEvent(), ToolbarModeChangeEvent()); // All the events that result in the voxel under the cursor changing Rebroadcast(this, CursorUpdateEvent(), LabelUnderCursorChangedEvent()); Rebroadcast(m_Driver->GetColorLabelTable(), SegmentationLabelChangeEvent(), LabelUnderCursorChangedEvent()); Rebroadcast(m_Driver, SegmentationChangeEvent(), LabelUnderCursorChangedEvent()); Rebroadcast(m_Driver, SegmentationChangeEvent(), StateMachineChangeEvent()); // Segmentation ROI event Rebroadcast(m_Driver->GetGlobalState()->GetSegmentationROISettingsModel(), ValueChangedEvent(), SegmentationROIChangedEvent()); // The initial reporter delegate is NULL m_ProgressReporterDelegate = NULL; // Initialize the progress reporting command SmartPtr > progcmd = itk::MemberCommand::New(); progcmd->SetCallbackFunction(this, &GlobalUIModel::ProgressCallback); m_ProgressCommand = progcmd.GetPointer(); } GlobalUIModel::~GlobalUIModel() { } bool GlobalUIModel::CheckState(UIState state) { // TODO: implement all the other cases // TODO: the flag values need to be cached and updated in response to incoming // events. Otherwise, there are just too many of these calls happening. Alternatively, // each state could be handled as a separate model. switch(state) { case UIF_BASEIMG_LOADED: return m_Driver->IsMainImageLoaded(); case UIF_IRIS_WITH_BASEIMG_LOADED: return m_Driver->IsMainImageLoaded() && !m_Driver->IsSnakeModeActive(); case UIF_IRIS_MODE: return !m_Driver->IsSnakeModeActive(); case UIF_IRIS_WITH_OVERLAY_LOADED: return m_Driver->IsMainImageLoaded() && !m_Driver->IsSnakeModeActive() && m_Driver->GetCurrentImageData()->GetNumberOfOverlays() > 0; case UIF_ROI_VALID: break; case UIF_LINKED_ZOOM: return m_SliceCoordinator->GetLinkedZoom(); case UIF_UNDO_POSSIBLE: return m_Driver->IsUndoPossible(); case UIF_REDO_POSSIBLE: return m_Driver->IsRedoPossible(); case UIF_UNSAVED_CHANGES: break; case UIF_MESH_SAVEABLE: break; case UIF_OVERLAY_LOADED: return m_Driver->GetCurrentImageData()->IsOverlayLoaded(); case UIF_SNAKE_MODE: return m_Driver->IsSnakeModeActive(); case UIF_LEVEL_SET_ACTIVE: return m_Driver->IsSnakeModeLevelSetActive(); case UIF_MULTIPLE_BASE_LAYERS: { LayerIterator it = m_Driver->GetCurrentImageData()->GetLayers( MAIN_ROLE | OVERLAY_ROLE | SNAP_ROLE); int n = 0; for(; !it.IsAtEnd(); ++it) if(it.GetLayer() && !it.GetLayer()->IsSticky()) ++n; return n > 1; } } return false; } void GlobalUIModel::AutoContrastAllLayers() { GenericImageData *id = m_Driver->GetCurrentImageData(); for(LayerIterator it = id->GetLayers(MAIN_ROLE | OVERLAY_ROLE); !it.IsAtEnd(); ++it) { ImageWrapperBase *layer = it.GetLayer(); AbstractContinuousImageDisplayMappingPolicy *policy = dynamic_cast(layer->GetDisplayMapping()); if(policy) policy->AutoFitContrast(); } } void GlobalUIModel::ResetContrastAllLayers() { GenericImageData *id = m_Driver->GetCurrentImageData(); for(LayerIterator it = id->GetLayers(MAIN_ROLE | OVERLAY_ROLE); !it.IsAtEnd(); ++it) { ImageWrapperBase *layer = it.GetLayer(); AbstractContinuousImageDisplayMappingPolicy *policy = dynamic_cast(layer->GetDisplayMapping()); if(policy && policy->GetIntensityCurve()) policy->GetIntensityCurve()->Reset(); } } void GlobalUIModel::ToggleOverlayVisibility() { // Are we in tiled mode or in stack mode? GenericImageData *id = m_Driver->GetCurrentImageData(); // Remember what layer is current in the general properties model ImageWrapperBase *curr_layer = m_LayerGeneralPropertiesModel->GetLayer(); // Apply the toggle for all overlays for(LayerIterator it = id->GetLayers(MAIN_ROLE | OVERLAY_ROLE | SNAP_ROLE); !it.IsAtEnd(); ++it) { // In stack mode, every overlay is affected. In tile mode, only stickly layers // are affected if(it.GetLayer()->IsSticky()) { m_LayerGeneralPropertiesModel->SetLayer(it.GetLayer()); m_LayerGeneralPropertiesModel->GetLayerVisibilityModel()->SetValue( !m_LayerGeneralPropertiesModel->GetLayerVisibilityModel()->GetValue()); } } // Restore the active layer m_LayerGeneralPropertiesModel->SetLayer(curr_layer); } void GlobalUIModel::AdjustOverlayOpacity(int delta) { // Are we in tiled mode or in stack mode? GenericImageData *id = m_Driver->GetCurrentImageData(); // Remember what layer is current in the general properties model ImageWrapperBase *curr_layer = m_LayerGeneralPropertiesModel->GetLayer(); // Apply the toggle for all overlays for(LayerIterator it = id->GetLayers(MAIN_ROLE | OVERLAY_ROLE | SNAP_ROLE); !it.IsAtEnd(); ++it) { // In stack mode, every overlay is affected. In tile mode, only stickly layers // are affected if(it.GetLayer()->IsSticky()) { m_LayerGeneralPropertiesModel->SetLayer(it.GetLayer()); int op = m_LayerGeneralPropertiesModel->GetLayerOpacityModel()->GetValue(); int op_new = std::min(100, std::max(0, op + delta)); m_LayerGeneralPropertiesModel->GetLayerOpacityModel()->SetValue(op_new); } } // Restore the active layer m_LayerGeneralPropertiesModel->SetLayer(curr_layer); } void GlobalUIModel::SetGlobalDisplaySettings( const GlobalDisplaySettings *settings) { // Check if the settings affecting the slice RAI codes have changed std::string raiOld[3], raiNew[3]; m_GlobalDisplaySettings->GetAnatomyToDisplayTransforms(raiOld[0], raiOld[1], raiOld[2]); settings->GetAnatomyToDisplayTransforms(raiNew[0], raiNew[1], raiNew[2]); // Update the global display settings m_GlobalDisplaySettings->DeepCopy(settings); // React to the change in RAI codes if(raiOld[0] != raiNew[0] || raiOld[1] != raiNew[1] || raiOld[2] != raiNew[2]) { // Update the RAI codes in all slice views m_Driver->SetDisplayGeometry(IRISDisplayGeometry(raiNew[0], raiNew[1], raiNew[2])); // Update the cursor location if(m_Driver->IsMainImageLoaded()) { // Update the cursor position (forced) m_Driver->SetCursorPosition(m_Driver->GetCursorPosition(), true); // Reinitialize all the slice views for(int i = 0; i < 3; i++) m_SliceModel[i]->InitializeSlice(m_Driver->GetCurrentImageData()); // Recenter all views m_SliceCoordinator->ResetViewToFitInAllWindows(); } } } SystemInterface * GlobalUIModel::GetSystemInterface() const { return m_Driver->GetSystemInterface(); } GlobalState * GlobalUIModel::GetGlobalState() const { return m_Driver->GetGlobalState(); } #include "SynchronizationModel.h" void GlobalUIModel::LoadUserPreferences() { SystemInterface *si = m_Driver->GetSystemInterface(); DefaultBehaviorSettings *dbs = m_Driver->GetGlobalState()->GetDefaultBehaviorSettings(); // Load the user preferences from the file system si->LoadUserPreferences(); // Read the appearance settings m_AppearanceSettings->LoadFromRegistry( si->Folder("UserInterface.Appearance")); // Read the default behaviors dbs->ReadFromRegistry( si->Folder("UserInterface.DefaultBehavior")); // Read the global display properties m_GlobalDisplaySettings->ReadFromRegistry( si->Folder("SliceView.DisplaySettings")); // Read the 3D mesh options m_Driver->GetGlobalState()->GetMeshOptions()->ReadFromRegistry( si->Folder("View3D.MeshOptions")); // At this point we should check if the color map preset in the prefs // is still a valid preset, and if it isn't, replace it with a default if(!m_Driver->GetColorMapPresetManager()->IsValidPreset( dbs->GetOverlayColorMapPreset())) { dbs->SetOverlayColorMapPreset(ColorMap::GetPresetName(ColorMap::COLORMAP_GREY)); } // Apply the default startup behaviors m_SliceCoordinator->SetLinkedZoom(dbs->GetLinkedZoom()); m_SynchronizationModel->SetSyncEnabled(dbs->GetSynchronization()); m_SynchronizationModel->SetSyncCursor(dbs->GetSyncCursor()); m_SynchronizationModel->SetSyncZoom(dbs->GetSyncZoom()); m_SynchronizationModel->SetSyncPan(dbs->GetSyncPan()); m_Model3D->SetContinuousUpdate(dbs->GetContinuousMeshUpdate()); m_Driver->GetGlobalState()->SetSliceViewLayerLayout(dbs->GetOverlayLayout()); } void GlobalUIModel::SaveUserPreferences() { SystemInterface *si = m_Driver->GetSystemInterface(); // Read the appearance settings m_AppearanceSettings->SaveToRegistry( si->Folder("UserInterface.Appearance")); // Read the default behaviors m_Driver->GetGlobalState()->GetDefaultBehaviorSettings()->WriteToRegistry( si->Folder("UserInterface.DefaultBehavior")); // Read the global display properties m_GlobalDisplaySettings->WriteToRegistry( si->Folder("SliceView.DisplaySettings")); // Read the 3D mesh options m_Driver->GetGlobalState()->GetMeshOptions()->WriteToRegistry( si->Folder("View3D.MeshOptions")); // Save the preferences si->SaveUserPreferences(); } bool GlobalUIModel::GetCursorPositionValueAndRange( Vector3ui &value, NumericValueRange *range) { if(m_Driver->IsMainImageLoaded()) { value = m_Driver->GetCursorPosition() + 1u; if(range) { range->Set(Vector3ui(1u), m_Driver->GetCurrentImageData()->GetMain()->GetSize(), Vector3ui(1u)); } return true; } return false; } void GlobalUIModel::SetCursorPosition(Vector3ui value) { m_Driver->SetCursorPosition(value - 1u); } bool GlobalUIModel::GetSnakeROIIndexValueAndRange( Vector3ui &value, NumericValueRange *range) { // There has to be an image if(!m_Driver->IsMainImageLoaded()) return false; // Get the image size Vector3ui imsize = m_Driver->GetCurrentImageData()->GetImageRegion().GetSize(); // Get the system's region of interest GlobalState::RegionType roiSystem = m_Driver->GetGlobalState()->GetSegmentationROI(); // Populate the return value for(int i = 0; i < 3; i++) { value[i] = roiSystem.GetIndex()[i] + 1; if(range) { range->Minimum[i] = 1; range->Maximum[i] = imsize[i] - 1; range->StepSize[i] = 1; } } return true; } void GlobalUIModel::SetSnakeROIIndexValue(Vector3ui value) { // Get the image size Vector3ui imsize = m_Driver->GetCurrentImageData()->GetImageRegion().GetSize(); // Get the system's region of interest GlobalState::RegionType roi = m_Driver->GetGlobalState()->GetSegmentationROI(); // Index changed, clamp the size for(int i = 0; i < 3; i++) { roi.SetIndex(i, value[i] - 1); roi.SetSize(i, std::min(value[i], imsize[i] - value[i])); } m_Driver->GetGlobalState()->SetSegmentationROI(roi); } bool GlobalUIModel::GetSnakeROISizeValueAndRange( Vector3ui &value, NumericValueRange *range) { // There has to be an image if(!m_Driver->IsMainImageLoaded()) return false; // Get the image size Vector3ui imsize = m_Driver->GetCurrentImageData()->GetImageRegion().GetSize(); // Get the system's region of interest GlobalState::RegionType roiSystem = m_Driver->GetGlobalState()->GetSegmentationROI(); // Populate the return value for(int i = 0; i < 3; i++) { value[i] = roiSystem.GetSize()[i]; if(range) { range->Minimum[i] = 1; range->Maximum[i] = imsize[i]; range->StepSize[i] = 1; } } return true; } void GlobalUIModel::SetSnakeROISizeValue(Vector3ui value) { // Get the image size Vector3ui imsize = m_Driver->GetCurrentImageData()->GetImageRegion().GetSize(); // Get the system's region of interest GlobalState::RegionType roi = m_Driver->GetGlobalState()->GetSegmentationROI(); // Size changed, clamp the index for(int i = 0; i < 3; i++) { roi.SetSize(i, value[i]); if(value[i] + roi.GetIndex(i) > imsize[i]) roi.SetIndex(i, imsize[i] - value[1]); } m_Driver->GetGlobalState()->SetSegmentationROI(roi); } bool GlobalUIModel::GetSnakeROISeedWithCurrentSegmentationValue(bool &value) { // There has to be an image if(!m_Driver->IsMainImageLoaded()) return false; value = m_Driver->GetGlobalState()->GetSegmentationROISettings().IsSeedWithCurrentSegmentation(); return true; } void GlobalUIModel::SetSnakeROISeedWithCurrentSegmentationValue(bool value) { SNAPSegmentationROISettings roi_settings = m_Driver->GetGlobalState()->GetSegmentationROISettings(); roi_settings.SetSeedWithCurrentSegmentation(value); m_Driver->GetGlobalState()->SetSegmentationROISettings(roi_settings); } bool GlobalUIModel::GetSegmentationOpacityValueAndRange( int &value, NumericValueRange *domain) { // Round the current alpha value to the nearest integer double alpha = m_Driver->GetGlobalState()->GetSegmentationAlpha(); value = (int)(alpha * 100 + 0.5); // Set the domain if(domain) domain->Set(0, 100, 5); return true; } void GlobalUIModel::SetSegmentationOpacityValue(int value) { m_Driver->GetGlobalState()->SetSegmentationAlpha(value / 100.0); } std::vector GlobalUIModel::GetRecentHistoryItems(const char *historyCategory, unsigned int k, bool global_history) { // Load the list of recent files from the history file const HistoryManager::HistoryListType &history = global_history ? this->GetSystemInterface()->GetHistoryManager()->GetGlobalHistory(historyCategory) : this->GetSystemInterface()->GetHistoryManager()->GetLocalHistory(historyCategory); std::vector recent; // Take the five most recent items and create menu items for(unsigned int i = 0; i < k; i++) { if(i < history.size()) { recent.push_back(history[history.size() - (i+1)]); } } return recent; } bool GlobalUIModel::IsHistoryEmpty(const char *historyCategory) { // Load the list of recent files from the history file const HistoryManager::HistoryListType &history = this->GetSystemInterface()->GetHistoryManager()->GetGlobalHistory(historyCategory); return history.size() == 0; } GlobalUIModel::AbstractHistoryModel * GlobalUIModel::GetHistoryModel(const std::string &category) { return m_Driver->GetHistoryManager()->GetGlobalHistoryModel(category); } std::string GlobalUIModel::GenerateScreenshotFilename() { // Get the last screen shot filename used std::string last = m_LastScreenshotFileName; if(last.length() == 0) return "snapshot0001.png"; // Count how many digits there are at the end of the filename std::string noext = itksys::SystemTools::GetFilenameWithoutExtension(last); unsigned int digits = 0; for(int i = noext.length() - 1; i >= 0; i--) { if(isdigit(noext[i])) digits++; else break; } // If there are no digits, return the filename if(digits == 0) return last; // Get the number at the end of the string std::string snum = noext.substr(noext.length() - digits); std::istringstream iss(snum); unsigned long num = 0; iss >> num; // Increment the number by one and convert to another string, padding with zeros std::ostringstream oss; oss << itksys::SystemTools::GetFilenamePath(last); oss << "/"; oss << noext.substr(0, noext.length() - digits); oss << std::setw(digits) << std::setfill('0') << (num + 1); oss << itksys::SystemTools::GetFilenameExtension(last); return oss.str(); } SmartPtr GlobalUIModel::CreateIOWizardModelForSave(ImageWrapperBase *layer, LayerRole role) { // Create save delegate for this layer SmartPtr delegate = m_Driver->CreateSaveDelegateForLayer(layer, role); // Create a model for IO SmartPtr modelIO = ImageIOWizardModel::New(); modelIO->InitializeForSave(this, delegate, delegate->GetCategory().c_str()); return modelIO; } void GlobalUIModel::AnimateLayerComponents() { // TODO: For now all the layers tagged as animating animate. This is the // dumbest possible way to do animation, but it's fine for one layer or // multiple layers with the same number of components. for(LayerIterator it = m_Driver->GetCurrentImageData()->GetLayers(); !it.IsAtEnd(); ++it) { if(it.GetLayer()->GetNumberOfComponents() > 1) { AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast< AbstractMultiChannelDisplayMappingPolicy *>( it.GetLayer()->GetDisplayMapping()); if(dp && dp->GetAnimate()) { MultiChannelDisplayMode mode = dp->GetDisplayMode(); if(mode.SelectedScalarRep == SCALAR_REP_COMPONENT) { mode.SelectedComponent = (mode.SelectedComponent + 1) % it.GetLayer()->GetNumberOfComponents(); dp->SetDisplayMode(mode); } } } } } void GlobalUIModel::IncrementDrawingColorLabel(int delta) { ColorLabelTable *clt = m_Driver->GetColorLabelTable(); LabelType current = m_Driver->GetGlobalState()->GetDrawingColorLabel(); ColorLabelTable::ValidLabelConstIterator pos = clt->GetValidLabels().find(current); if(delta == 1) ++pos; else if(delta == -1) --pos; if(pos != clt->GetValidLabels().end()) m_Driver->GetGlobalState()->SetDrawingColorLabel(pos->first); } void GlobalUIModel::IncrementDrawOverColorLabel(int delta) { // Handle basic cases DrawOverFilter dof = m_Driver->GetGlobalState()->GetDrawOverFilter(); if(dof.CoverageMode == PAINT_OVER_ALL && delta == 1) { dof.CoverageMode = PAINT_OVER_VISIBLE; } else if(dof.CoverageMode == PAINT_OVER_VISIBLE && delta == 1) { dof.CoverageMode = PAINT_OVER_ONE; dof.DrawOverLabel = 0; } else if(dof.CoverageMode == PAINT_OVER_VISIBLE && delta == -1) { dof.CoverageMode = PAINT_OVER_ALL; } else if(dof.CoverageMode == PAINT_OVER_ONE && delta == -1 && dof.DrawOverLabel == 0) { dof.CoverageMode = PAINT_OVER_VISIBLE; dof.DrawOverLabel = 0; } else if(dof.CoverageMode == PAINT_OVER_ONE) { ColorLabelTable *clt = m_Driver->GetColorLabelTable(); ColorLabelTable::ValidLabelConstIterator pos = clt->GetValidLabels().find(dof.DrawOverLabel); if(delta == 1) ++pos; else if(delta == -1) --pos; if(pos != clt->GetValidLabels().end()) dof.DrawOverLabel = pos->first; } else { return; } m_Driver->GetGlobalState()->SetDrawOverFilter(dof); } void GlobalUIModel ::ProgressCallback(itk::Object *source, const itk::EventObject &event) { if(m_ProgressReporterDelegate) { itk::ProcessObject *po = static_cast(source); m_ProgressReporterDelegate->SetProgressValue(po->GetProgress()); } } itksnap-3.4.0/GUI/Model/GlobalUIModel.h000066400000000000000000000337601263013355200174540ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef GLOBALUIMODEL_H #define GLOBALUIMODEL_H #include #include #include #include #include #include class IRISApplication; class SNAPAppearanceSettings; class GenericSliceModel; class OrthogonalSliceCursorNavigationModel; class PolygonDrawingModel; class AnnotationModel; class SliceWindowCoordinator; class GuidedNativeImageIO; class SystemInterface; class GlobalState; class AbstractLoadImageDelegate; class IRISWarningList; class IntensityCurveModel; class LayerSelectionModel; class ColorMapModel; class ImageInfoModel; class Generic3DModel; class LabelEditorModel; class ConcreteColorLabelPropertyModel; class CursorInspectionModel; class SnakeROIModel; class SnakeWizardModel; class SnakeROIResampleModel; class ProgressReporterDelegate; class ReorientImageModel; class DisplayLayoutModel; class PaintbrushModel; class PaintbrushSettingsModel; class PolygonSettingsModel; class LayerGeneralPropertiesModel; class SynchronizationModel; class SnakeParameterModel; class MeshExportModel; class GlobalPreferencesModel; class GlobalDisplaySettings; class ImageIOWizardModel; class ImageWrapperBase; class ColorLabelQuickListModel; namespace itk { class Command; } class SystemInfoDelegate; /** */ class GlobalUIModel : public AbstractModel { public: typedef AbstractModel Superclass; typedef GlobalUIModel Self; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkNewMacro(Self) itkTypeMacro(GlobalUIModel, AbstractModel) // Events fired by this object FIRES(CursorUpdateEvent) FIRES(LayerChangeEvent) FIRES(LinkedZoomUpdateEvent) FIRES(LabelUnderCursorChangedEvent) FIRES(ToolbarModeChangedEvent) irisGetMacro(Driver, IRISApplication *) irisGetMacro(AppearanceSettings, SNAPAppearanceSettings *) /** Get the global display settings (thumbnail properties, etc.) */ irisGetMacro(GlobalDisplaySettings, const GlobalDisplaySettings *) /** Update the global display settings (thumbnail properties, etc.) */ void SetGlobalDisplaySettings(const GlobalDisplaySettings *settings); irisGetMacro(SliceCoordinator, SliceWindowCoordinator *) // Convenience access to the SystemInfterface SystemInterface *GetSystemInterface() const; // I don't know why this is in a separate class GlobalState *GetGlobalState() const; /** * Load the global user preferences at startup. This high-level method * loads the preference file into m_SystemInterface and then pulls out * of the registry folders various settings. * * TODO: this is coded badly. SystemInterface is a poorly designed class * that combines low-level and high-level functionality. It needs to be * recoded */ void LoadUserPreferences(); /** * Save user preferences to disk before quitting the application */ void SaveUserPreferences(); GenericSliceModel *GetSliceModel(unsigned int i) const { return m_SliceModel[i]; } /** Get the slice navigation model for each slice */ OrthogonalSliceCursorNavigationModel * GetCursorNavigationModel(unsigned int i) const { return m_CursorNavigationModel[i]; } /** Get the polygon drawing model for each slice */ PolygonDrawingModel *GetPolygonDrawingModel(unsigned int i) const { return m_PolygonDrawingModel[i]; } /** Get the polygon drawing model for each slice */ SnakeROIModel *GetSnakeROIModel(unsigned int i) const { return m_SnakeROIModel[i]; } PaintbrushModel *GetPaintbrushModel(unsigned int i) const { return m_PaintbrushModel[i]; } /** Get the annotation model for each slice */ AnnotationModel *GetAnnotationModel(unsigned int i) const { return m_AnnotationModel[i]; } /** Get the model for intensity curve navigation */ irisGetMacro(IntensityCurveModel, IntensityCurveModel *) /** Get the model for color map interation */ irisGetMacro(ColorMapModel, ColorMapModel *) /** Get the model for obtaining image info for layers */ irisGetMacro(ImageInfoModel, ImageInfoModel *) /** Get the model for selecting channels in a multi-channel image */ irisGetMacro(LayerGeneralPropertiesModel, LayerGeneralPropertiesModel *) /** Get the model encapsulating the main images and all overlays */ irisGetMacro(LoadedLayersSelectionModel, LayerSelectionModel *) /** Get the model for 3D window interaction */ irisGetMacro(Model3D, Generic3DModel *) /** Get the model for the label editor */ irisGetMacro(LabelEditorModel, LabelEditorModel *) /** Get the model for image reorientation */ irisGetMacro(ReorientImageModel, ReorientImageModel *) /** Get the model that handles UI for the cursor inspector */ irisGetMacro(CursorInspectionModel, CursorInspectionModel *) /** The model that handles snake wizard interaction */ irisGetMacro(SnakeWizardModel, SnakeWizardModel *) /** The model handling display layout properties */ irisGetMacro(DisplayLayoutModel, DisplayLayoutModel *) /** Model for managing paintbrush settings */ irisGetMacro(PaintbrushSettingsModel, PaintbrushSettingsModel *) /** Model for managing polygon settings */ irisGetMacro(PolygonSettingsModel, PolygonSettingsModel *) /** Model for multi-session sync */ irisGetMacro(SynchronizationModel, SynchronizationModel *) /** Model for snake parameter editing */ irisGetMacro(SnakeParameterModel, SnakeParameterModel *) /** Model for the snake ROI resampling */ irisGetMacro(SnakeROIResampleModel, SnakeROIResampleModel *) /** Model for the mesh export wizard */ irisGetMacro(MeshExportModel, MeshExportModel *) /** Model for the preferences dialog */ irisGetMacro(GlobalPreferencesModel, GlobalPreferencesModel *) /** Model for the list of recently used color labels */ irisGetMacro(ColorLabelQuickListModel, ColorLabelQuickListModel *) /** Check the state of the system. This class will issue StateChangeEvent() when one of the flags has changed. This method can be used together with the SNAPUIFlag object to construct listeners to complex state changes. */ bool CheckState(UIState state); /** Get the model for the cursor coordinates */ irisGetMacro(CursorPositionModel, AbstractRangedUIntVec3Property *) /** Get the models for the snake ROI */ irisGetMacro(SnakeROIIndexModel, AbstractRangedUIntVec3Property *) irisGetMacro(SnakeROISizeModel, AbstractRangedUIntVec3Property *) /** Get the model for the snake mode segmentation carry over behavior */ irisSimplePropertyAccessMacro(SnakeROISeedWithCurrentSegmentation, bool) /** A model for overall segmentation opacity (int, range 0..100) */ irisRangedPropertyAccessMacro(SegmentationOpacity, int) /** A model for the segmentation visibility on/off state */ irisSimplePropertyAccessMacro(SegmentationVisibility, bool) /** Method to toggle overlay visibility (all or selected overlays) */ void ToggleOverlayVisibility(); /** Method to adjust overlay opacity (all or selected overlays) */ void AdjustOverlayOpacity(int delta); /** Get a list of k recent "things" that are tracked in history */ std::vector GetRecentHistoryItems(const char *historyCategory, unsigned int k = 5, bool global_history = true); /** Check if a particular history is empty */ bool IsHistoryEmpty(const char *historyCategory); typedef AbstractPropertyModel< std::vector > AbstractHistoryModel; /** A quick-access method to the global history models, same as calling the * GetGlobalHistoryModel() in the HistoryManager() */ AbstractHistoryModel *GetHistoryModel(const std::string &category); /** Get the progress command */ irisGetMacro(ProgressCommand, itk::Command *) /** Get and set the progress reporter delegate */ irisGetSetMacro(ProgressReporterDelegate, ProgressReporterDelegate *) /** Get and set the last screenshot filename */ irisGetSetMacro(LastScreenshotFileName, std::string) /** Generate a suggested filename for saving screenshots, by incrementing * from thelast saved screenshot filename */ std::string GenerateScreenshotFilename(); /** * Create a temporary model for saving an image layer to a file, to use in * conjunction with an IO wizard. We pass in the Layer and the LayerRole */ SmartPtr CreateIOWizardModelForSave( ImageWrapperBase *layer, LayerRole role); /** * Perform an animation step */ void AnimateLayerComponents(); /** Increment the current color label (delta = 1 or -1) */ void IncrementDrawingColorLabel(int delta); /** Increment the draw over color label (delta = 1 or -1) */ void IncrementDrawOverColorLabel(int delta); /** Auto-adjust contrast in all image layers */ void AutoContrastAllLayers(); /** Auto-adjust contrast in all image layers */ void ResetContrastAllLayers(); protected: GlobalUIModel(); ~GlobalUIModel(); // Callback for reporting progress void ProgressCallback(itk::Object *source, const itk::EventObject &event); SmartPtr m_Driver; SmartPtr m_AppearanceSettings; SmartPtr m_GlobalDisplaySettings; // A set of three slice models, representing the UI state of each // of the 2D slice panels the user interacts with SmartPtr m_SliceModel[3]; // A set of models that support cursor navigation SmartPtr m_CursorNavigationModel[3]; // Models for polygon drawing SmartPtr m_PolygonDrawingModel[3]; // Models for snake ROI drawing SmartPtr m_SnakeROIModel[3]; // Models for paintbrush drawing SmartPtr m_PaintbrushModel[3]; // Models for annotation SmartPtr m_AnnotationModel[3]; // Window coordinator SmartPtr m_SliceCoordinator; // Model for intensity curve manipulation SmartPtr m_IntensityCurveModel; // Model for color map manipulation SmartPtr m_ColorMapModel; // Model for image info interaction SmartPtr m_ImageInfoModel; // Model for multi-channel image component selection SmartPtr m_LayerGeneralPropertiesModel; // Layer selection model encapsulating the main image and overlays SmartPtr m_LoadedLayersSelectionModel; // Label editor model SmartPtr m_LabelEditorModel; // Reorient image model SmartPtr m_ReorientImageModel; // Cursor interaction model SmartPtr m_CursorInspectionModel; // 3D Model SmartPtr m_Model3D; // The snake wizard model SmartPtr m_SnakeWizardModel; // Display layout model SmartPtr m_DisplayLayoutModel; // Paintbrush settings SmartPtr m_PaintbrushSettingsModel; // Polygon settings SmartPtr m_PolygonSettingsModel; // Synchronization SmartPtr m_SynchronizationModel; // Snake parameters SmartPtr m_SnakeParameterModel; // Snake resampling model SmartPtr m_SnakeROIResampleModel; // Model for the quick list of recently used labels SmartPtr m_ColorLabelQuickListModel; // Current coordinates of the cursor SmartPtr m_CursorPositionModel; bool GetCursorPositionValueAndRange( Vector3ui &value, NumericValueRange *range); void SetCursorPosition(Vector3ui value); // Current ROI for snake mode SmartPtr m_SnakeROIIndexModel; bool GetSnakeROIIndexValueAndRange( Vector3ui &value, NumericValueRange *range); void SetSnakeROIIndexValue(Vector3ui value); SmartPtr m_SnakeROISizeModel; bool GetSnakeROISizeValueAndRange( Vector3ui &value, NumericValueRange *range); void SetSnakeROISizeValue(Vector3ui value); // What happens to segmentation on entering snake mode SmartPtr m_SnakeROISeedWithCurrentSegmentationModel; bool GetSnakeROISeedWithCurrentSegmentationValue(bool &value); void SetSnakeROISeedWithCurrentSegmentationValue(bool value); // The model for the mesh export wizard SmartPtr m_MeshExportModel; // Global preferences model SmartPtr m_GlobalPreferencesModel; // A pointer to the progress reporter delegate object ProgressReporterDelegate *m_ProgressReporterDelegate; // An ITK command used to handle progress SmartPtr m_ProgressCommand; // Screenshot filename std::string m_LastScreenshotFileName; // Segmentation opacity and visibility models SmartPtr m_SegmentationOpacityModel; SmartPtr m_SegmentationVisibilityModel; // Callbacks for the opacity model bool GetSegmentationOpacityValueAndRange(int &value, NumericValueRange *domain); void SetSegmentationOpacityValue(int value); }; #endif // GLOBALUIMODEL_H itksnap-3.4.0/GUI/Model/ImageIOWizardModel.cxx000066400000000000000000000356161263013355200210260ustar00rootroot00000000000000#include "ImageIOWizardModel.h" #include "GuidedNativeImageIO.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "SystemInterface.h" #include "ImageCoordinateGeometry.h" #include #include "HistoryManager.h" #include "ColorMap.h" #include "IRISException.h" #include #include "ImageIODelegates.h" ImageIOWizardModel::ImageIOWizardModel() { m_Parent = NULL; m_GuidedIO = NULL; m_LoadDelegate = NULL; m_SaveDelegate = NULL; m_Overlay = false; m_UseRegistration = false; m_LoadedImage = NULL; // Initialize various property models m_StickyOverlayModel = wrapGetterSetterPairAsProperty( this, &Self::GetStickyOverlayValue, &Self::SetStickyOverlayValue); m_StickyOverlayColorMapModel = wrapGetterSetterPairAsProperty( this, &Self::GetStickyOverlayColorMapValue, &Self::SetStickyOverlayColorMapValue); // Initialize the registration models // Registration mode RegistrationModeDomain reg_mode_domain; reg_mode_domain[ImageRegistrationManager::RIGID] = "Rigid"; reg_mode_domain[ImageRegistrationManager::SIMILARITY] = "Rigid with uniform scaling"; reg_mode_domain[ImageRegistrationManager::AFFINE] = "Affine"; reg_mode_domain[ImageRegistrationManager::INITONLY] = "Initial alignment only"; m_RegistrationModeModel = NewConcreteProperty(ImageRegistrationManager::RIGID, reg_mode_domain); // Registration metric RegistrationMetricDomain reg_metric_domain; reg_metric_domain[ImageRegistrationManager::NMI] = "Normalized mutual information"; reg_metric_domain[ImageRegistrationManager::NCC] = "Normalized cross-correlation"; reg_metric_domain[ImageRegistrationManager::SSD] = "Squared intensity difference"; m_RegistrationMetricModel = NewConcreteProperty(ImageRegistrationManager::NMI, reg_metric_domain); // Registration initialization RegistrationInitDomain reg_init_domain; reg_init_domain[ImageRegistrationManager::HEADERS] = "Align based on image headers"; reg_init_domain[ImageRegistrationManager::CENTERS] = "Align image centers"; m_RegistrationInitModel = NewConcreteProperty(ImageRegistrationManager::HEADERS, reg_init_domain); // Registration manager m_RegistrationManager = ImageRegistrationManager::New(); Rebroadcast(m_RegistrationManager, itk::IterationEvent(), RegistrationProgressEvent()); // Optimization progress renderer m_RegistrationProgressRenderer = OptimizationProgressRenderer::New(); m_RegistrationProgressRenderer->SetModel(this); } void ImageIOWizardModel ::InitializeForSave(GlobalUIModel *parent, AbstractSaveImageDelegate *delegate, const char *dispName) { m_Parent = parent; m_Mode = SAVE; m_HistoryName = delegate->GetHistoryName(); m_DisplayName = dispName; m_GuidedIO = GuidedNativeImageIO::New(); m_LoadDelegate = NULL; m_SaveDelegate = delegate; m_SuggestedFilename = delegate->GetCurrentFilename(); m_UseRegistration = false; m_Overlay = false; m_LoadedImage = NULL; } void ImageIOWizardModel ::InitializeForLoad(GlobalUIModel *parent, AbstractLoadImageDelegate *delegate, const char *name, const char *dispName) { m_Parent = parent; m_Mode = LOAD; m_HistoryName = name; m_DisplayName = dispName; m_GuidedIO = GuidedNativeImageIO::New(); m_LoadDelegate = delegate; m_SaveDelegate = NULL; m_UseRegistration = delegate->GetUseRegistration(); m_Overlay = delegate->IsOverlay(); m_LoadedImage = NULL; } ImageIOWizardModel::~ImageIOWizardModel() { } std::string ImageIOWizardModel ::GetFilter(const char *lineEntry, const char *extEntry, const char *extSeparator, const char *rowSeparator) { std::ostringstream ossMain; char buffer[1024]; // Go through all supported formats for(unsigned int i=0;i < GuidedNativeImageIO::FORMAT_COUNT;i++) { FileFormat fmt = static_cast(i); GuidedNativeImageIO::FileFormatDescriptor fd = GuidedNativeImageIO::GetFileFormatDescriptor(fmt); // Check if the file format is supported if(this->CanHandleFileFormat(fmt)) { std::ostringstream ossLine; // Scan all of the separators size_t pos = 0; while(pos < fd.pattern.size()) { if(pos) ossLine << extSeparator; size_t pend = fd.pattern.find(',', pos); std::string ext = fd.pattern.substr(pos, pend-pos); sprintf(buffer, extEntry, ext.c_str()); ossLine << buffer; pos = (pend == std::string::npos) ? pend : pend+1; } // Append a row to the format list sprintf(buffer, lineEntry, fd.name.c_str(), ossLine.str().c_str()); ossMain << buffer; ossMain << rowSeparator; } } return ossMain.str(); } ImageIOWizardModel::FileFormat ImageIOWizardModel::GuessFileFormat( const std::string &fname, bool &fileExists) { // For files that don't exist, format can not be reported if(m_Mode == LOAD) { fileExists = itksys::SystemTools::FileExists(fname.c_str(), true); if(!fileExists) return GuidedNativeImageIO::FORMAT_COUNT; } // Look if there is prior knowledge of this image. This overrides // everything else Registry reg; m_Parent->GetDriver()->GetSystemInterface()-> FindRegistryAssociatedWithFile(fname.c_str(), reg); // If the registry contains a file format, override with that FileFormat fmt = m_GuidedIO->GetFileFormat(reg, GuidedNativeImageIO::FORMAT_COUNT); // Try to select a file format accoring to the file name if(fmt != GuidedNativeImageIO::FORMAT_COUNT) return fmt; // If there is no prior knowledge determine the format using magic // numbers and extension information return GuidedNativeImageIO::GuessFormatForFileName(fname, m_Mode==LOAD); } ImageIOWizardModel::FileFormat ImageIOWizardModel::GetFileFormatByName(const std::string &formatName) const { for(int i = 0; i < GuidedNativeImageIO::FORMAT_COUNT; i++) { FileFormat fmt = (FileFormat) i; if(GuidedNativeImageIO::GetFileFormatDescriptor(fmt).name == formatName) return fmt; } return GuidedNativeImageIO::FORMAT_COUNT; } std::string ImageIOWizardModel::GetFileFormatName(ImageIOWizardModel::FileFormat fmt) const { return GuidedNativeImageIO::GetFileFormatDescriptor(fmt).name; } bool ImageIOWizardModel ::CanHandleFileFormat(ImageIOWizardModel::FileFormat fmt) { GuidedNativeImageIO::FileFormatDescriptor fd = GuidedNativeImageIO::GetFileFormatDescriptor(fmt); return (m_Mode == LOAD) || (m_Mode == SAVE && fd.can_write); } std::string ImageIOWizardModel::GetBrowseDirectory(const std::string &file) { // If empty return empty if(file.length() == 0) return file; // If file is a directory, return it std::string fn_expand = file; itksys::SystemTools::ConvertToUnixSlashes(fn_expand); if(itksys::SystemTools::FileIsDirectory(fn_expand.c_str())) return fn_expand; // Get the base name of the file std::string path = itksys::SystemTools::GetFilenamePath(fn_expand); if(itksys::SystemTools::FileIsDirectory(path.c_str())) return path; // By default, return empty string return std::string(""); } std::string ImageIOWizardModel::GetDisplayName() const { return m_DisplayName; } template std::string triple2str(const T &triple) { std::ostringstream oss; oss << triple[0]; oss << " x "; oss << triple[1]; oss << " x "; oss << triple[2]; return oss.str(); } std::string ImageIOWizardModel::GetSummaryItem(ImageIOWizardModel::SummaryItem item) { std::ostringstream sout; vnl_matrix dir; std::string rai; switch(item) { case ImageIOWizardModel::SI_FILENAME: return m_GuidedIO->GetFileNameOfNativeImage(); case ImageIOWizardModel::SI_DIMS: return triple2str(m_GuidedIO->GetNativeImage()->GetBufferedRegion().GetSize()); case ImageIOWizardModel::SI_SPACING: return triple2str(m_GuidedIO->GetNativeImage()->GetSpacing()); case ImageIOWizardModel::SI_ORIGIN: return triple2str(m_GuidedIO->GetNativeImage()->GetOrigin()); case ImageIOWizardModel::SI_ORIENT: dir = m_GuidedIO->GetNativeImage()->GetDirection().GetVnlMatrix(); rai = ImageCoordinateGeometry::ConvertDirectionMatrixToClosestRAICode(dir); if(ImageCoordinateGeometry::IsDirectionMatrixOblique(dir)) sout << "Oblique (closest to " << rai << ")"; else sout << rai; return sout.str(); case ImageIOWizardModel::SI_ENDIAN: return (m_GuidedIO->GetByteOrderInNativeImage() == itk::ImageIOBase::BigEndian) ? "Big Endian" : "Little Endian"; case ImageIOWizardModel::SI_DATATYPE: if(m_GuidedIO->GetComponentTypeInNativeImage() != itk::ImageIOBase::UNKNOWNCOMPONENTTYPE) { // There actually is a type in the IO object return m_GuidedIO->GetComponentTypeAsStringInNativeImage(); } else { // TODO: This is a workaround on an itk bug with RawImageIO // TODO: fix this (get the text selected for the raw image) return "Unknown"; } case ImageIOWizardModel::SI_COMPONENTS: sout << m_GuidedIO->GetNumberOfComponentsInNativeImage(); return sout.str(); case ImageIOWizardModel::SI_FILESIZE: sout << (m_GuidedIO->GetFileSizeOfNativeImage() / 1024.0) << " Kb"; return sout.str(); } return std::string(""); } void ImageIOWizardModel::SetSelectedFormat(ImageIOWizardModel::FileFormat fmt) { GuidedNativeImageIO::SetFileFormat(m_Registry, fmt); } ImageIOWizardModel::FileFormat ImageIOWizardModel::GetSelectedFormat() { return GuidedNativeImageIO::GetFileFormat(m_Registry); } #include "GenericImageData.h" void ImageIOWizardModel::LoadImage(std::string filename) { // There is no loaded image to start with m_LoadedImage = NULL; try { // Clear the warnings m_Warnings.clear(); // Load the header m_GuidedIO->ReadNativeImageHeader(filename.c_str(), m_Registry); // Check if the header is valid m_LoadDelegate->ValidateHeader(m_GuidedIO, m_Warnings); // Remove current data m_LoadDelegate->UnloadCurrentImage(); // Load the data from the image m_GuidedIO->ReadNativeImageData(); // Validate the image data m_LoadDelegate->ValidateImage(m_GuidedIO, m_Warnings); // Update the application m_LoadedImage = m_LoadDelegate->UpdateApplicationWithImage(m_GuidedIO); // Save the IO hints to the registry Registry regAssoc; SystemInterface *si = m_Parent->GetDriver()->GetSystemInterface(); si->FindRegistryAssociatedWithFile( m_GuidedIO->GetFileNameOfNativeImage().c_str(), regAssoc); regAssoc.Folder("Files.Grey").Update(m_Registry); si->AssociateRegistryWithFile( m_GuidedIO->GetFileNameOfNativeImage().c_str(), regAssoc); } catch(IRISException &excIRIS) { throw excIRIS; } catch(std::exception &exc) { throw IRISException("Error: exception occured during image IO. " "Exception: %s", exc.what()); } } void ImageIOWizardModel::SaveImage(std::string filename) { try { m_SaveDelegate->SaveImage(filename, m_GuidedIO, m_Registry, m_Warnings); } catch(std::exception &exc) { throw IRISException("Error: exception occured during image IO. " "Exception: %s", exc.what()); } } bool ImageIOWizardModel::CheckImageValidity() { IRISWarningList warn; m_LoadDelegate->ValidateHeader(m_GuidedIO, warn); return true; } void ImageIOWizardModel::Reset() { m_Registry.Clear(); } void ImageIOWizardModel::ProcessDicomDirectory(const std::string &filename) { // Here is a request GuidedNativeImageIO::DicomRequest req; req.push_back(GuidedNativeImageIO::DicomRequestField( 0x0020, 0x0011, "SeriesNumber")); // Get the directory std::string dir = GetBrowseDirectory(filename); // Get the registry try { m_GuidedIO->ParseDicomDirectory(dir, m_DicomContents, req); } catch (IRISException &ei) { throw ei; } catch (std::exception &e) { throw IRISException("Error: exception occured when parsing DICOM directory. " "Exception: %s", e.what()); } } void ImageIOWizardModel ::LoadDicomSeries(const std::string &filename, int series) { // Set up the registry for DICOM IO m_Registry["DICOM.SeriesId"] << m_DicomContents[series]["SeriesId"][""]; m_Registry.Folder("DICOM.SeriesFiles").PutArray( m_DicomContents[series].Folder("SeriesFiles").GetArray(std::string())); // Set the format to DICOM SetSelectedFormat(GuidedNativeImageIO::FORMAT_DICOM_DIR); // Get the directory std::string dir = GetBrowseDirectory(filename); // Call the main load method this->LoadImage(dir); } unsigned long ImageIOWizardModel::GetFileSizeInBytes(const std::string &file) { return itksys::SystemTools::FileLength(file.c_str()); } bool ImageIOWizardModel::IsImageLoaded() const { // TODO: this may have to change based on validity checks return m_GuidedIO->IsNativeImageLoaded(); } void ImageIOWizardModel::Finalize() { } void ImageIOWizardModel::PerformRegistration() { m_RegistrationManager->PerformRegistration(m_Parent->GetDriver()->GetCurrentImageData(), this->GetRegistrationMode(), this->GetRegistrationMetric(), this->GetRegistrationInit()); } void ImageIOWizardModel::UpdateImageTransformFromRegistration() { m_RegistrationManager->UpdateImageTransformFromRegistration( m_Parent->GetDriver()->GetCurrentImageData()); } double ImageIOWizardModel::GetRegistrationObjective() { return m_RegistrationManager->GetRegistrationObjective(); } bool ImageIOWizardModel::GetStickyOverlayValue(bool &value) { // Make sure the image has already been loaded if(!m_LoadedImage) return false; // Return the stickiness value value = m_LoadedImage->IsSticky(); return true; } void ImageIOWizardModel::SetStickyOverlayValue(bool value) { assert(m_LoadedImage); m_LoadedImage->SetSticky(value); } bool ImageIOWizardModel::GetStickyOverlayColorMapValue(std::string &value) { // Make sure the image has already been loaded if(!m_LoadedImage || !m_LoadedImage->IsSticky()) return false; // Get the display mapping policy (to get a color map) ColorMap *cmap = m_LoadedImage->GetDefaultScalarRepresentation()->GetColorMap(); if(!cmap) return false; // Return the color map preset value = ColorMap::GetPresetName(cmap->GetSystemPreset()); return true; } void ImageIOWizardModel::SetStickyOverlayColorMapValue(std::string value) { assert(m_LoadedImage && m_LoadedImage->IsSticky()); ScalarImageWrapperBase *base = m_LoadedImage->GetDefaultScalarRepresentation(); for(int i = 0; i < ColorMap::COLORMAP_CUSTOM; i++) if(value == ColorMap::GetPresetName((ColorMap::SystemPreset) i)) { base->GetColorMap()->SetToSystemPreset((ColorMap::SystemPreset) i); return; } } itksnap-3.4.0/GUI/Model/ImageIOWizardModel.h000066400000000000000000000235371263013355200204520ustar00rootroot00000000000000#ifndef IMAGEIOWIZARDMODEL_H #define IMAGEIOWIZARDMODEL_H #include #include "AbstractModel.h" #include "GuidedNativeImageIO.h" #include "Registry.h" #include "ImageIODelegates.h" #include "ImageRegistrationManager.h" #include "OptimizationProgressRenderer.h" class GlobalUIModel; namespace itk { class GDCMSeriesFileNames; class FastMutexLock; } /** This class provides a model for the ImageIO wizards. This allows the wizard to be distanced from the program logic. The wizard is just a collection of buttons and callbacks, but very little state This class is subclassed by specific dialogs, to allow customization. For example, save/load dialog behavior is handled this way. */ class ImageIOWizardModel : public AbstractModel { public: irisITKObjectMacro(ImageIOWizardModel, AbstractModel) itkEventMacro(RegistrationProgressEvent, IRISEvent) FIRES(RegistrationProgressEvent) typedef GuidedNativeImageIO::FileFormat FileFormat; enum Mode { LOAD, SAVE }; enum SummaryItem { SI_FILENAME, SI_DIMS, SI_SPACING, SI_ORIGIN, SI_ORIENT, SI_ENDIAN, SI_COMPONENTS, SI_DATATYPE, SI_FILESIZE }; // Initialize the wizard for load operations. Note that the delegate, // which specializes the behavior of this class, is stored internally // using a smart pointer, so its ownership can be relinquished to the // wizard. void InitializeForLoad(GlobalUIModel *parent, AbstractLoadImageDelegate *delegate, const char *name, const char *dispName); void InitializeForSave(GlobalUIModel *parent, AbstractSaveImageDelegate *delegate, const char *dispName); irisGetMacro(Parent, GlobalUIModel *) irisGetMacro(GuidedIO, GuidedNativeImageIO *) // Does the model support loading bool IsLoadMode() const { return m_Mode == LOAD; } // Does the model support loading bool IsSaveMode() const { return m_Mode == SAVE; } /** Access the IO delegate used for loading */ irisGetMacro(LoadDelegate, AbstractLoadImageDelegate *) /** Access the IO delegate used for saving */ irisGetMacro(SaveDelegate, AbstractSaveImageDelegate *) // Whether we can save or load a file format virtual bool CanHandleFileFormat(FileFormat fmt); // This another method that checks if a loaded image is valid virtual bool CheckImageValidity(); // Default extension for saving files virtual std::string GetDefaultFormatForSave() const { return std::string("NiFTI"); } /** Create a filter string for file IO dialogs. The lineEntry is in the printf format, with first %s being the title of the format, and the second being the list of extensions. extEntry is similar, used to print each extension. The examples for Qt are "%s (%s)" for lineEntry and "*.%s" for extEntry. For FLTK it would be "%s *.{%s}" for lineEntry and "%s" for extEntry. The separators are used to separate extensions per entry and rows in the filter string. */ std::string GetFilter(const char *lineEntry, const char *extEntry, const char *extSeparator, const char *rowSeparator); /** Guess the format for the file. In load mode, if the file does not exist, this will return FORMAT_COUNT, i.e., failure to determine format. If it exists, the format will be determined using registry information (if open before), magic number, and extension, in turn. If in save mode, format is detected using registry and extension only. The last parameter is only considered in Load mode. */ FileFormat GuessFileFormat(const std::string &fname, bool &fileExists); /** Get the file format from file format name */ FileFormat GetFileFormatByName(const std::string &formatName) const; /** Get the name of a file format */ std::string GetFileFormatName(FileFormat fmt) const; /** Get the directory where to browse, given a currently entered file */ std::string GetBrowseDirectory(const std::string &file); /** Get the size of the file in bytes */ unsigned long GetFileSizeInBytes(const std::string &file); /** Get the history of filenames */ irisGetMacro(HistoryName, std::string) /** Get the display name to show in the dialog */ std::string GetDisplayName() const; /** * Reset the state of the model */ void Reset(); /** Set the format selected by the user */ void SetSelectedFormat(FileFormat fmt); FileFormat GetSelectedFormat(); /** Load the image from filename, putting warnings into a warning list. This may also fire an exception (e.g., if validation failed) */ void LoadImage(std::string filename); /** Save the image to a filename */ void SaveImage(std::string filename); /** Get the warnings generated by LoadImage */ irisGetMacro(Warnings, IRISWarningList) /** Whether an image was loaded */ bool IsImageLoaded() const; /** Get summary items to display for the user */ std::string GetSummaryItem(SummaryItem item); /** Load relevant information from DICOM directory */ void ProcessDicomDirectory(const std::string &filename); /** Get the DICOM directory contents */ irisGetMacro(DicomContents, const GuidedNativeImageIO::RegistryArray &) /** Load n-th series from DICOM directory */ void LoadDicomSeries(const std::string &filename, int series); irisGetSetMacro(SuggestedFilename, std::string) /** Access the registry stored in the model and used for providing hints to the image IO. */ Registry &GetHints() { return m_Registry; } /** Called just before exiting the wizard. Should update history, etc. */ virtual void Finalize(); /** * Set whether the IO module should use registration */ irisGetMacro(UseRegistration, bool) /** * Is this an overlay? */ irisIsMacro(Overlay) /** Should the overlay be loaded as sticky */ irisSimplePropertyAccessMacro(StickyOverlay, bool) /** Which is the colormap of the sticky overlay */ irisSimplePropertyAccessMacro(StickyOverlayColorMap, std::string) // Registration mode typedefs typedef ImageRegistrationManager::RegistrationMode RegistrationMode; typedef ImageRegistrationManager::RegistrationMetric RegistrationMetric; typedef ImageRegistrationManager::RegistrationInit RegistrationInit; // Registration domains typedef SimpleItemSetDomain RegistrationModeDomain; typedef SimpleItemSetDomain RegistrationMetricDomain; typedef SimpleItemSetDomain RegistrationInitDomain; // Access to registration models irisGenericPropertyAccessMacro(RegistrationMode, RegistrationMode, RegistrationModeDomain) irisGenericPropertyAccessMacro(RegistrationMetric, RegistrationMetric, RegistrationMetricDomain) irisGenericPropertyAccessMacro(RegistrationInit, RegistrationInit, RegistrationInitDomain) /** * Perform registration between loaded overlay and main image. This operation is meant to * be executed in a separate thread. From time to time, it will place the registration * results into a thread-safe variable and fire a progress event. Use the method * UpdateImageTransformFromRegistration() to apply registration results to the displayed image */ void PerformRegistration(); /** * Apply the currently computed transform to the image being loaded - allowing the user to * see the registration results on the fly */ void UpdateImageTransformFromRegistration(); /** Get the value of the registration objective function */ double GetRegistrationObjective(); /** Get the progress renderer object */ irisGetMacro(RegistrationProgressRenderer, OptimizationProgressRenderer *) protected: // Standard ITK protected constructors ImageIOWizardModel(); virtual ~ImageIOWizardModel(); // State of the model Mode m_Mode; // Delegate that does the actual loading or saving SmartPtr m_LoadDelegate; // Delegate than handles saving SmartPtr m_SaveDelegate; // The history list associated with the model std::string m_HistoryName, m_DisplayName; // Parent model GlobalUIModel *m_Parent; SmartPtr m_GuidedIO; // Warnings generated during IO IRISWarningList m_Warnings; // Registry containing auxiliary info Registry m_Registry; // Whether the layer being loaded is an overlay bool m_Overlay; // Whether registration should be used to load this image bool m_UseRegistration; // Suggested filename std::string m_SuggestedFilename; // DICOM support GuidedNativeImageIO::RegistryArray m_DicomContents; // Overlay display behavior models SmartPtr m_StickyOverlayModel; bool GetStickyOverlayValue(bool &value); void SetStickyOverlayValue(bool value); // Selected color map behavior model SmartPtr m_StickyOverlayColorMapModel; bool GetStickyOverlayColorMapValue(std::string &value); void SetStickyOverlayColorMapValue(std::string value); // Registration models typedef ConcretePropertyModel RegistrationModeModel; typedef ConcretePropertyModel RegistrationMetricModel; typedef ConcretePropertyModel RegistrationInitModel; SmartPtr m_RegistrationModeModel; SmartPtr m_RegistrationMetricModel; SmartPtr m_RegistrationInitModel; // Registration manager SmartPtr m_RegistrationManager; // Renderer used to plot the metric SmartPtr m_RegistrationProgressRenderer; // Pointer to the image layer that has been loaded ImageWrapperBase *m_LoadedImage; }; #endif // IMAGEIOWIZARDMODEL_H itksnap-3.4.0/GUI/Model/ImageInfoModel.cxx000066400000000000000000000125661263013355200202300ustar00rootroot00000000000000#include "ImageInfoModel.h" #include "LayerAssociation.txx" #include "MetaDataAccess.h" #include #include // This compiles the LayerAssociation for the color map template class LayerAssociation; ImageInfoModel::ImageInfoModel() { m_ImageDimensionsModel = wrapGetterSetterPairAsProperty( this, &Self::GetImageDimensions); m_ImageSpacingModel = wrapGetterSetterPairAsProperty( this, &Self::GetImageSpacing); m_ImageOriginModel = wrapGetterSetterPairAsProperty( this, &Self::GetImageOrigin); m_ImageItkCoordinatesModel = wrapGetterSetterPairAsProperty( this, &Self::GetImageItkCoordinates); m_ImageNiftiCoordinatesModel = wrapGetterSetterPairAsProperty( this, &Self::GetImageNiftiCoordinates); m_ImageMinMaxModel = wrapGetterSetterPairAsProperty( this, &Self::GetImageMinMax); m_ImageOrientationModel = wrapGetterSetterPairAsProperty( this, &Self::GetImageOrientation); // Create the property model for the filter m_MetadataFilterModel = ConcreteSimpleStringProperty::New(); // Listen to events on the filter, so we can update the metadata Rebroadcast(m_MetadataFilterModel, ValueChangedEvent(), MetadataChangeEvent()); // Also rebroadcast active layer change events as both ModelChange and Metadata // change events Rebroadcast(this, ActiveLayerChangedEvent(), MetadataChangeEvent()); } void ImageInfoModel::SetParentModel(GlobalUIModel *parent) { Superclass::SetParentModel(parent); // Cursor update events are mapped to model update events Rebroadcast(m_ParentModel, CursorUpdateEvent(), ModelUpdateEvent()); } bool ImageInfoModel ::GetImageDimensions(Vector3ui &value) { if(!this->GetLayer()) return false; value = GetLayer()->GetSize(); return true; } bool ImageInfoModel ::GetImageOrigin(Vector3d &value) { if(!this->GetLayer()) return false; value = GetLayer()->GetImageBase()->GetOrigin(); return true; } bool ImageInfoModel ::GetImageSpacing(Vector3d &value) { if(!this->GetLayer()) return false; value = GetLayer()->GetImageBase()->GetSpacing(); return true; } bool ImageInfoModel ::GetImageItkCoordinates(Vector3d &value) { if(!this->GetLayer()) return false; Vector3ui cursor = m_ParentModel->GetDriver()->GetCursorPosition(); value = GetLayer()->TransformVoxelIndexToPosition(cursor); return true; } bool ImageInfoModel ::GetImageNiftiCoordinates(Vector3d &value) { if(!this->GetLayer()) return false; Vector3ui cursor = m_ParentModel->GetDriver()->GetCursorPosition(); value = GetLayer()->TransformVoxelIndexToNIFTICoordinates(to_double(cursor)); return true; } bool ImageInfoModel ::GetImageMinMax(Vector2d &value) { ImageWrapperBase *layer = this->GetLayer(); // TODO: figure out how to handle this conistently throughout if(layer) { value = Vector2d(layer->GetImageMinNative(), layer->GetImageMaxNative()); return true; } return false; } bool ImageInfoModel ::GetImageOrientation(std::string &value) { if(!this->GetLayer()) return false; const ImageCoordinateGeometry &geo = m_ParentModel->GetDriver()->GetCurrentImageData()->GetImageGeometry(); ImageCoordinateGeometry::DirectionMatrix dmat = geo.GetImageDirectionCosineMatrix(); std::string raicode = ImageCoordinateGeometry::ConvertDirectionMatrixToClosestRAICode(dmat); if (ImageCoordinateGeometry::IsDirectionMatrixOblique(dmat)) value = std::string("Oblique (closest to ") + raicode + string(")"); else value = raicode; return true; } void ImageInfoModel::OnUpdate() { Superclass::OnUpdate(); // Is there a change to the metadata? if(this->m_EventBucket->HasEvent(ActiveLayerChangedEvent()) || this->m_EventBucket->HasEvent(ValueChangedEvent(),m_MetadataFilterModel)) { // Recompute the metadata index this->UpdateMetadataIndex(); } } // #include bool case_insensitive_predicate(char a, char b) { return std::tolower(a) == std::tolower(b); } bool case_insensitive_find(std::string &a, std::string &b) { std::string::iterator it = std::search( a.begin(), a.end(), b.begin(), b.end(), case_insensitive_predicate); return it != a.end(); } void ImageInfoModel::UpdateMetadataIndex() { // Clear the list of selected keys m_MetadataKeys.clear(); // Search keys and values that meet the filter if(GetLayer()) { MetaDataAccess mda(GetLayer()->GetImageBase()); std::vector keys = mda.GetKeysAsArray(); std::string filter = m_MetadataFilterModel->GetValue(); for(size_t i = 0; i < keys.size(); i++) { std::string key = keys[i]; std::string dcm = mda.MapKeyToDICOM(key); std::string value = mda.GetValueAsString(key); if(filter.size() == 0 || case_insensitive_find(dcm, filter) || case_insensitive_find(value, filter)) { m_MetadataKeys.push_back(key); } } } } int ImageInfoModel::GetMetadataRows() { return m_MetadataKeys.size(); } std::string ImageInfoModel::GetMetadataCell(int row, int col) { assert(GetLayer()); assert(row >= 0 && row < (int) m_MetadataKeys.size()); std::string key = m_MetadataKeys[row]; MetaDataAccess mda(GetLayer()->GetImageBase()); return (col == 0) ? mda.MapKeyToDICOM(key) : mda.GetValueAsString(key); } itksnap-3.4.0/GUI/Model/ImageInfoModel.h000066400000000000000000000062401263013355200176450ustar00rootroot00000000000000#ifndef IMAGEINFOMODEL_H #define IMAGEINFOMODEL_H #include "AbstractLayerAssociatedModel.h" #include "PropertyModel.h" class ImageInfoLayerProperties { public: irisGetSetMacro(ObserverTag, unsigned long) virtual ~ImageInfoLayerProperties() {} protected: // Whether or not we are already listening to events from this layer unsigned long m_ObserverTag; }; typedef AbstractLayerAssociatedModel< ImageInfoLayerProperties, ImageWrapperBase> ImageInfoModelBase; class ImageInfoModel : public ImageInfoModelBase { public: irisITKObjectMacro(ImageInfoModel, ImageInfoModelBase) // An event fired when some aspect of the metadata has changed itkEventMacro(MetadataChangeEvent, IRISEvent) FIRES(MetadataChangeEvent) // Implementation of virtual functions from parent class void RegisterWithLayer(ImageWrapperBase *layer) {} void UnRegisterFromLayer(ImageWrapperBase *layer, bool being_deleted) {} // Parent model assignment override virtual void SetParentModel(GlobalUIModel *parent); // Function called in response to events virtual void OnUpdate(); // Access the individual models irisGetMacro(ImageDimensionsModel, AbstractSimpleUIntVec3Property *) irisGetMacro(ImageSpacingModel, AbstractSimpleDoubleVec3Property *) irisGetMacro(ImageOriginModel, AbstractSimpleDoubleVec3Property *) irisGetMacro(ImageItkCoordinatesModel, AbstractSimpleDoubleVec3Property *) irisGetMacro(ImageNiftiCoordinatesModel, AbstractSimpleDoubleVec3Property *) irisGetMacro(ImageMinMaxModel, AbstractSimpleDoubleVec2Property *) irisGetMacro(ImageOrientationModel, AbstractSimpleStringProperty *) // Access the internally stored filter irisSimplePropertyAccessMacro(MetadataFilter, std::string) // The voxel coordinate model just refers to the parent mode AbstractRangedUIntVec3Property *GetImageVoxelCoordinatesModel() const { return m_ParentModel->GetCursorPositionModel(); } /** Number of rows in the metadata table */ int GetMetadataRows(); /** Get and entry in the metadata table */ std::string GetMetadataCell(int row, int col); protected: SmartPtr m_ImageSpacingModel; SmartPtr m_ImageOriginModel; SmartPtr m_ImageItkCoordinatesModel; SmartPtr m_ImageNiftiCoordinatesModel; SmartPtr m_ImageDimensionsModel; SmartPtr m_ImageMinMaxModel; SmartPtr m_ImageOrientationModel; SmartPtr m_MetadataFilterModel; bool GetImageDimensions(Vector3ui &value); bool GetImageOrigin(Vector3d &value); bool GetImageSpacing(Vector3d &value); bool GetImageItkCoordinates(Vector3d &value); bool GetImageNiftiCoordinates(Vector3d &value); bool GetImageMinMax(Vector2d &value); bool GetImageOrientation(std::string &value); // Update the list of keys managed by the metadata void UpdateMetadataIndex(); // A list of metadata keys obeying the current filter std::vector m_MetadataKeys; ImageInfoModel(); virtual ~ImageInfoModel() {} }; #endif // IMAGEINFOMODEL_H itksnap-3.4.0/GUI/Model/ImageRegistrationManager.cxx000066400000000000000000000147361263013355200223220ustar00rootroot00000000000000#include "ImageRegistrationManager.h" #include "GenericImageData.h" #include #include #include // #include #include #include #include #include #include #include #include ImageRegistrationManager::ImageRegistrationManager() { m_RegistrationResultLock = itk::FastMutexLock::New(); m_RegistrationResult.Transform = TransformBase::New(); } void ImageRegistrationManager::PerformRegistration(GenericImageData *imageData, RegistrationMode in_mode, RegistrationMetric in_metric, RegistrationInit in_init) { // Get the things we will be registering typedef itk::Image ImageType; // The main image already loaded - this will be the fixed image ImageWrapperBase *main = imageData->GetMain(); ImageWrapperBase *overlay = imageData->GetLastOverlay(); // Cast to floating point format SmartPtr castMain = main->GetDefaultScalarRepresentation()->CreateCastToFloatPipeline(); SmartPtr castOverlay = overlay->GetDefaultScalarRepresentation()->CreateCastToFloatPipeline(); // Apply some default smoothing to the images, with standard deviation equal to twice // the smallest image spacing typedef itk::RecursiveGaussianImageFilter SmoothFilter; SmartPtr smoothMain = SmoothFilter::New(); smoothMain->SetInput(castMain->GetOutput()); smoothMain->SetSigma(castMain->GetOutput()->GetSpacing().GetVnlVector().min_value() * 1.0); smoothMain->InPlaceOn(); SmartPtr smoothOverlay = SmoothFilter::New(); smoothOverlay->SetInput(castOverlay->GetOutput()); smoothOverlay->SetSigma(castOverlay->GetOutput()->GetSpacing().GetVnlVector().min_value() * 1.0); smoothOverlay->InPlaceOn(); // Set up registration (for now, rigid is the default) typedef itk::Euler3DTransform TransformType; typedef itk::ImageRegistrationMethodv4 RegMethod; // Set up the registration method SmartPtr method = RegMethod::New(); // Set the fixed and moving images method->SetFixedImage(smoothMain->GetOutput()); method->SetMovingImage(smoothOverlay->GetOutput()); // Set the metric typedef itk::MattesMutualInformationImageToImageMetricv4 MetricType; SmartPtr metric = MetricType::New(); metric->SetNumberOfHistogramBins(32); metric->SetUseMovingImageGradientFilter( false ); metric->SetUseFixedImageGradientFilter( false ); metric->SetUseFixedSampledPointSet( false ); metric->SetVirtualDomainFromImage(castMain->GetOutput()); method->SetMetric(metric); method->SetMetricSamplingStrategy(RegMethod::REGULAR); method->SetMetricSamplingPercentage(0.1); // Set up the number of shrink levels RegMethod::ShrinkFactorsArrayType shrinkArray(2); shrinkArray[0] = 4; shrinkArray[1] = 2; method->SetNumberOfLevels(2); method->SetShrinkFactorsPerLevel(shrinkArray); RegMethod::SmoothingSigmasArrayType smoothArray(2); smoothArray.fill(0.0); method->SetSmoothingSigmasPerLevel(smoothArray); // Set up the scales estimator typedef itk::RegistrationParameterScalesFromPhysicalShift ScalesEstimatorType; SmartPtr scalesEstimator = ScalesEstimatorType::New(); scalesEstimator->SetMetric( metric ); scalesEstimator->SetTransformForward( true ); // Set up the optimizer typedef itk::ConjugateGradientLineSearchOptimizerv4Template OptimizerType; SmartPtr optimizer = OptimizerType::New(); optimizer->SetLowerLimit( 0 ); optimizer->SetUpperLimit( 2 ); optimizer->SetEpsilon( 0.2 ); optimizer->SetLearningRate( 0.25 ); optimizer->SetMaximumStepSizeInPhysicalUnits( 0.25 ); optimizer->SetNumberOfIterations( 1000 ); optimizer->SetScalesEstimator( scalesEstimator ); optimizer->SetDoEstimateScales( true ); optimizer->SetMinimumConvergenceValue( 1e-6 ); optimizer->SetConvergenceWindowSize( 10 ); optimizer->SetDoEstimateLearningRateAtEachIteration( true ); optimizer->SetDoEstimateLearningRateOnce( false ); // Set the optimizer method->SetOptimizer(optimizer); // Print out the optimizer status at every iteration. typedef itk::MemberCommand CommandType; SmartPtr command = CommandType::New(); command->SetCallbackFunction(this, &Self::OnRegistrationUpdate); optimizer->AddObserver(itk::IterationEvent(), command); // Perform registration method->Update(); } void ImageRegistrationManager ::OnRegistrationUpdate(itk::Object *caller, const itk::EventObject &event) { // Get the optimizer OptimizerType *optimizer = static_cast(caller); // Obtain a lock on the registration result m_RegistrationResultLock->Lock(); // Get the metric value m_RegistrationResult.MetricValue = optimizer->GetValue(); // TODO: split by transform type! // Get the parameters and convert them to a matrix typedef itk::Euler3DTransform TransformType; SmartPtr transform = TransformType::New(); transform->SetParameters(optimizer->GetCurrentPosition()); m_RegistrationResult.Transform->SetMatrix(transform->GetMatrix()); m_RegistrationResult.Transform->SetOffset(transform->GetOffset()); // Release the lock on the registration result m_RegistrationResultLock->Unlock(); // Fire an iteration event InvokeEvent(itk::IterationEvent()); } void ImageRegistrationManager::UpdateImageTransformFromRegistration(GenericImageData *imageData) { // Make a copy of the registration result m_RegistrationResultLock->Lock(); RegistrationResult result = m_RegistrationResult; m_RegistrationResultLock->Unlock(); std::cout << "METRIC Value: " << result.MetricValue << std::endl; // Apply the registration result to the data imageData->GetLastOverlay()->SetITKTransform( imageData->GetMain()->GetImageBase(), result.Transform); } double ImageRegistrationManager::GetRegistrationObjective() { return m_RegistrationResult.MetricValue; } itksnap-3.4.0/GUI/Model/ImageRegistrationManager.h000066400000000000000000000042361263013355200217410ustar00rootroot00000000000000#ifndef IMAGEREGISTRATIONMANAGER_H #define IMAGEREGISTRATIONMANAGER_H #include "AbstractModel.h" #include "itkMatrixOffsetTransformBase.h" class GenericImageData; namespace itk { class FastMutexLock; template class ConjugateGradientLineSearchOptimizerv4Template; template class MatrixOffsetTransformBase; } /** * This class encapsulates the logic of rigid/affine image registration. It * can perform registration and provide registration results. */ class ImageRegistrationManager : public AbstractModel { public: irisITKObjectMacro(ImageRegistrationManager, AbstractModel) // Registration enums enum RegistrationMode { INITONLY = 0, RIGID, SIMILARITY, AFFINE, INVALID_MODE }; enum RegistrationMetric { NMI = 0, NCC, SSD, INVALID_METRIC }; enum RegistrationInit { HEADERS=0, CENTERS, INVALID_INIT }; // Perform registration with provided parameters void PerformRegistration(GenericImageData *imageData, RegistrationMode mode, RegistrationMetric metric, RegistrationInit init); // Update image data with the registration progress void UpdateImageTransformFromRegistration(GenericImageData *imageData); // Get the value of the objective function double GetRegistrationObjective(); protected: ImageRegistrationManager(); ~ImageRegistrationManager() {} void OnRegistrationUpdate(itk::Object *caller, const itk::EventObject &event); private: // Registration typedefs typedef itk::ConjugateGradientLineSearchOptimizerv4Template OptimizerType; typedef itk::MatrixOffsetTransformBase TransformBase; // Representation of the result of image registration struct RegistrationResult { double MetricValue; SmartPtr Transform; }; // Thread-safe variable for storing registration results RegistrationResult m_RegistrationResult; SmartPtr m_RegistrationResultLock; // Initial direction matrix of the image vnl_matrix m_InitialDirectionMatrix; vnl_vector m_InitialOrigin; }; #endif // IMAGEREGISTRATIONMANAGER_H itksnap-3.4.0/GUI/Model/IntensityCurveModel.cxx000066400000000000000000000451451263013355200213640ustar00rootroot00000000000000#include "IntensityCurveModel.h" #include "ImageWrapperBase.h" #include "IRISApplication.h" #include "itkImageBase.h" #include "ScalarImageHistogram.h" #include "GlobalUIModel.h" #include "IntensityCurveInterface.h" #include "DisplayMappingPolicy.h" #include "LayerAssociation.txx" template class LayerAssociation; IntensityCurveModel::IntensityCurveModel() : IntensityCurveModelBase() { // Create the child model for the moving control point id m_MovingControlIdModel = wrapGetterSetterPairAsProperty( this, &Self::GetMovingControlPointIdValueAndRange, &Self::SetMovingControlPointId); // Create the child model for the control point coordinates m_MovingControlXYModel = wrapGetterSetterPairAsProperty( this, &Self::GetMovingControlPointPositionAndRange, &Self::SetMovingControlPointPosition); // Min/max/level/window models for(int i = 0; i < 4; i++) m_IntensityRangeModel[i] = wrapIndexedGetterSetterPairAsProperty( this, i, &Self::GetIntensityRangeIndexedValueAndRange, &Self::SetIntensityRangeIndexedValue); // Histogram bin size and other controls m_HistogramBinSizeModel = wrapGetterSetterPairAsProperty( this, &Self::GetHistogramBinSizeValueAndRange, &Self::SetHistogramBinSize); m_HistogramCutoffModel = wrapGetterSetterPairAsProperty( this, &Self::GetHistogramCutoffValueAndRange, &Self::SetHistogramCutoff); m_HistogramScaleModel = wrapGetterSetterPairAsProperty( this, &Self::GetHistogramScale, &Self::SetHistogramScale); // Model events are also state changes for GUI activation Rebroadcast(this, ModelUpdateEvent(), StateMachineChangeEvent()); } IntensityCurveModel::~IntensityCurveModel() { } AbstractContinuousImageDisplayMappingPolicy * IntensityCurveModel ::GetDisplayPolicy() { ImageWrapperBase *layer = this->GetLayer(); if(layer) return dynamic_cast (layer->GetDisplayMapping()); return NULL; } void IntensityCurveModel ::RegisterWithLayer(ImageWrapperBase *layer) { IntensityCurveLayerProperties &p = GetProperties(); // Listen to changes in the layer's intensity curve unsigned long tag = Rebroadcast(layer, WrapperDisplayMappingChangeEvent(), ModelUpdateEvent()); // Set a flag so we don't register a listener again p.SetObserverTag(tag); // If this is the first time we are registered with this layer, we are going // to set the histogram cutoff optimally. The user may change this later so // we only do this for the first-time registration. // // TODO: it may make more sense for this to be a property that's associated // with the image for future times that it is loaded. Then the cutoff would // have to be stored in the ImageWrapper. if(p.IsFirstTime()) { // Set the cutoff automatically const ScalarImageHistogram *hist = layer->GetHistogram(0); p.SetHistogramCutoff(hist->GetReasonableDisplayCutoff(0.95, 0.6)); p.SetFirstTime(false); } } void IntensityCurveModel ::UnRegisterFromLayer(ImageWrapperBase *layer, bool being_deleted) { if(!being_deleted) { // It's safe to call GetProperties() unsigned long tag = GetProperties().GetObserverTag(); if(tag) { layer->GetDisplayMapping()->RemoveObserver(tag); } } } const ScalarImageHistogram * IntensityCurveModel ::GetHistogram() { AbstractContinuousImageDisplayMappingPolicy *dmp = this->GetDisplayPolicy(); assert(dmp); // Get the properties for the layer IntensityCurveLayerProperties *p = m_LayerProperties[m_Layer]; // Figure out the number of bins that we want unsigned int nBins = DEFAULT_HISTOGRAM_BINS; if(m_ViewportReporter && m_ViewportReporter->CanReportSize()) { unsigned int width = m_ViewportReporter->GetViewportSize()[0]; nBins = width / p->GetHistogramBinSize(); } // Get the histogram return dmp->GetHistogram(nBins); } IntensityCurveLayerProperties::IntensityCurveLayerProperties() { m_ObserverTag = 0; m_HistogramLog = false; m_MovingControlPoint = false; m_HistogramBinSize = 10; m_HistogramCutoff = 1; m_FirstTime = true; } IntensityCurveLayerProperties::~IntensityCurveLayerProperties() { } IntensityCurveInterface * IntensityCurveModel::GetCurve() { return (this->GetDisplayPolicy()) ? this->GetDisplayPolicy()->GetIntensityCurve() : NULL; } Vector2d IntensityCurveModel::GetNativeImageRangeForCurve() { assert(this->GetDisplayPolicy()); return this->GetDisplayPolicy()->GetNativeImageRangeForCurve(); } Vector2d IntensityCurveModel::GetCurveRange() { IntensityCurveInterface *curve = this->GetCurve(); assert(curve); // Get the control point range float t0, y0, t1, y1; curve->GetControlPoint(0, t0, y0); curve->GetControlPoint(curve->GetControlPointCount() - 1, t1, y1); // Get the reference intensity range Vector2d range = this->GetNativeImageRangeForCurve(); // Map the extents of the control points to image units Vector2d outRange; outRange[0] = range[0] * (1 - t0) + range[1] * t0; outRange[1] = range[0] * (1 - t1) + range[1] * t1; return outRange; } Vector2d IntensityCurveModel::GetVisibleImageRange() { IntensityCurveInterface *curve = this->GetCurve(); assert(curve); // Get the control point range float t0, y0, t1, y1; curve->GetControlPoint(0, t0, y0); curve->GetControlPoint(curve->GetControlPointCount() - 1, t1, y1); // Get the reference intensity range Vector2d range = this->GetNativeImageRangeForCurve(); // Compute the range over which the curve is plotted, where [0 1] is the // image intensity range float z0 = std::min(t0, 0.0f); float z1 = std::max(t1, 1.0f); // Compute the range over which the curve is plotted, in intensity units Vector2d outRange; outRange[0] = range[0] * (1 - z0) + range[1] * z0; outRange[1] = range[0] * (1 - z1) + range[1] * z1; return outRange; } bool IntensityCurveModel ::UpdateControlPoint(size_t i, float t, float x) { IntensityCurveInterface *curve = this->GetCurve(); // Must be in range assert(i < curve->GetControlPointCount()); // Get the current values float told, xold; curve->GetControlPoint(i, told, xold); // The control value should be in range // if(t < 0.0 || t > 1.0) // return false; // First and last control points are treated specially because they // provide windowing style behavior int last = curve->GetControlPointCount()-1; if (i == 0 || i == (size_t) last) { // Get the current domain float tMin,tMax,x; curve->GetControlPoint(0, tMin, x); curve->GetControlPoint(last,tMax, x); // Check if the new domain is valid float epsilon = 0.02; if (i == 0 && t < tMax - epsilon) tMin = t; else if (i == (size_t) last && t > tMin + epsilon) tMax = t; else // One of the conditions failed; the window has size <= 0 return false; // Change the domain of the curve curve->ScaleControlPointsToWindow(tMin, tMax); } else { // Check whether the X coordinate is in range if (x < 0.0 || x > 1.0) return false; // Update the control point curve->UpdateControlPoint(i, t, x); // Check the curve for monotonicity if(!curve->IsMonotonic()) { curve->UpdateControlPoint(i, told, xold); return false; } } return true; } int IntensityCurveModel ::GetControlPointInVicinity(float x, float y, int pixelRadius) { assert(m_ViewportReporter && m_ViewportReporter->CanReportSize()); Vector2ui vp = m_ViewportReporter->GetViewportSize(); IntensityCurveInterface *curve = GetCurve(); float rx = pixelRadius * 1.0f / vp[0]; float ry = pixelRadius * 1.0f / vp[1]; float fx = 1.0f / (rx * rx); float fy = 1.0f / (ry * ry); float minDistance = 1.0f; int nearestPoint = -1; for (unsigned int c=0;cGetControlPointCount();c++) { // Get the next control point float cx,cy; curve->GetControlPoint(c,cx,cy); // Check the distance to the control point float d = (cx - x) * (cx - x) * fx + (cy - y) * (cy - y) * fy; if (minDistance >= d) { minDistance = d; nearestPoint = c; } } // Negative: return -1 return nearestPoint; } Vector3d IntensityCurveModel::GetEventCurveCoordiantes(const Vector3d &x) { float t0, t1, xDummy; GetCurve()->GetControlPoint(0, t0, xDummy); GetCurve()->GetControlPoint( GetCurve()->GetControlPointCount() - 1, t1, xDummy); float z0 = std::min(t0, 0.0f); float z1 = std::max(t1, 1.0f); // Scale the display so that leftmost point to plot maps to 0, rightmost to 1 return Vector3d(x[0] * (z1 - z0) + z0, x[1], x[2]); } bool IntensityCurveModel::ProcessMousePressEvent(const Vector3d &x) { // Check the control point affected by the event Vector3d xCurve = this->GetEventCurveCoordiantes(x); GetProperties().SetMovingControlPoint( GetControlPointInVicinity(xCurve[0], xCurve[1], 5)); // SetCursor(m_MovingControlPoint); // Clear the dragged flag m_FlagDraggedControlPoint = false; return true; } bool IntensityCurveModel::ProcessMouseDragEvent(const Vector3d &x) { Vector3d xCurve = this->GetEventCurveCoordiantes(x); if (GetProperties().GetMovingControlPoint() >= 0) { // Update the moving control point if(UpdateControlPoint(GetProperties().GetMovingControlPoint(), xCurve[0], xCurve[1])) { InvokeEvent(ModelUpdateEvent()); } // Set the dragged flag m_FlagDraggedControlPoint = true; return true; } return false; } bool IntensityCurveModel::ProcessMouseReleaseEvent(const Vector3d &x) { Vector3d xCurve = this->GetEventCurveCoordiantes(x); if (GetProperties().GetMovingControlPoint() >= 0) { // Update the moving control point if (UpdateControlPoint(GetProperties().GetMovingControlPoint(), xCurve[0], xCurve[1])) { InvokeEvent(ModelUpdateEvent()); } // Set the dragged flag m_FlagDraggedControlPoint = true; return true; } return false; } bool IntensityCurveModel ::GetMovingControlPointIdValueAndRange(int &value, NumericValueRange *range) { if(!m_Layer) return false; if(range) { range->Minimum = 1; range->Maximum = GetCurve()->GetControlPointCount(); range->StepSize = 1; } value = GetProperties().GetMovingControlPoint() + 1; return value >= 1; } void IntensityCurveModel ::SetMovingControlPointId(int value) { GetProperties().SetMovingControlPoint(value - 1); InvokeEvent(ModelUpdateEvent()); } bool IntensityCurveModel ::GetIntensityRangeIndexedValueAndRange( int index, double &value, NumericValueRange *range) { if(!this->GetCurve()) return false; // Get the range of the curve in image units Vector2d crange = this->GetCurveRange(); // Level and window switch(index) { case 0 : value = crange[0]; break; case 1 : value = crange[1]; break; case 2 : value = (crange[0] + crange[1]) / 2; break; case 3 : value = (crange[1] - crange[0]); break; } // Compute range and step if needed if(range) { // The range for the window and level are basically unlimited. To be safe, we // set it to be two orders of magnitude greater than the largest absolute // value in the image. Vector2d irange = this->GetNativeImageRangeForCurve(); double step = pow(10, floor(0.5 + log10(irange[1] - irange[0]) - 3)); double order = log10(std::max(fabs(irange[0]), fabs(irange[1]))); double maxabsval = pow(10, ceil(order)+2); // Set the ranges for each of the four properties switch(index) { case 0 : case 1 : case 2 : range->Minimum = -maxabsval; range->Maximum = maxabsval; break; case 3 : range->Minimum = 0.0; range->Maximum = maxabsval; break; } range->StepSize = step; } // Value is valid return true; } void IntensityCurveModel::SetIntensityRangeIndexedValue(int index, double value) { // Get the curve IntensityCurveInterface *curve = this->GetCurve(); // Get the intensity range and curve range in image units Vector2d irange = this->GetNativeImageRangeForCurve(); Vector2d crange = this->GetCurveRange(); // Get the current window and level double win = crange[1] - crange[0]; double level = (crange[0] + crange[1]) / 2; double step = pow(10, floor(0.5 + log10(irange[1] - irange[0]) - 3)); // How we set the output range depends on what property was changed switch(index) { case 0: // min crange[0] = value; if(crange[0] >= crange[1]) crange[1] = crange[0] + step; break; case 1: // max crange[1] = value; if(crange[1] <= crange[0]) crange[0] = crange[1] - step; break; case 2: // level (mid-range) crange[0] = value - win / 2; crange[1] = value + win / 2; break; case 3: // window if(value <= 0) value = step; crange[0] = level - value / 2; crange[1] = level + value / 2; break; } // Map the range into curve units double t0 = (crange[0] - irange[0]) / (irange[1] - irange[0]); double t1 = (crange[1] - irange[0]) / (irange[1] - irange[0]); curve->ScaleControlPointsToWindow(t0, t1); } bool IntensityCurveModel ::GetMovingControlPointPositionAndRange( Vector2d &pos, NumericValueRange *range) { AbstractContinuousImageDisplayMappingPolicy *dmp = this->GetDisplayPolicy(); if(!dmp) return false; // If no control point selected, the value is invalid if(GetProperties().GetMovingControlPoint() < 0) return false; IntensityCurveInterface *curve = dmp->GetIntensityCurve(); int cp = GetProperties().GetMovingControlPoint(); // Get the absolute range Vector2d iAbsRange = dmp->GetNativeImageRangeForCurve(); // Compute the position float x, t; curve->GetControlPoint(cp, t, x); double intensity = iAbsRange[0] * (1-t) + iAbsRange[1] * t; pos = Vector2d(intensity,x); // Compute the range if(range) { float t0, x0, t1, x1; double iAbsSpan = iAbsRange[1] - iAbsRange[0]; double xStep = pow(10, floor(0.5 + log10(iAbsSpan) - 3)); double order = log10(std::max(fabs(iAbsRange[0]), fabs(iAbsRange[1]))); double maxabsval = pow(10, ceil(order)+2); if(cp == 0) { range->Minimum[0] = -maxabsval; } else { curve->GetControlPoint(cp - 1, t0, x0); range->Minimum[0] = iAbsRange[0] + iAbsSpan * t0 + xStep; } if(cp == (int)(curve->GetControlPointCount() - 1)) { range->Maximum[0] = maxabsval; } else { curve->GetControlPoint(cp + 1, t1, x1); range->Maximum[0] = iAbsRange[0] + iAbsSpan * t1 - xStep; } if(cp == 0) { range->Minimum[1] = range->Maximum[1] = 0; } else if(cp == (int)(curve->GetControlPointCount() - 1)) { range->Minimum[1] = range->Maximum[1] = 1; } else { range->Minimum[1] = x0 + 0.01; range->Maximum[1] = x1 - 0.01; } range->StepSize[0] = xStep; range->StepSize[1] = 0.01; } return true; } void IntensityCurveModel::SetMovingControlPointPosition(Vector2d p) { AbstractContinuousImageDisplayMappingPolicy *dmp = this->GetDisplayPolicy(); assert(dmp); IntensityCurveInterface *curve = dmp->GetIntensityCurve(); Vector2d iAbsRange = dmp->GetNativeImageRangeForCurve(); double t = (p[0] - iAbsRange[0]) / (iAbsRange[1] - iAbsRange[0]); curve->UpdateControlPoint(GetProperties().GetMovingControlPoint(), t, p[1]); } void IntensityCurveModel ::OnControlPointNumberDecreaseAction() { IntensityCurveInterface *curve = this->GetCurve(); if (curve->GetControlPointCount() > 3) { curve->Initialize(curve->GetControlPointCount() - 1); GetProperties().SetMovingControlPoint(0); // m_BoxCurve->GetInteractor()->SetMovingControlPoint(0); // OnWindowLevelChange(); InvokeEvent(ModelUpdateEvent()); } // if (m_Curve->GetControlPointCount() == 3) // m_BtnCurveLessControlPoint->deactivate(); } void IntensityCurveModel ::OnControlPointNumberIncreaseAction() { this->GetCurve()->Initialize(this->GetCurve()->GetControlPointCount() + 1); InvokeEvent(ModelUpdateEvent()); } void IntensityCurveModel::OnResetCurveAction() { this->GetCurve()->Reset(); InvokeEvent(ModelUpdateEvent()); } void IntensityCurveModel::OnUpdate() { Superclass::OnUpdate(); } AbstractRangedDoubleProperty * IntensityCurveModel::GetIntensityRangeModel( IntensityRangePropertyType index) const { return m_IntensityRangeModel[index]; } void IntensityCurveModel::OnAutoFitWindow() { // There must be a layer AbstractContinuousImageDisplayMappingPolicy *dmp = this->GetDisplayPolicy(); assert(dmp); // Get the histogram dmp->AutoFitContrast(); } bool IntensityCurveModel ::GetHistogramBinSizeValueAndRange( int &value, NumericValueRange *range) { if(m_Layer) { value = (int) GetProperties().GetHistogramBinSize(); if(range) { range->Minimum = 1; range->Maximum = m_Layer->GetNumberOfVoxels() / 10; range->StepSize = 1; } return true; } return false; } void IntensityCurveModel ::SetHistogramBinSize(int value) { assert(m_Layer); GetProperties().SetHistogramBinSize((unsigned int) value); InvokeEvent(ModelUpdateEvent()); } bool IntensityCurveModel ::GetHistogramCutoffValueAndRange( double &value, NumericValueRange *range) { if(m_Layer) { value = GetProperties().GetHistogramCutoff() * 100.0; if(range) *range = NumericValueRange(0.1, 100, 1); return true; } return false; } void IntensityCurveModel ::SetHistogramCutoff(double value) { assert(m_Layer); GetProperties().SetHistogramCutoff(value / 100.0); InvokeEvent(ModelUpdateEvent()); } bool IntensityCurveModel ::GetHistogramScale(bool &value) { if(m_Layer) { value = GetProperties().IsHistogramLog(); return true; } return false; } void IntensityCurveModel ::SetHistogramScale(bool value) { assert(m_Layer); GetProperties().SetHistogramLog(value); InvokeEvent(ModelUpdateEvent()); } bool IntensityCurveModel::CheckState(IntensityCurveModel::UIState state) { // All flags are false if no layer is loaded if(this->GetLayer() == NULL) return false; // Otherwise get the properties IntensityCurveLayerProperties &p = this->GetProperties(); int cp = p.GetMovingControlPoint(); switch(state) { case UIF_LAYER_ACTIVE: return true; case UIF_CONTROL_SELECTED: return cp >= 0; } return false; } itksnap-3.4.0/GUI/Model/IntensityCurveModel.h000066400000000000000000000167031263013355200210070ustar00rootroot00000000000000#ifndef INTENSITYCURVEMODEL_H #define INTENSITYCURVEMODEL_H #include #include #include #include "GenericImageData.h" #include #include "PropertyModel.h" class GlobalUIModel; class IntensityCurveModel; class AbstractContinuousImageDisplayMappingPolicy; /** A set of properties associated with a specific layer */ class IntensityCurveLayerProperties { public: /** Desired histogram bin size, in pixels */ irisGetSetMacro(HistogramBinSize, unsigned int) irisIsMacro(HistogramLog) irisSetMacro(HistogramLog, bool) /** How much of the height of the histogram is shown */ irisGetSetMacro(HistogramCutoff, double) /** The control point currently moving */ irisGetSetMacro(MovingControlPoint, int) /** Is this the first time we are registered with the layer? */ irisIsMacro(FirstTime) irisSetMacro(FirstTime, bool) irisGetSetMacro(ObserverTag, unsigned long) IntensityCurveLayerProperties(); virtual ~IntensityCurveLayerProperties(); protected: unsigned int m_HistogramBinSize; bool m_HistogramLog; double m_HistogramCutoff; int m_MovingControlPoint; // Whether or not we have been registered with this layer before. // By default, this is set to true. This flag allows us to perform // some one-off initialization stuff (like set the histogram cutoff) // in RegisterWithLayer bool m_FirstTime; // Whether or not we are already listening to events from this layer unsigned long m_ObserverTag; }; typedef AbstractLayerAssociatedModel< IntensityCurveLayerProperties, ImageWrapperBase> IntensityCurveModelBase; /** The intensity curve model is used to interact with the intensity curve in the adjust contrast user interface. The model is associated with a single layer, or no layer at all. There is a one-to-one relationship between the model and the view, and a one-to-many relationship between the model and the image layers. For each layer, the model maintains a properties object of type IntensityCurveLayerProperties. To change the layer with which the model is associated, call SetLayer. The model will fire an event, to which the UI should listen, refreshing the UI in response. */ class IntensityCurveModel : public IntensityCurveModelBase { public: irisITKObjectMacro(IntensityCurveModel, IntensityCurveModelBase) /** Before using the model, it must be coupled with a size reporter */ irisGetSetMacro(ViewportReporter, ViewportSizeReporter *) // Implementation of virtual functions from parent class void RegisterWithLayer(ImageWrapperBase *layer); void UnRegisterFromLayer(ImageWrapperBase *layer, bool being_deleted); /** States in which the model can be, which allow the activation and deactivation of various widgets in the interface */ enum UIState { UIF_LAYER_ACTIVE, UIF_CONTROL_SELECTED }; /** Get the curve stored in the current layer */ IntensityCurveInterface *GetCurve(); /** Get the input intensity range for the curve (native) */ Vector2d GetNativeImageRangeForCurve(); /** * Get the current min and max of the curve in native image units */ Vector2d GetCurveRange(); /** * Get the visibile intensity range (in native image intensity units). * The lower bound of this range is the minimum of the first control point's * intensity and the minimum of the image intensity. The upper bound is the * maximum of the last control point's intensity and the maximum image value. */ Vector2d GetVisibleImageRange(); /** Check the state flags above */ bool CheckState(UIState state); /** Get the histogram of the current layer */ const ScalarImageHistogram *GetHistogram(); /** Process curve interaction event */ bool ProcessMousePressEvent(const Vector3d &x); bool ProcessMouseDragEvent(const Vector3d &x); bool ProcessMouseReleaseEvent(const Vector3d &x); /** Reduce number of control points */ void OnControlPointNumberDecreaseAction(); /** Increase number of control points */ void OnControlPointNumberIncreaseAction(); /** Reset the curve */ void OnResetCurveAction(); /** Try changing the control point to new values */ bool UpdateControlPoint(size_t i, float t, float x); /** Update the model in response to upstream events */ virtual void OnUpdate(); // Access the models irisGetMacro(MovingControlXYModel, AbstractRangedDoubleVec2Property *) irisGetMacro(MovingControlIdModel, AbstractRangedIntProperty *) irisGetMacro(HistogramBinSizeModel, AbstractRangedIntProperty *) irisGetMacro(HistogramCutoffModel, AbstractRangedDoubleProperty *) irisGetMacro(HistogramScaleModel, AbstractSimpleBooleanProperty *) // This enum lists the types of global intensity range properties for which // separate models are defined enum IntensityRangePropertyType { MINIMUM = 0, MAXIMUM, LEVEL, WINDOW }; // Access the models for the intensity min, max, level and window. These // models are specified by an index AbstractRangedDoubleProperty *GetIntensityRangeModel( IntensityRangePropertyType index) const; void OnAutoFitWindow(); protected: IntensityCurveModel(); virtual ~IntensityCurveModel(); AbstractContinuousImageDisplayMappingPolicy *GetDisplayPolicy(); // A size reporter delegate ViewportSizeReporter *m_ViewportReporter; // Whether the control point is being dragged bool m_FlagDraggedControlPoint; Vector3d GetEventCurveCoordiantes(const Vector3d &x); int GetControlPointInVicinity(float x, float y, int pixelRadius); // Model for the control point index SmartPtr m_MovingControlIdModel; // Moving control point Id access methods bool GetMovingControlPointIdValueAndRange(int &value, NumericValueRange *range); void SetMovingControlPointId(int value); // The child models for control point X and Y coordinates SmartPtr m_MovingControlXYModel; // Moving control point position access methods bool GetMovingControlPointPositionAndRange(Vector2d &lw, NumericValueRange *range); void SetMovingControlPointPosition(Vector2d p); // Child models for min, max, window and level SmartPtr m_IntensityRangeModel[4]; // Window and level access methods bool GetIntensityRangeIndexedValueAndRange(int index, double &value, NumericValueRange *range); void SetIntensityRangeIndexedValue(int index, double value); // Child model for histogram bin size SmartPtr m_HistogramBinSizeModel; // Histogram bin size access methods bool GetHistogramBinSizeValueAndRange(int &value, NumericValueRange *range); void SetHistogramBinSize(int value); // Child model for histogram cutoff SmartPtr m_HistogramCutoffModel; // Histogram bin size access methods bool GetHistogramCutoffValueAndRange(double &value, NumericValueRange *range); void SetHistogramCutoff(double value); // Child model for histogram scale SmartPtr m_HistogramScaleModel; // Histogram bin size access methods bool GetHistogramScale(bool &value); void SetHistogramScale(bool value); }; #endif // INTENSITYCURVEMODEL_H itksnap-3.4.0/GUI/Model/LabelEditorModel.cxx000066400000000000000000000244221263013355200205520ustar00rootroot00000000000000#include "LabelEditorModel.h" #include "IRISException.h" #include "IRISApplication.h" #include "IRISException.h" LabelEditorModel::LabelEditorModel() { // Create a new instance of the model m_CurrentLabelModel = ConcreteColorLabelPropertyModel::New(); // When the value in the model changes, we need to rebroadcast this // as a change in the model, so the GUI can update itself Rebroadcast(m_CurrentLabelModel, ValueChangedEvent(), ModelUpdateEvent()); // The model update events should also be rebroadcast as state changes Rebroadcast(this, ModelUpdateEvent(), StateMachineChangeEvent()); // Initialize the wrapper models m_CurrentLabelDescriptionModel = wrapGetterSetterPairAsProperty( this, &Self::GetCurrentLabelDescription, &Self::SetCurrentLabelDescription); m_CurrentLabelIdModel = wrapGetterSetterPairAsProperty( this, &Self::GetCurrentLabelIdValueAndRange, &Self::SetCurrentLabelId); m_CurrentLabelOpacityModel = wrapGetterSetterPairAsProperty( this, &Self::GetCurrentLabelOpacityValueAndRange, &Self::SetCurrentLabelOpacity); m_CurrentLabelHiddenStateModel = wrapGetterSetterPairAsProperty( this, &Self::GetCurrentLabelHiddenState, &Self::SetCurrentLabelHiddenState); m_CurrentLabelColorModel = wrapGetterSetterPairAsProperty( this, &Self::GetCurrentLabelColor, &Self::SetCurrentLabelColor); m_IsForegroundBackgroundModel = wrapGetterSetterPairAsProperty( this, &Self::GetIsForegroundBackground, &Self::SetIsForegroundBackground); } void LabelEditorModel::SetParentModel(GlobalUIModel *parent) { // Set the parent model m_Parent = parent; m_LabelTable = parent->GetDriver()->GetColorLabelTable(); // Stick the color label information into the domain object m_CurrentLabelModel->Initialize(m_LabelTable); m_CurrentLabelModel->SetValue( parent->GetDriver()->GetGlobalState()->GetDrawingColorLabel()); // Listen to events Rebroadcast(m_LabelTable, SegmentationLabelChangeEvent(), ModelUpdateEvent()); // Listen to changes in the active label event m_IsForegroundBackgroundModel->RebroadcastFromSourceProperty( m_Parent->GetGlobalState()->GetDrawingColorLabelModel()); m_IsForegroundBackgroundModel->RebroadcastFromSourceProperty( m_Parent->GetGlobalState()->GetDrawOverFilterModel()); } bool LabelEditorModel::GetCurrentLabelDescription(std::string &value) { if(GetAndStoreCurrentLabel()) { value = m_SelectedColorLabel.GetLabel(); return true; } return false; } void LabelEditorModel::SetCurrentLabelDescription(std::string value) { if(GetAndStoreCurrentLabel()) { m_SelectedColorLabel.SetLabel(value.c_str()); m_LabelTable->SetColorLabel(m_SelectedId, m_SelectedColorLabel); } } bool LabelEditorModel::GetCurrentLabelIdValueAndRange( int &value, NumericValueRange *domain) { // Get the numeric ID of the current label if(GetAndStoreCurrentLabel()) { value = m_SelectedId; if(domain) domain->Set(0, MAX_COLOR_LABELS, 1); return true; } return false; } void LabelEditorModel::SetCurrentLabelId(int value) { // This actually requires some funky stuff. Save it for later. } bool LabelEditorModel::GetCurrentLabelOpacityValueAndRange( int &value, NumericValueRange *domain) { if(GetAndStoreCurrentLabel()) { value = m_SelectedColorLabel.GetAlpha(); if(domain) domain->Set(0, 255, 1); return true; } return false; } void LabelEditorModel::SetCurrentLabelOpacity(int value) { if(GetAndStoreCurrentLabel()) { m_SelectedColorLabel.SetAlpha((unsigned char)(value)); m_LabelTable->SetColorLabel(m_SelectedId, m_SelectedColorLabel); } } bool LabelEditorModel::GetCurrentLabelHiddenState(iris_vector_fixed &value) { if(GetAndStoreCurrentLabel()) { value[0] = !m_SelectedColorLabel.IsVisible(); value[1] = !m_SelectedColorLabel.IsVisibleIn3D(); return true; } return false; } void LabelEditorModel::SetCurrentLabelHiddenState(iris_vector_fixed value) { if(GetAndStoreCurrentLabel()) { m_SelectedColorLabel.SetVisible(!value[0]); m_SelectedColorLabel.SetVisibleIn3D(!value[1]); m_LabelTable->SetColorLabel(m_SelectedId, m_SelectedColorLabel); } } bool LabelEditorModel::GetCurrentLabelColor(Vector3ui &value) { if(GetAndStoreCurrentLabel()) { value[0] = m_SelectedColorLabel.GetRGB(0); value[1] = m_SelectedColorLabel.GetRGB(1); value[2] = m_SelectedColorLabel.GetRGB(2); return true; } return false; } void LabelEditorModel::SetCurrentLabelColor(Vector3ui value) { if(GetAndStoreCurrentLabel()) { m_SelectedColorLabel.SetRGB((unsigned char) value[0], (unsigned char) value[1], (unsigned char) value[2]); m_LabelTable->SetColorLabel(m_SelectedId, m_SelectedColorLabel); } } bool LabelEditorModel::GetIsForegroundBackground(Vector2b &value) { if(GetAndStoreCurrentLabel()) { LabelType fg = m_Parent->GetGlobalState()->GetDrawingColorLabel(); DrawOverFilter bg = m_Parent->GetGlobalState()->GetDrawOverFilter(); value[0] = (fg == m_SelectedId); value[1] = (bg.CoverageMode == PAINT_OVER_ONE && bg.DrawOverLabel == m_SelectedId); return true; } return false; } void LabelEditorModel::SetIsForegroundBackground(Vector2b value) { GlobalState *gs = m_Parent->GetGlobalState(); if(GetAndStoreCurrentLabel()) { DrawOverFilter bg = gs->GetDrawOverFilter(); if(value[0]) { gs->SetDrawingColorLabel(m_SelectedId); } else { // Do nothing - there is no default label to switch to... } if(value[1]) { bg.CoverageMode = PAINT_OVER_ONE; bg.DrawOverLabel = m_SelectedId; gs->SetDrawOverFilter(bg); } else { bg.CoverageMode = PAINT_OVER_ALL; bg.DrawOverLabel = 0; gs->SetDrawOverFilter(bg); } } } bool LabelEditorModel::GetAndStoreCurrentLabel() { m_SelectedId = m_CurrentLabelModel->GetValue(); if(m_LabelTable->IsColorLabelValid(m_SelectedId)) { m_SelectedColorLabel = m_LabelTable->GetColorLabel(m_SelectedId); return true; } return false; } bool LabelEditorModel::CheckState(LabelEditorModel::UIState state) { bool valid = GetAndStoreCurrentLabel(); switch(state) { case UIF_EDITABLE_LABEL_SELECTED: return valid && m_SelectedId > 0; } return true; } bool LabelEditorModel::MakeNewLabel(bool copyCurrent) { bool valid = GetAndStoreCurrentLabel(); if(valid) { // Find the next insertion spot after the current selection LabelType insertpos = m_LabelTable->GetInsertionSpot(m_SelectedId); // If the insertion spot returned is zero, throw an exception (no room) if(insertpos == 0) return false; // Create a new label at this position and set the selection to it m_LabelTable->SetColorLabelValid(insertpos, true); // Duplicate current label if needed if(copyCurrent) { // Append ' copy' to the text of the color label std::string title = m_SelectedColorLabel.GetLabel(); if(title.substr(title.size() - 5) != " copy") title += " copy"; m_SelectedColorLabel.SetLabel(title.c_str()); m_LabelTable->SetColorLabel(insertpos, m_SelectedColorLabel); } // Select the new label m_CurrentLabelModel->SetValue(insertpos); return true; } return false; } bool LabelEditorModel::IsLabelDeletionDestructive() { if(GetAndStoreCurrentLabel()) return m_Parent->GetDriver()->GetNumberOfVoxelsWithLabel(m_SelectedId) > 0; else return false; } void LabelEditorModel::DeleteCurrentLabel() { if(GetAndStoreCurrentLabel() && m_SelectedId > 0) { // Get the global state GlobalState *gs = m_Parent->GetGlobalState(); // Compute the next available id that will be selected LabelType lnext = m_LabelTable->FindNextValidLabel(m_SelectedId, false); // Check if the drawing labe is pointing to the current id if(gs->GetDrawingColorLabel() == m_SelectedId) gs->SetDrawingColorLabel(lnext); // Check for the draw over label as well. Here we use 0 as the replacement DrawOverFilter dof = gs->GetDrawOverFilter(); if(dof.DrawOverLabel == m_SelectedId) gs->SetDrawOverFilter(DrawOverFilter(dof.CoverageMode, 0)); // Replace all the voxels with the current label by zero size_t nUpdated = m_Parent->GetDriver()->ReplaceLabel(0, m_SelectedId); // If some voxels were removed, reset the undo state, because // changes to the label metadata are not undoable operations (not yet) if(nUpdated > 0) { // This operation can not be undone! m_Parent->GetDriver()->ClearUndoPoints(); } // Change the current selection m_CurrentLabelModel->SetValue(lnext); // Invalidate the current label m_LabelTable->SetColorLabelValid(m_SelectedId, false); } } void LabelEditorModel::ResetLabels() { m_LabelTable->InitializeToDefaults(); } bool LabelEditorModel::ReassignLabelId(LabelType newid) { // Check if the ID is taken if(m_LabelTable->IsColorLabelValid(newid)) return false; // Do the relabeling if(GetAndStoreCurrentLabel() && m_SelectedId > 0) { // Get the global state GlobalState *gs = m_Parent->GetGlobalState(); // Create a new valid label and copy current label m_LabelTable->SetColorLabelValid(newid, true); m_LabelTable->SetColorLabel(newid, m_SelectedColorLabel); // Check if the drawing label is pointing to the current id if(gs->GetDrawingColorLabel() == m_SelectedId) gs->SetDrawingColorLabel(newid); // Check for the draw over label as well. Here we use 0 as the replacement DrawOverFilter dof = gs->GetDrawOverFilter(); if(dof.DrawOverLabel == m_SelectedId) gs->SetDrawOverFilter(DrawOverFilter(dof.CoverageMode, newid)); // Reassign the ID size_t nUpdated = m_Parent->GetDriver()->ReplaceLabel(newid, m_SelectedId); // There is no undo for this if(nUpdated > 0) { // This operation can not be undone! m_Parent->GetDriver()->ClearUndoPoints(); } // Delete the old label m_LabelTable->SetColorLabelValid(m_SelectedId, false); // Select the new label m_CurrentLabelModel->SetValue(newid); } return true; } itksnap-3.4.0/GUI/Model/LabelEditorModel.h000066400000000000000000000076361263013355200202070ustar00rootroot00000000000000#ifndef LABELEDITORMODEL_H #define LABELEDITORMODEL_H #include #include #include #include #include #include class LabelEditorModel : public AbstractModel { public: // Standard ITK stuff irisITKObjectMacro(LabelEditorModel, AbstractModel) /** Initialize with the parent model */ void SetParentModel(GlobalUIModel *parent); /** Get the model describing the current selected label (and its domain) */ irisGetMacro(CurrentLabelModel, ConcreteColorLabelPropertyModel *) /** Get the model for the current label description */ irisGetMacro(CurrentLabelDescriptionModel, AbstractSimpleStringProperty *) /** Get the model for the current label id */ irisGetMacro(CurrentLabelIdModel, AbstractRangedIntProperty *) /** Get the model for the current label id */ irisGetMacro(CurrentLabelOpacityModel, AbstractRangedIntProperty *) /** Get the model for the current label id */ irisGetMacro(CurrentLabelHiddenStateModel, AbstractSimpleBooleanVec2Property *) /** Get the model for the current label id */ irisGetMacro(CurrentLabelColorModel, AbstractSimpleUIntVec3Property *) /** Get the model that specifies whether the current label is background or * foreground label */ irisGetMacro(IsForegroundBackgroundModel, AbstractSimpleBooleanVec2Property *) /** Change the label id of the selected label */ bool ReassignLabelId(LabelType newid); /** States in which the model can be, which allow the activation and deactivation of various widgets in the interface */ enum UIState { UIF_EDITABLE_LABEL_SELECTED }; /** Check the state flags above */ bool CheckState(UIState state); /** Create a new label */ bool MakeNewLabel(bool copyCurrent); /** Will deleting the current label affect the segmentation? */ bool IsLabelDeletionDestructive(); /** Delete the selected label */ void DeleteCurrentLabel(); /** Reset labels to defaults */ void ResetLabels(); protected: // Hidden constructor/destructor LabelEditorModel(); virtual ~LabelEditorModel() {} // Helper method to retrieve current color label from the table bool GetAndStoreCurrentLabel(); // Id of the current label SmartPtr m_CurrentLabelIdModel; bool GetCurrentLabelIdValueAndRange( int &value, NumericValueRange *domain); void SetCurrentLabelId(int value); // Opacity of the current label SmartPtr m_CurrentLabelOpacityModel; bool GetCurrentLabelOpacityValueAndRange( int &value, NumericValueRange *domain); void SetCurrentLabelOpacity(int value); // Description of the current label SmartPtr m_CurrentLabelDescriptionModel; bool GetCurrentLabelDescription(std::string &value); void SetCurrentLabelDescription(std::string value); // Visibility of the current label SmartPtr m_CurrentLabelHiddenStateModel; bool GetCurrentLabelHiddenState(iris_vector_fixed &value); void SetCurrentLabelHiddenState(iris_vector_fixed value); // Color of the current label SmartPtr m_CurrentLabelColorModel; bool GetCurrentLabelColor(Vector3ui &value); void SetCurrentLabelColor(Vector3ui value); // Foreground/background status SmartPtr m_IsForegroundBackgroundModel; bool GetIsForegroundBackground(Vector2b &value); void SetIsForegroundBackground(Vector2b value); // The parent model GlobalUIModel *m_Parent; // A pointer to the color label table ColorLabelTable *m_LabelTable; // The label that is currently selected SmartPtr m_CurrentLabelModel; // The information about the current label (temporarily valid) ColorLabel m_SelectedColorLabel; LabelType m_SelectedId; }; #endif // LABELEDITORMODEL_H itksnap-3.4.0/GUI/Model/LayerGeneralPropertiesModel.cxx000066400000000000000000000257551263013355200230250ustar00rootroot00000000000000#include "LayerGeneralPropertiesModel.h" #include "DisplayMappingPolicy.h" #include "LayerAssociation.txx" #include "NumericPropertyToggleAdaptor.h" #include "LayerTableRowModel.h" template class LayerAssociation; LayerGeneralPropertiesModel::LayerGeneralPropertiesModel() { // Create the derived models m_DisplayModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetDisplayModeValueAndRange, &Self::SetDisplayModeValue); m_SelectedComponentModel = wrapGetterSetterPairAsProperty( this, &Self::GetSelectedComponentValueAndRange, &Self::SetSelectedComponentValue); m_AnimateModel = wrapGetterSetterPairAsProperty( this, &Self::GetAnimateValue, &Self::SetAnimateValue); m_LayerOpacityModel = wrapGetterSetterPairAsProperty( this, &Self::GetLayerOpacityValueAndRange, &Self::SetLayerOpacityValue); m_LayerVisibilityModel = wrapGetterSetterPairAsProperty( this, &Self::GetLayerVisibilityValue, &Self::SetLayerVisibilityValue); m_FilenameModel = wrapGetterSetterPairAsProperty( this, &Self::GetFilenameValue); m_NicknameModel = wrapGetterSetterPairAsProperty( this, &Self::GetNicknameValue, &Self::SetNicknameValue); m_IsStickyModel = wrapGetterSetterPairAsProperty( this, &Self::GetIsStickyValue, &Self::SetIsStickyValue); } LayerGeneralPropertiesModel::~LayerGeneralPropertiesModel() { } void LayerGeneralPropertiesModel::SetParentModel(GlobalUIModel *parent) { Superclass::SetParentModel(parent); this->Rebroadcast( parent->GetDriver(), WrapperMetadataChangeEvent(), ModelUpdateEvent()); this->Rebroadcast( parent->GetDriver(), WrapperDisplayMappingChangeEvent(), ModelUpdateEvent()); this->Rebroadcast( parent->GetDriver(), MainImageDimensionsChangeEvent(), ModelUpdateEvent()); this->Rebroadcast(this, ModelUpdateEvent(), StateMachineChangeEvent()); } void LayerGeneralPropertiesModel::RegisterWithLayer(ImageWrapperBase *layer) { // Listen to changes in the layer's intensity curve // TODO: maybe we need better event granularity here? This event will fire when // the intensity curve or colormap is changed too unsigned long tag = Rebroadcast(layer->GetDisplayMapping(), itk::ModifiedEvent(), ModelUpdateEvent()); // Set a flag so we don't register a listener again GetProperties().SetObserverTag(tag); } void LayerGeneralPropertiesModel::UnRegisterFromLayer(ImageWrapperBase *layer, bool being_deleted) { if(!being_deleted) { // It's safe to call GetProperties() unsigned long tag = GetProperties().GetObserverTag(); if(tag) { layer->GetDisplayMapping()->RemoveObserver(tag); } } } void LayerGeneralPropertiesModel::OnUpdate() { Superclass::OnUpdate(); // Do we need this method? } bool LayerGeneralPropertiesModel::CheckState(LayerGeneralPropertiesModel::UIState state) { if(!m_Layer) return false; // Defer to the row model when we can LayerTableRowModel *row_model = this->GetSelectedLayerTableRowModel(); switch(state) { case UIF_CAN_SWITCH_COMPONENTS: { AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast( m_Layer->GetDisplayMapping()); return dp && dp->GetDisplayMode().SelectedScalarRep == SCALAR_REP_COMPONENT; } case UIF_MULTICOMPONENT: return m_Layer->GetNumberOfComponents() > 1; case UIF_IS_STICKINESS_EDITABLE: return row_model->CheckState(LayerTableRowModel::UIF_PINNABLE) || row_model->CheckState(LayerTableRowModel::UIF_UNPINNABLE); case UIF_IS_OPACITY_EDITABLE: return row_model->CheckState(LayerTableRowModel::UIF_OPACITY_EDITABLE); case UIF_MOVABLE_UP: return row_model->CheckState(LayerTableRowModel::UIF_MOVABLE_UP); case UIF_MOVABLE_DOWN: return row_model->CheckState(LayerTableRowModel::UIF_MOVABLE_DOWN); } return false; } void LayerGeneralPropertiesModel::MoveLayerUp() { this->GetSelectedLayerTableRowModel()->MoveLayerUp(); } void LayerGeneralPropertiesModel::MoveLayerDown() { this->GetSelectedLayerTableRowModel()->MoveLayerDown(); } bool LayerGeneralPropertiesModel ::GetDisplayModeValueAndRange(DisplayMode &value, DisplayModeDomain *domain) { VectorImageWrapperBase *layer = this->GetLayerAsVector(); if(!layer) return false; // Get the current display mode MultiChannelDisplayMode mode = GetMultiChannelDisplayPolicy()->GetDisplayMode(); if(mode.UseRGB) { value = MODE_RGB; } else { switch(mode.SelectedScalarRep) { case SCALAR_REP_MAGNITUDE: value = MODE_MAGNITUDE; break; case SCALAR_REP_MAX: value = MODE_MAX; break; case SCALAR_REP_AVERAGE: value = MODE_AVERAGE; break; case SCALAR_REP_COMPONENT: value = MODE_COMPONENT; break; default: return false; } } // Fill out the domain if(domain) { (*domain)[MODE_COMPONENT] = "Single Component"; (*domain)[MODE_MAGNITUDE] = "Magnitude of Components"; (*domain)[MODE_MAX] = "Maximum Component"; (*domain)[MODE_AVERAGE] = "Average of Components"; // RGB is available only if there are three components if(layer->GetNumberOfComponents() == 3) (*domain)[MODE_RGB] = "RGB Display"; } return true; } void LayerGeneralPropertiesModel ::SetDisplayModeValue(DisplayMode value) { assert(this->GetLayerAsVector()); // Get the current display mode MultiChannelDisplayMode mode = GetMultiChannelDisplayPolicy()->GetDisplayMode(); // Update the current display mode switch(value) { case LayerGeneralPropertiesModel::MODE_COMPONENT: mode.SelectedScalarRep = SCALAR_REP_COMPONENT; mode.UseRGB = false; break; case LayerGeneralPropertiesModel::MODE_MAGNITUDE: mode.SelectedScalarRep = SCALAR_REP_MAGNITUDE; mode.SelectedComponent = 0; mode.UseRGB = false; break; case LayerGeneralPropertiesModel::MODE_MAX: mode.SelectedScalarRep = SCALAR_REP_MAX; mode.SelectedComponent = 0; mode.UseRGB = false; break; case LayerGeneralPropertiesModel::MODE_AVERAGE: mode.SelectedScalarRep = SCALAR_REP_AVERAGE; mode.SelectedComponent = 0; mode.UseRGB = false; break; case LayerGeneralPropertiesModel::MODE_RGB: mode.UseRGB = true; mode.SelectedScalarRep = SCALAR_REP_COMPONENT; mode.SelectedComponent = 0; break; } GetMultiChannelDisplayPolicy()->SetDisplayMode(mode); } bool LayerGeneralPropertiesModel ::GetSelectedComponentValueAndRange(int &value, NumericValueRange *domain) { VectorImageWrapperBase *layer = this->GetLayerAsVector(); if(!layer) return false; // Get the current display mode MultiChannelDisplayMode mode = GetMultiChannelDisplayPolicy()->GetDisplayMode(); // Mode must be single component if(mode.UseRGB || mode.SelectedScalarRep != SCALAR_REP_COMPONENT) return false; // Use 1-based indexing value = mode.SelectedComponent + 1; if(domain) { domain->Set(1, layer->GetNumberOfComponents(), 1); } return true; } void LayerGeneralPropertiesModel ::SetSelectedComponentValue(int value) { assert(this->GetLayerAsVector()); // Get the current display mode MultiChannelDisplayMode mode = GetMultiChannelDisplayPolicy()->GetDisplayMode(); mode.SelectedComponent = value - 1; GetMultiChannelDisplayPolicy()->SetDisplayMode(mode); } bool LayerGeneralPropertiesModel ::GetAnimateValue(bool &value) { if(!this->GetLayerAsVector()) return false; // Get the current display mode MultiChannelDisplayMode mode = GetMultiChannelDisplayPolicy()->GetDisplayMode(); // Animation is only possible when showing components if(mode.UseRGB || mode.SelectedScalarRep != SCALAR_REP_COMPONENT) return false; value = GetMultiChannelDisplayPolicy()->GetAnimate(); return true; } void LayerGeneralPropertiesModel ::SetAnimateValue(bool value) { assert(this->GetLayerAsVector()); // Get the current display mode GetMultiChannelDisplayPolicy()->SetAnimate(value); } bool LayerGeneralPropertiesModel ::GetLayerOpacityValueAndRange(int &value, NumericValueRange *domain) { LayerTableRowModel *trm = GetSelectedLayerTableRowModel(); return trm ? trm->GetLayerOpacityModel()->GetValueAndDomain(value, domain) : false; } void LayerGeneralPropertiesModel::SetLayerOpacityValue(int value) { LayerTableRowModel *trm = GetSelectedLayerTableRowModel(); trm->GetLayerOpacityModel()->SetValue(value); } bool LayerGeneralPropertiesModel::GetLayerVisibilityValue(bool &value) { LayerTableRowModel *trm = GetSelectedLayerTableRowModel(); return trm ? trm->GetVisibilityToggleModel()->GetValueAndDomain(value, NULL) : false; } void LayerGeneralPropertiesModel::SetLayerVisibilityValue(bool value) { LayerTableRowModel *trm = GetSelectedLayerTableRowModel(); trm->GetVisibilityToggleModel()->SetValue(value); } bool LayerGeneralPropertiesModel::GetFilenameValue(std::string &value) { ImageWrapperBase *layer = this->GetLayer(); if(layer) { value = layer->GetFileName(); return true; } return false; } bool LayerGeneralPropertiesModel::GetNicknameValue(std::string &value) { ImageWrapperBase *layer = this->GetLayer(); if(layer) { value = layer->GetCustomNickname(); return true; } return false; } void LayerGeneralPropertiesModel::SetNicknameValue(std::string value) { ImageWrapperBase *layer = this->GetLayer(); layer->SetCustomNickname(value); } VectorImageWrapperBase * LayerGeneralPropertiesModel::GetLayerAsVector() { return dynamic_cast(this->GetLayer()); } bool LayerGeneralPropertiesModel::GetIsStickyValue(bool &value) { // Delegate to the row model for this LayerTableRowModel *trm = GetSelectedLayerTableRowModel(); return trm ? trm->GetStickyModel()->GetValueAndDomain(value, NULL) : false; } void LayerGeneralPropertiesModel::SetIsStickyValue(bool value) { // Delegate to the row model for this LayerTableRowModel *trm = GetSelectedLayerTableRowModel(); // Calling this method will set the globally selected layer to the main layer // because the globally selected layer cannot be sticky. However, we can still // have a sticky layer selected in the layer inspector. So we override. trm->GetStickyModel()->SetValue(value); } AbstractMultiChannelDisplayMappingPolicy * LayerGeneralPropertiesModel::GetMultiChannelDisplayPolicy() { AbstractMultiChannelDisplayMappingPolicy *dp = static_cast< AbstractMultiChannelDisplayMappingPolicy *>( this->GetLayer()->GetDisplayMapping()); return dp; } LayerTableRowModel *LayerGeneralPropertiesModel::GetSelectedLayerTableRowModel() { if(m_Layer) return dynamic_cast(m_Layer->GetUserData("LayerTableRowModel")); else return NULL; } itksnap-3.4.0/GUI/Model/LayerGeneralPropertiesModel.h000066400000000000000000000121471263013355200224410ustar00rootroot00000000000000#ifndef LAYERGENERALPROPERTIESMODEL_H #define LAYERGENERALPROPERTIESMODEL_H #include "AbstractLayerAssociatedModel.h" #include "PropertyModel.h" class AbstractMultiChannelDisplayMappingPolicy; class LayerTableRowModel; /** * Properties maintained for each layer in the layer association */ class GeneralLayerProperties { public: irisGetSetMacro(ObserverTag, unsigned long) irisGetSetMacro(VisibilityToggleModel, AbstractSimpleBooleanProperty *) GeneralLayerProperties() : m_ObserverTag(0), m_VisibilityToggleModel(NULL) {} virtual ~GeneralLayerProperties() {} protected: // The visibility toggle model for this layer. This model must remember the // previous opacity value (and restore it when toggled from off to on) so it // has to be associated with each layer SmartPtr m_VisibilityToggleModel; // Whether or not we are already listening to events from this layer unsigned long m_ObserverTag; }; typedef AbstractLayerAssociatedModel< GeneralLayerProperties, ImageWrapperBase> LayerGeneralPropertiesModelBase; class LayerGeneralPropertiesModel : public LayerGeneralPropertiesModelBase { public: irisITKObjectMacro(LayerGeneralPropertiesModel, LayerGeneralPropertiesModelBase) /** * This enum defines the selections that the user can make for display mode. * These selections roughly map to the ScalarRepresentation * but the RGB mode is handled differently here and there (because RGB mode does * is not a scalar representation). */ enum DisplayMode { MODE_COMPONENT = 0, MODE_MAGNITUDE, MODE_MAX, MODE_AVERAGE, MODE_RGB }; /** States for this model */ enum UIState { UIF_MULTICOMPONENT, UIF_CAN_SWITCH_COMPONENTS, UIF_IS_STICKINESS_EDITABLE, UIF_IS_OPACITY_EDITABLE, UIF_MOVABLE_UP, UIF_MOVABLE_DOWN }; // Implementation of virtual functions from parent class void RegisterWithLayer(ImageWrapperBase *layer); void UnRegisterFromLayer(ImageWrapperBase *layer, bool being_deleted); // Parent model assignment override virtual void SetParentModel(GlobalUIModel *parent); // Function called in response to events virtual void OnUpdate(); // State management bool CheckState(UIState state); // Typedefs for the model for component selection typedef SimpleItemSetDomain DisplayModeDomain; typedef AbstractPropertyModel AbstractDisplayModeModel; // Models irisGetMacro(DisplayModeModel, AbstractDisplayModeModel *) irisGetMacro(SelectedComponentModel, AbstractRangedIntProperty *) irisGetMacro(AnimateModel, AbstractSimpleBooleanProperty *) /** A model for overall layer opacity (int, range 0..100) */ irisRangedPropertyAccessMacro(LayerOpacity, int) /** A model for the layer visibility on/off state */ irisSimplePropertyAccessMacro(LayerVisibility, bool) /** A model for the stickiness */ irisSimplePropertyAccessMacro(IsSticky, bool) /** A model for the filename */ irisSimplePropertyAccessMacro(Filename, std::string) /** A model for the nickname */ irisSimplePropertyAccessMacro(Nickname, std::string) /** Move the layer up in the list */ void MoveLayerUp(); void MoveLayerDown(); protected: LayerGeneralPropertiesModel(); virtual ~LayerGeneralPropertiesModel(); SmartPtr m_DisplayModeModel; bool GetDisplayModeValueAndRange(DisplayMode &value, DisplayModeDomain *domain); void SetDisplayModeValue(DisplayMode value); SmartPtr m_SelectedComponentModel; bool GetSelectedComponentValueAndRange(int &value, NumericValueRange *domain); void SetSelectedComponentValue(int value); SmartPtr m_AnimateModel; bool GetAnimateValue(bool &value); void SetAnimateValue(bool value); // layer opacity and visibility models SmartPtr m_LayerOpacityModel; SmartPtr m_LayerVisibilityModel; // Callbacks for the opacity model bool GetLayerOpacityValueAndRange(int &value, NumericValueRange *domain); void SetLayerOpacityValue(int value); // Callbacks for the visibility model bool GetLayerVisibilityValue(bool &value); void SetLayerVisibilityValue(bool value); // Filename and nickname SmartPtr m_FilenameModel; SmartPtr m_NicknameModel; bool GetFilenameValue(std::string &value); bool GetNicknameValue(std::string &value); void SetNicknameValue(std::string value); // Stickiness SmartPtr m_IsStickyModel; bool GetIsStickyValue(bool &value); void SetIsStickyValue(bool value); // Get the current display settings VectorImageWrapperBase *GetLayerAsVector(); AbstractMultiChannelDisplayMappingPolicy *GetMultiChannelDisplayPolicy(); // Get the LayerTableRowModel corresponding to the selected layer, or // NULL if no layer is selected. Some of the properties that this model // exposes are already exposed in LayerTableRowModel, so we delegate to // them. LayerTableRowModel *GetSelectedLayerTableRowModel(); }; #endif // LAYERGENERALPROPERTIESMODEL_H itksnap-3.4.0/GUI/Model/LayerSelectionModel.cxx000066400000000000000000000007041263013355200213030ustar00rootroot00000000000000#include "LayerSelectionModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" int LayerSelectionModel::GetNumberOfLayers() { GenericImageData *id = m_Parent->GetDriver()->GetCurrentImageData(); return id->GetNumberOfLayers(m_RoleFilter); } LayerIterator LayerSelectionModel::GetNthLayer(int n) { GenericImageData *id = m_Parent->GetDriver()->GetCurrentImageData(); LayerIterator it(id, m_RoleFilter); it += n; return it; } itksnap-3.4.0/GUI/Model/LayerSelectionModel.h000066400000000000000000000023721263013355200207330ustar00rootroot00000000000000#ifndef LAYERSELECTIONMODEL_H #define LAYERSELECTIONMODEL_H #include "AbstractModel.h" #include "GenericImageData.h" class GlobalUIModel; class ImageWrapperBase; /** This model encapsulates a dynamic selection of image layers based on a filter. For example, it can be used to create a dynamic list consisting of a main image and all overlays. By default, all of the roles are included in the filter. */ class LayerSelectionModel : public AbstractModel { public: irisITKObjectMacro(LayerSelectionModel, AbstractModel) /** Set the parent model */ void SetParentModel(GlobalUIModel *parent) { m_Parent = parent; } /** Set the filter for this model. See GenericImageData::GetLayer for details */ void SetRoleFilter(int role_filter) { m_RoleFilter = role_filter; } /** Get the number of layers satisfying the filter. This is updated dynamically, so it is always correct */ int GetNumberOfLayers(); /** Get an iterator object pointing to the N-th layer */ LayerIterator GetNthLayer(int n); protected: LayerSelectionModel() : AbstractModel(), m_Parent(NULL), m_RoleFilter(ALL_ROLES) {} virtual ~LayerSelectionModel() {} GlobalUIModel *m_Parent; int m_RoleFilter; }; #endif // LAYERSELECTIONMODEL_H itksnap-3.4.0/GUI/Model/LayerTableRowModel.cxx000066400000000000000000000314751263013355200211060ustar00rootroot00000000000000#include "LayerTableRowModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "ImageWrapperBase.h" #include "NumericPropertyToggleAdaptor.h" #include "DisplayMappingPolicy.h" #include "DisplayLayoutModel.h" #include "ImageIOWizardModel.h" #include "ColorMapModel.h" #include "IntensityCurveModel.h" #include "LayerGeneralPropertiesModel.h" #include "SNAPImageData.h" LayerTableRowModel::LayerTableRowModel() { m_LayerOpacityModel = wrapGetterSetterPairAsProperty( this, &Self::GetLayerOpacityValueAndRange, &Self::SetLayerOpacityValue); m_NicknameModel = wrapGetterSetterPairAsProperty( this, &Self::GetNicknameValue, &Self::SetNicknameValue); m_ComponentNameModel = wrapGetterSetterPairAsProperty( this, &Self::GetComponentNameValue); m_ColorMapPresetModel = wrapGetterSetterPairAsProperty( this, &Self::GetColorMapPresetValue, &Self::SetColorMapPresetValue); m_StickyModel = wrapGetterSetterPairAsProperty( this, &Self::GetStickyValue, &Self::SetSticklyValue); m_DisplayModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetDisplayModeValue, &Self::SetDisplayModeValue); m_VisibilityToggleModel = NewNumericPropertyToggleAdaptor( m_LayerOpacityModel.GetPointer(), 0, 50); m_LayerRole = NO_ROLE; m_LayerPositionInRole = -1; m_ImageData = NULL; } bool LayerTableRowModel::CheckState(UIState state) { // Are we in tiling mode? bool tiling = ( m_ParentModel->GetGlobalState()->GetSliceViewLayerLayout() == LAYOUT_TILED); bool snapmode = m_ParentModel->GetDriver()->IsSnakeModeActive(); // Check the states switch(state) { // Opacity can be edited for all layers except the main image layer case LayerTableRowModel::UIF_OPACITY_EDITABLE: return (m_Layer->IsSticky()); // Pinnable means it's not sticky and may be overlayed (i.e., not main) case LayerTableRowModel::UIF_PINNABLE: return (m_LayerRole != MAIN_ROLE && !m_Layer->IsSticky()); // Unpinnable means it's not sticky and may be overlayed (i.e., not main) case LayerTableRowModel::UIF_UNPINNABLE: return (m_LayerRole != MAIN_ROLE && m_Layer->IsSticky()); case LayerTableRowModel::UIF_MOVABLE_UP: return (m_LayerRole == OVERLAY_ROLE && m_LayerPositionInRole > 0); case LayerTableRowModel::UIF_MOVABLE_DOWN: return (m_LayerRole == OVERLAY_ROLE && m_LayerPositionInRole < m_LayerNumberOfLayersInRole - 1); case LayerTableRowModel::UIF_CLOSABLE: return ((m_LayerRole == OVERLAY_ROLE || m_LayerRole == MAIN_ROLE) && !snapmode); case LayerTableRowModel::UIF_CONTRAST_ADJUSTABLE: return (m_Layer && m_Layer->GetDisplayMapping()->GetIntensityCurve()); case LayerTableRowModel::UIF_COLORMAP_ADJUSTABLE: return (m_Layer && m_Layer->GetDisplayMapping()->GetColorMap()); case LayerTableRowModel::UIF_MULTICOMPONENT: return (m_Layer && m_Layer->GetNumberOfComponents() > 1); } return false; } void LayerTableRowModel::UpdateRoleInfo() { LayerIterator it(m_ImageData); it.Find(m_Layer); m_LayerRole = it.GetRole(); m_LayerPositionInRole = it.GetPositionInRole(); m_LayerNumberOfLayersInRole = it.GetNumberOfLayersInRole(); } void LayerTableRowModel::UpdateDisplayModeList() { m_AvailableDisplayModes.clear(); if(m_Layer && m_Layer->GetNumberOfComponents() > 1) { for(int i = 0; i < m_Layer->GetNumberOfComponents(); i++) m_AvailableDisplayModes.push_back( MultiChannelDisplayMode(false, SCALAR_REP_COMPONENT, i)); m_AvailableDisplayModes.push_back( MultiChannelDisplayMode(false, SCALAR_REP_MAGNITUDE, 0)); m_AvailableDisplayModes.push_back( MultiChannelDisplayMode(false, SCALAR_REP_MAX, 0)); m_AvailableDisplayModes.push_back( MultiChannelDisplayMode(false, SCALAR_REP_AVERAGE, 0)); if(m_Layer->GetNumberOfComponents() == 3) m_AvailableDisplayModes.push_back( MultiChannelDisplayMode::DefaultForRGB()); } } MultiChannelDisplayMode LayerTableRowModel::GetDisplayMode() { AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast< AbstractMultiChannelDisplayMappingPolicy *>(m_Layer->GetDisplayMapping()); return dp->GetDisplayMode(); } void LayerTableRowModel::Initialize(GlobalUIModel *parentModel, ImageWrapperBase *layer) { m_ParentModel = parentModel; m_Layer = layer; m_ImageData = parentModel->GetDriver()->GetCurrentImageData(); // For some of the functions, it is useful to know the role and the index // in the role of this layer. We shouldn't have to worry about this info // changing, since the rows get rebuilt when LayerChangeEvent() is fired. UpdateRoleInfo(); // Update the list of display modes (again, should not change during the // lifetime of this object UpdateDisplayModeList(); // Listen to cosmetic events from the layer Rebroadcast(layer, WrapperMetadataChangeEvent(), ModelUpdateEvent()); Rebroadcast(layer, WrapperDisplayMappingChangeEvent(), ModelUpdateEvent()); // What happens if the layer is deleted? The model should be notified, and // it should update its state to a NULL state before something bad happens // in the GUI... Rebroadcast(layer, itk::DeleteEvent(), ModelUpdateEvent()); // The state of this model only depends on wrapper's position in the list of // layers, not on the wrapper metadata Rebroadcast(m_ParentModel->GetDriver(), LayerChangeEvent(), StateMachineChangeEvent()); // The state also depends on the current tiling mode Rebroadcast(m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel(), ValueChangedEvent(), StateMachineChangeEvent()); Rebroadcast(layer, WrapperMetadataChangeEvent(), StateMachineChangeEvent()); } void LayerTableRowModel::MoveLayerUp() { m_ParentModel->GetDriver()->ChangeOverlayPosition(m_Layer, -1); } void LayerTableRowModel::MoveLayerDown() { m_ParentModel->GetDriver()->ChangeOverlayPosition(m_Layer, +1); } SmartPtr LayerTableRowModel::CreateIOWizardModelForSave() { return m_ParentModel->CreateIOWizardModelForSave(m_Layer, m_LayerRole); } bool LayerTableRowModel::IsMainLayer() { return m_LayerRole == MAIN_ROLE; } void LayerTableRowModel::SetSelected(bool selected) { // If the layer is selected and is not sticky, we set is as the currently visible // layer in the render views if(selected && !m_Layer->IsSticky()) { m_ParentModel->GetGlobalState()->SetSelectedLayerId(m_Layer->GetUniqueId()); } } void LayerTableRowModel::CloseLayer() { // If this is an overlay, we unload it like this if(m_LayerRole == OVERLAY_ROLE) { m_ParentModel->GetDriver()->UnloadOverlay(m_Layer); m_Layer = NULL; } // The main image can also be unloaded else if(m_LayerRole == MAIN_ROLE) { m_ParentModel->GetDriver()->UnloadMainImage(); m_Layer = NULL; } } void LayerTableRowModel::AutoAdjustContrast() { if(m_Layer && m_Layer->GetDisplayMapping()->GetIntensityCurve()) { // TODO: this is a bit of a hack, since we go through a different model // and have to swap out that model's current layer, which adds some overhead IntensityCurveModel *icm = m_ParentModel->GetIntensityCurveModel(); ImageWrapperBase *currentLayer = icm->GetLayer(); icm->SetLayer(m_Layer); icm->OnAutoFitWindow(); icm->SetLayer(currentLayer); } } #include "MomentTextures.h" void LayerTableRowModel::GenerateTextureFeatures() { ScalarImageWrapperBase *scalar = dynamic_cast(m_Layer); if(scalar) { // Get the image out SmartPtr common_rep = scalar->GetCommonFormatImage(); /* SmartPtr texture_image = ScalarImageWrapperBase::CommonFormatImageType::New(); texture_image->CopyInformation(common_rep); texture_image->SetRegions(common_rep->GetBufferedRegion()); texture_image->Allocate();*/ // Create a radius - hard-coded for now itk::Size<3> radius; radius.Fill(2); // Create a filter to generate textures typedef AnatomicImageWrapperTraits::ImageType TextureImageType; typedef bilwaj::MomentTextureFilter< ScalarImageWrapperBase::CommonFormatImageType, TextureImageType> MomentFilterType; MomentFilterType::Pointer filter = MomentFilterType::New(); filter->SetInput(common_rep); filter->SetRadius(radius); filter->SetHighestDegree(3); filter->Update(); // Create a new image wrapper SmartPtr newWrapper = AnatomicImageWrapper::New(); newWrapper->InitializeToWrapper(m_Layer, filter->GetOutput(), NULL, NULL); newWrapper->SetDefaultNickname("Textures"); this->GetParentModel()->GetDriver()->AddDerivedOverlayImage(newWrapper); } } std::string LayerTableRowModel::GetDisplayModeString(const MultiChannelDisplayMode &mode) { if(mode.UseRGB) { return "RGB"; } std::ostringstream oss; switch(mode.SelectedScalarRep) { case SCALAR_REP_COMPONENT: oss << (1 + mode.SelectedComponent) << "/" << m_Layer->GetNumberOfComponents(); return oss.str(); case SCALAR_REP_MAGNITUDE: return "Mag"; case SCALAR_REP_MAX: return "Max"; case SCALAR_REP_AVERAGE: return "Avg"; case NUMBER_OF_SCALAR_REPS: break; }; return ""; } bool LayerTableRowModel::GetNicknameValue(std::string &value) { if(!m_Layer) return false; value = m_Layer->GetNickname(); return true; } void LayerTableRowModel::SetNicknameValue(std::string value) { m_Layer->SetCustomNickname(value); } bool LayerTableRowModel::GetComponentNameValue(std::string &value) { // Get the name of the compomnent if(m_Layer && m_Layer->GetNumberOfComponents() > 1) { value = this->GetDisplayModeString(this->GetDisplayMode()); return true; } return false; } bool LayerTableRowModel::GetColorMapPresetValue(std::string &value) { if(m_Layer && m_Layer->GetDisplayMapping()->GetColorMap()) { value = ColorMap::GetPresetName( m_Layer->GetDisplayMapping()->GetColorMap()->GetSystemPreset()); return true; } return false; } void LayerTableRowModel::SetColorMapPresetValue(std::string value) { // TODO: this is a cheat! The current way of handling color map presets in // snap is hacky, and I really don't like it. We need a common class that // can handle system and user presets for a variety of objects, one that // can interface nicely with the GUI models. Here we are going through // the functionality provided by the ColorMapModel class. ColorMapModel *cmm = m_ParentModel->GetColorMapModel(); ImageWrapperBase *currentLayer = cmm->GetLayer(); cmm->SetLayer(m_Layer); cmm->SelectPreset(value); cmm->SetLayer(currentLayer); } bool LayerTableRowModel::GetDisplayModeValue(MultiChannelDisplayMode &value) { if(m_Layer && m_Layer->GetNumberOfComponents() > 1) { AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast< AbstractMultiChannelDisplayMappingPolicy *>(m_Layer->GetDisplayMapping()); value = dp->GetDisplayMode(); return true; } return false; } void LayerTableRowModel::SetDisplayModeValue(MultiChannelDisplayMode value) { AbstractMultiChannelDisplayMappingPolicy *dp = dynamic_cast< AbstractMultiChannelDisplayMappingPolicy *>(m_Layer->GetDisplayMapping()); dp->SetDisplayMode(value); } void LayerTableRowModel::OnUpdate() { // Has our layer been deleted? if(this->m_EventBucket->HasEvent(itk::DeleteEvent(), m_Layer)) { m_Layer = NULL; m_LayerRole = NO_ROLE; m_LayerPositionInRole = -1; m_LayerNumberOfLayersInRole = -1; } else if(this->m_EventBucket->HasEvent(LayerChangeEvent())) { this->UpdateRoleInfo(); } } bool LayerTableRowModel::GetLayerOpacityValueAndRange(int &value, NumericValueRange *domain) { // For opacity to be defined, the layer must be sticky if(!m_Layer || !m_Layer->IsSticky()) return false; // Meaning of 'visible' is different for sticky and non-sticky layers value = (int)(100.0 * m_Layer->GetAlpha()); if(domain) domain->Set(0, 100, 5); return true; } void LayerTableRowModel::SetLayerOpacityValue(int value) { assert(m_Layer && m_Layer->IsSticky()); m_Layer->SetAlpha(value / 100.0); } bool LayerTableRowModel::GetStickyValue(bool &value) { if(!m_Layer) return false; value = m_Layer->IsSticky(); return true; } void LayerTableRowModel::SetSticklyValue(bool value) { // Make sure the selected ID is legitimate if(m_ParentModel->GetGlobalState()->GetSelectedLayerId() == m_Layer->GetUniqueId()) { m_ParentModel->GetGlobalState()->SetSelectedLayerId( m_ParentModel->GetDriver()->GetCurrentImageData()->GetMain()->GetUniqueId()); } m_Layer->SetSticky(value); } itksnap-3.4.0/GUI/Model/LayerTableRowModel.h000066400000000000000000000122501263013355200205210ustar00rootroot00000000000000#ifndef LAYERTABLEROWMODEL_H #define LAYERTABLEROWMODEL_H #include "PropertyModel.h" class ImageWrapperBase; class GlobalUIModel; class ImageIOWizardModel; struct MultiChannelDisplayMode; class GenericImageData; /** * @brief The LayerTableRowModel class * * This is a GUI model used to access image layer properties that can appear * in a list/table of layers. These properties include opacity, visibility, * nickname, etc. * * This model is different from models such as LayerGeneralPropertiesModel: * it has a one-to-one association with the layers, whereas those other models * have a 1-N association with the layers (just one model object, parameterized * by a pointer to the 'active' layer). */ class LayerTableRowModel : public AbstractModel { public: irisITKObjectMacro(LayerTableRowModel, AbstractModel) irisGetMacro(Layer, ImageWrapperBase *) irisGetMacro(ParentModel, GlobalUIModel *) /** A model for overall layer opacity (int, range 0..100) */ irisRangedPropertyAccessMacro(LayerOpacity, int) /** A model for layer visibility (on/off, interacts with opacity) */ irisSimplePropertyAccessMacro(VisibilityToggle, bool) /** A model for layer stickiness */ irisSimplePropertyAccessMacro(Sticky, bool) /** A model for the nickname */ irisSimplePropertyAccessMacro(Nickname, std::string) /** A model for the component name */ irisSimplePropertyAccessMacro(ComponentName, std::string) /** A model for the color map preset currently selected */ irisSimplePropertyAccessMacro(ColorMapPreset, std::string) /** A model for the display mode */ typedef AbstractPropertyModel AbstractDisplayModeModel; irisGetMacro(DisplayModeModel, AbstractDisplayModeModel *) /** States in which the model can be, which allow the activation and deactivation of various widgets in the interface */ enum UIState { UIF_OPACITY_EDITABLE, UIF_PINNABLE, UIF_UNPINNABLE, UIF_MOVABLE_UP, UIF_MOVABLE_DOWN, UIF_CLOSABLE, UIF_COLORMAP_ADJUSTABLE, UIF_CONTRAST_ADJUSTABLE, UIF_MULTICOMPONENT }; /** Check the state of the system */ bool CheckState(UIState state); void Initialize(GlobalUIModel *parentModel, ImageWrapperBase *layer); /** Move layer up or down in its role. */ void MoveLayerUp(); void MoveLayerDown(); /** * Create a temporary model for saving this image to a file, to use in * conjunction with an IO wizard */ SmartPtr CreateIOWizardModelForSave(); /** * Whether closing the layer requires prompting for changes */ bool IsMainLayer(); /** * Mark the layer as selected */ void SetSelected(bool selected); /** * Close the current layer */ void CloseLayer(); /** Auto-adjust contrast (via the IntensityCurveModel) */ void AutoAdjustContrast(); /** * Generate texture features from this layer * TODO: this is a placeholder for the future more complex functionality */ void GenerateTextureFeatures(); typedef std::list DisplayModeList; /** Get a mapping of available display modes to user-readable strings */ irisGetMacro(AvailableDisplayModes, const DisplayModeList &) /** Get the printable name for a display mode */ std::string GetDisplayModeString(const MultiChannelDisplayMode &mode); protected: LayerTableRowModel(); virtual ~LayerTableRowModel() {} // Parent model GlobalUIModel *m_ParentModel; // Generic image data object in which this layer lives. GenericImageData *m_ImageData; // Layer ImageWrapperBase *m_Layer; // Role information (cached) LayerRole m_LayerRole; int m_LayerPositionInRole, m_LayerNumberOfLayersInRole; // Cached list of display modes DisplayModeList m_AvailableDisplayModes; // Visibility model SmartPtr m_VisibilityToggleModel; // Stickly model SmartPtr m_StickyModel; bool GetStickyValue(bool &value); void SetSticklyValue(bool value); // Opacity model SmartPtr m_LayerOpacityModel; bool GetLayerOpacityValueAndRange(int &value, NumericValueRange *domain); void SetLayerOpacityValue(int value); // Nickname model SmartPtr m_NicknameModel; bool GetNicknameValue(std::string &value); void SetNicknameValue(std::string value); // Component name model SmartPtr m_ComponentNameModel; bool GetComponentNameValue(std::string &value); // Color map preset model SmartPtr m_ColorMapPresetModel; bool GetColorMapPresetValue(std::string &value); void SetColorMapPresetValue(std::string value); // Display mode model SmartPtr m_DisplayModeModel; bool GetDisplayModeValue(MultiChannelDisplayMode &value); void SetDisplayModeValue(MultiChannelDisplayMode value); // Update our state in response to events from the layer virtual void OnUpdate(); // Update cached role information void UpdateRoleInfo(); // Update cached display modes list void UpdateDisplayModeList(); // Get the display mode MultiChannelDisplayMode GetDisplayMode(); }; #endif // LAYERTABLEROWMODEL_H itksnap-3.4.0/GUI/Model/MeshExportModel.cxx000066400000000000000000000116171263013355200204640ustar00rootroot00000000000000#include "MeshExportModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "MeshExportSettings.h" #include "Registry.h" #include "GuidedMeshIO.h" #include "Generic3DModel.h" #include "itksys/RegularExpression.hxx" #include "HistoryManager.h" MeshExportModel::MeshExportModel() : AbstractModel() { m_SaveMode = SAVE_SINGLE_LABEL; m_SaveModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetSaveModeValue, &Self::SetSaveModeValue); m_ExportFileNameModel = wrapGetterSetterPairAsProperty( this, &Self::GetExportFileNameValue, &Self::SetExportFileNameValue); m_ExportedLabelModel = ConcreteColorLabelPropertyModel::New(); // Configure the export model m_ExportFormatModel = ConcreteFileFormatModel::New(); m_ExportFormatModel->SetValue(GuidedMeshIO::FORMAT_VTK); // Configure the format regular expressions m_FormatRegExp[GuidedMeshIO::FORMAT_VTK] = ".*\\.vtk$"; m_FormatRegExp[GuidedMeshIO::FORMAT_STL] = ".*\\.stl$"; m_FormatRegExp[GuidedMeshIO::FORMAT_BYU] = ".*\\.(byu|y)$"; m_FormatRegExp[GuidedMeshIO::FORMAT_VRML] = ".*\\.vrml$"; } void MeshExportModel::SetParentModel(GlobalUIModel *parent) { // Store the parent model m_ParentModel = parent; // Initialize the label property m_ExportedLabelModel->Initialize(parent->GetDriver()->GetColorLabelTable()); } bool MeshExportModel::CheckState(MeshExportModel::UIState flag) { switch(flag) { case MeshExportModel::UIF_LABEL_SELECTION_ACTIVE: return this->GetSaveMode() == SAVE_SINGLE_LABEL; } return false; } void MeshExportModel::OnDialogOpen() { // Use the drawing label as the default LabelType label = m_ParentModel->GetGlobalState()->GetDrawingColorLabel(); if(label == 0) label = m_ParentModel->GetDriver()->GetColorLabelTable()->FindNextValidLabel(0, false); m_ExportedLabelModel->SetValue(label); // Get the default filename for the currently loaded image m_ExportFileNameModel->SetValue(""); // Update the file formats list UpdateFormatDomain(); } void MeshExportModel::SaveMesh() { IRISApplication *app = m_ParentModel->GetDriver(); MeshExportSettings settings; settings.SetMeshFileName(m_ExportFileName); switch(this->GetSaveMode()) { case MeshExportModel::SAVE_SINGLE_LABEL: settings.SetFlagSingleLabel(true); settings.SetFlagSingleScene(false); settings.SetExportLabel(this->GetExportedLabel()); break; case MeshExportModel::SAVE_MULTIPLE_FILES: settings.SetFlagSingleLabel(false); settings.SetFlagSingleScene(false); break; case MeshExportModel::SAVE_SCENE: settings.SetFlagSingleLabel(false); settings.SetFlagSingleScene(true); break; } // Handle the format (in a round-about way) Registry registry; GuidedMeshIO io; io.SetFileFormat(registry, this->GetExportFormat()); settings.SetMeshFormat(registry); // The actual export should use the Generic3DModel, which will take care of // thread safety (make sure the meshes are not being updated during export m_ParentModel->GetModel3D()->ExportMesh(settings); // Update the history m_ParentModel->GetSystemInterface()->GetHistoryManager()->UpdateHistory( this->GetHistoryName(), m_ExportFileName, true); } MeshExportModel::FileFormat MeshExportModel::GetFileFormatByName(const std::string &name) const { FileFormatDomain dom; FileFormat dummy; m_ExportFormatModel->GetValueAndDomain(dummy, &dom); for(int i = 0; i < GuidedMeshIO::FORMAT_COUNT; i++) { FileFormat fmt = (FileFormat) i; if(dom[fmt] == name) return fmt; } return GuidedMeshIO::FORMAT_COUNT; } void MeshExportModel::UpdateFormatDomain() { FileFormatDomain format_domain; if(GetSaveMode() == SAVE_SCENE) { format_domain[GuidedMeshIO::FORMAT_VTK] = "VTK PolyData File"; format_domain[GuidedMeshIO::FORMAT_VRML] = "VRML 2.0 File"; } else { format_domain[GuidedMeshIO::FORMAT_VTK] = "VTK PolyData File"; format_domain[GuidedMeshIO::FORMAT_STL] = "STL Mesh File"; format_domain[GuidedMeshIO::FORMAT_BYU] = "BYU Mesh File"; } m_ExportFormatModel->SetDomain(format_domain); } bool MeshExportModel::GetSaveModeValue(SaveMode &value) { value = m_SaveMode; return true; } void MeshExportModel::SetSaveModeValue(SaveMode value) { m_SaveMode = value; UpdateFormatDomain(); InvokeEvent(StateMachineChangeEvent()); } bool MeshExportModel::GetExportFileNameValue(std::string &value) { value = m_ExportFileName; return true; } void MeshExportModel::SetExportFileNameValue(std::string value) { m_ExportFileName = value; // Try to determine the file format from the extension for(int format = 0; format < GuidedMeshIO::FORMAT_COUNT; format++) { std::string regexp_str = m_FormatRegExp[(FileFormat) format]; itksys::RegularExpression regexp(regexp_str.c_str()); if(regexp.find(m_ExportFileName)) { m_ExportFormatModel->SetValue((FileFormat) format); } } } itksnap-3.4.0/GUI/Model/MeshExportModel.h000066400000000000000000000056771263013355200201220ustar00rootroot00000000000000#ifndef MESHEXPORTMODEL_H #define MESHEXPORTMODEL_H #include "PropertyModel.h" #include "ColorLabelPropertyModel.h" #include "GuidedMeshIO.h" class GlobalUIModel; class MeshExportModel : public AbstractModel { public: irisITKObjectMacro(MeshExportModel, AbstractModel) /** States in which the model can be, which allow the activation and deactivation of various widgets in the interface */ enum UIState { UIF_LABEL_SELECTION_ACTIVE }; void SetParentModel(GlobalUIModel *parent); bool CheckState(UIState flag); /** Selected save mode */ enum SaveMode { SAVE_SINGLE_LABEL, SAVE_MULTIPLE_FILES, SAVE_SCENE }; /** Mesh file format */ typedef GuidedMeshIO::FileFormat FileFormat; /** Domain for the mesh file format model */ typedef SimpleItemSetDomain FileFormatDomain; /** Model governing the selected save mode */ irisSimplePropertyAccessMacro(SaveMode, SaveMode) /** Model for selecting the color label */ irisGenericPropertyAccessMacro(ExportedLabel, LabelType, ColorLabelItemSetDomain) /** Filename for the export */ irisSimplePropertyAccessMacro(ExportFileName, std::string) /** File format for the export */ irisGenericPropertyAccessMacro(ExportFormat, FileFormat, FileFormatDomain) /** Get the parent model */ irisGetMacro(ParentModel, GlobalUIModel *) /** Get the name of the history used for mesh export */ static std::string GetHistoryName() { return "SegmentationMesh"; } /** This method is called when the dialog is opened */ void OnDialogOpen(); /** Perform the actual save */ void SaveMesh(); /** Get the file format corresponding to a name */ FileFormat GetFileFormatByName(const std::string &name) const; protected: MeshExportModel(); virtual ~MeshExportModel() {} // Parent model GlobalUIModel *m_ParentModel; // Model for the way meshes are saved typedef AbstractPropertyModel AbstractSaveModeModel; SmartPtr m_SaveModeModel; bool GetSaveModeValue(SaveMode &value); void SetSaveModeValue(SaveMode value); // Save mode (cached) SaveMode m_SaveMode; // Color label used SmartPtr m_ExportedLabelModel; // Filename for the export std::string m_ExportFileName; // Model wrapping around the filename SmartPtr m_ExportFileNameModel; bool GetExportFileNameValue(std::string &value); void SetExportFileNameValue(std::string value); // File format for the export typedef ConcretePropertyModel ConcreteFileFormatModel; SmartPtr m_ExportFormatModel; // Update the domain of the format model based on the current state of the save // mode. This is because only some formats are supported in save modes void UpdateFormatDomain(); // Regular expressions for file formats std::map m_FormatRegExp; }; #endif // MESHEXPORTMODEL_H itksnap-3.4.0/GUI/Model/NumericPropertyToggleAdaptor.h000066400000000000000000000073451263013355200226610ustar00rootroot00000000000000#ifndef NUMERICPROPERTYTOGGLEADAPTOR_H #define NUMERICPROPERTYTOGGLEADAPTOR_H #include "PropertyModel.h" /** * @brief The NumericPropertyToggleAdaptor class * * This model is a wrapper around a numerical property that allows the user * to toggle the property on and off. When the property is toggled off, it is * set to the DefaultOffValue, when it is toggled on, it is set to the last * value when it was toggled off. * * The main intended use of this adapter is for opacity sliders * * This model necessarily has an ambiguity when the user moves the slider to * the off value, and then toggles the switch to the on position. The model * sets the slider to the DefaultOnValue when that happens. * * The model is templated over the model representing the numerical value. * Typically, this would be ConcreteRangedIntProperty or a similar class. */ template class NumericPropertyToggleAdaptor : public AbstractSimpleBooleanProperty { public: typedef NumericPropertyToggleAdaptor Self; typedef AbstractSimpleBooleanProperty Superclass; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkTypeMacro(NumericPropertyToggleAdaptor, AbstractSimpleBooleanProperty) itkNewMacro(Self) typedef TNumericValueModel ValueModelType; typedef typename ValueModelType::ValueType NumericValueType; typedef typename Superclass::ValueType ValueType; typedef typename Superclass::DomainType DomainType; /** Set the default value when going from off state to on state */ void SetDefaultOnValue(NumericValueType value) { m_DefaultOnValue = value; m_LastOnValue = m_DefaultOnValue; } /** The default off value should be zero, but can be changed */ void SetDefaultOffValue(NumericValueType value) { m_DefaultOffValue = value; } /** Set the wrapped value property model */ void SetWrappedValueModel(ValueModelType *model) { m_ValueModel = model; Rebroadcast(model, ValueChangedEvent(), ValueChangedEvent()); } virtual bool GetValueAndDomain(bool &value, TrivialDomain *) { if(!m_ValueModel) return false; NumericValueType numValue; if(!m_ValueModel->GetValueAndDomain(numValue, NULL)) return false; value = (numValue != m_DefaultOffValue); return true; } virtual void SetValue(bool value) { if(!m_ValueModel) return; NumericValueType numValue; if(!m_ValueModel->GetValueAndDomain(numValue, NULL)) return; if(value && numValue == m_DefaultOffValue) { // Restore the value to what it was before we clicked the checkbox off m_ValueModel->SetValue(m_LastOnValue); m_LastOnValue = m_DefaultOnValue; } if(!value && numValue != m_DefaultOffValue) { // Save the current value before setting to default m_LastOnValue = numValue; m_ValueModel->SetValue(m_DefaultOffValue); } } protected: NumericPropertyToggleAdaptor() { m_DefaultOffValue = 0; m_DefaultOnValue = 0; m_LastOnValue = 0; } NumericValueType m_DefaultOnValue, m_DefaultOffValue, m_LastOnValue; SmartPtr m_ValueModel; }; /** * A factory method to create these models */ template SmartPtr NewNumericPropertyToggleAdaptor( TNumericValueModel *model, typename TNumericValueModel::ValueType defaultOffValue, typename TNumericValueModel::ValueType defaultOnValue) { typedef NumericPropertyToggleAdaptor Prop; SmartPtr p = Prop::New(); p->SetWrappedValueModel(model); p->SetDefaultOffValue(defaultOffValue); p->SetDefaultOnValue(defaultOnValue); SmartPtr pp = p.GetPointer(); return pp; } #endif // NUMERICPROPERTYTOGGLEADAPTOR_H itksnap-3.4.0/GUI/Model/OrthogonalSliceCursorNavigationModel.cxx000066400000000000000000000140461263013355200246770ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #include "OrthogonalSliceCursorNavigationModel.h" #include "GenericSliceModel.h" #include "IRISException.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "GlobalUIModel.h" #include "SliceWindowCoordinator.h" #include "ImageCoordinateTransform.h" void OrthogonalSliceCursorNavigationModel::UpdateCursor(Vector2f x) { // Compute the position in slice coordinates Vector3f xClick = m_Parent->MapWindowToSlice(x); // Compute the new cross-hairs position in image space Vector3f xCross = m_Parent->MapSliceToImage(xClick); // Round the cross-hairs position down to integer Vector3i xCrossInteger = to_int(xCross); // Make sure that the cross-hairs position is within bounds by clamping // it to image dimensions Vector3i xSize = to_int(m_Parent->GetDriver()-> GetCurrentImageData()->GetVolumeExtents()); Vector3ui xCrossClamped = to_unsigned_int( xCrossInteger.clamp(Vector3i(0),xSize - Vector3i(1))); // Update the crosshairs position in the global state m_Parent->GetDriver()->SetCursorPosition(xCrossClamped); } void OrthogonalSliceCursorNavigationModel::ProcessKeyNavigation(Vector3i dx) { // Get the displacement in image space Vector3f dximg = m_Parent->GetDisplayToImageTransform().TransformVector(to_float(dx)); Vector3i dximgi = to_int(dximg); // Update the cursor IRISApplication *app = m_Parent->GetDriver(); Vector3i xSize = to_int(app->GetCurrentImageData()->GetVolumeExtents()); Vector3i cursor = to_int(app->GetCursorPosition()); cursor += dximgi; cursor = cursor.clamp(Vector3i(0), xSize - 1); app->SetCursorPosition(to_unsigned_int(cursor)); } void OrthogonalSliceCursorNavigationModel::BeginZoom() { m_StartViewZoom = m_Parent->GetViewZoom(); } void OrthogonalSliceCursorNavigationModel::BeginPan() { m_StartViewPosition = m_Parent->GetViewPosition(); } void OrthogonalSliceCursorNavigationModel::EndZoom() { } void OrthogonalSliceCursorNavigationModel::EndPan() { } void OrthogonalSliceCursorNavigationModel ::ProcessZoomGesture(float scaleFactor) { // Get the slice coordinator SliceWindowCoordinator *coordinator = m_Parent->GetParentUI()->GetSliceCoordinator(); // Sometimes the scalefactor is 1, in which case, we restore the zoom if(scaleFactor == 1.0f) { if(m_Parent->GetViewZoom() == m_StartViewZoom) return; else m_Parent->SetViewZoom(m_StartViewZoom); } else { // Compute the zoom factor (is this good?) float zoom = m_StartViewZoom * scaleFactor; // Clamp the zoom factor to reasonable limits zoom = coordinator->ClampZoom(m_Parent->GetId(), zoom); // Make sure the zoom factor is an integer fraction zoom = m_Parent->GetOptimalZoom() * ((int)(zoom / m_Parent->GetOptimalZoom() * 100)) / 100.0f; // Set the zoom factor using the window coordinator m_Parent->SetViewZoom(zoom); } // TODO: remove this! coordinator->OnZoomUpdateInWindow(m_Parent->GetId(), m_Parent->GetViewZoom()); } void OrthogonalSliceCursorNavigationModel ::ProcessPanGesture(Vector2f uvOffset) { // Compute the start and end point in slice coordinates Vector3f zOffset = m_Parent->MapWindowOffsetToSliceOffset(uvOffset); Vector2f xOffset(zOffset[0] * m_Parent->GetSliceSpacing()[0], zOffset[1] * m_Parent->GetSliceSpacing()[1]); // Under the left button, the tool changes the view_pos by the // distance traversed m_Parent->SetViewPosition(m_StartViewPosition - xOffset); } void OrthogonalSliceCursorNavigationModel ::ProcessScrollGesture(float scrollAmount) { // Get the cross-hairs position in image space Vector3ui xCrossImage = m_Parent->GetDriver()->GetCursorPosition(); // Map it into slice space Vector3f xCrossSlice = m_Parent->MapImageToSlice(to_float(xCrossImage) + Vector3f(0.5f)); // Advance by the scroll amount xCrossSlice[2] += scrollAmount; // Map back into display space xCrossImage = to_unsigned_int(m_Parent->MapSliceToImage(xCrossSlice)); // Clamp by the image size Vector3ui xSize = m_Parent->GetDriver()->GetCurrentImageData()->GetVolumeExtents(); Vector3ui xCrossClamped = xCrossImage.clamp(Vector3ui(0,0,0), xSize - Vector3ui(1,1,1)); // Update the crosshairs position in the global state m_Parent->GetDriver()->SetCursorPosition(xCrossClamped); } bool OrthogonalSliceCursorNavigationModel::CheckZoomThumbnail(Vector2i xCanvas) { // Check if the event is inside of the thumbnail boundaries Vector2i xThumb = m_Parent->GetZoomThumbnailPosition(); Vector2i sThumb = m_Parent->GetZoomThumbnailSize(); return (m_Parent->IsThumbnailOn() && xCanvas[0] > xThumb[0] && xCanvas[0] < xThumb[0] + sThumb[0] && xCanvas[1] > xThumb[1] && xCanvas[1] < xThumb[1] + sThumb[1]); } void OrthogonalSliceCursorNavigationModel ::ProcessThumbnailPanGesture(Vector2i uvOffset) { // Figure out how this movement translates to space units Vector2f xOffset( uvOffset[0] / m_Parent->GetThumbnailZoom(), uvOffset[1] / m_Parent->GetThumbnailZoom()); // Add to the position m_Parent->SetViewPosition(m_StartViewPosition - xOffset); } itksnap-3.4.0/GUI/Model/OrthogonalSliceCursorNavigationModel.h000066400000000000000000000053731263013355200243270ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef ORTHOGONALSLICECURSORNAVIGATIONMODEL_H #define ORTHOGONALSLICECURSORNAVIGATIONMODEL_H #include #include "AbstractModel.h" class GenericSliceModel; class OrthogonalSliceCursorNavigationModel : public AbstractModel { public: irisITKObjectMacro(OrthogonalSliceCursorNavigationModel, AbstractModel) irisSetMacro(Parent, GenericSliceModel *) irisGetMacro(Parent, GenericSliceModel *) // Move 3D cursor to (x,y) point on the screen supplied by user void UpdateCursor(Vector2f x); // Start zoom operation void BeginZoom(); // End zoom operation void EndZoom(); // Start pan operation void BeginPan(); // End pan void EndPan(); // Process zoom or pan operation (parameter is the gesture length, // which in theory should work both on desktop and on an iPhone). void ProcessZoomGesture(float scaleFactor); // Process pan operation (parameter is the gesture vector, i.e., mouse // drag or three-finger gesture) void ProcessPanGesture(Vector2f uvOffset); // Process a scrolling-type gesture (mouse wheel, or two-finger scroll) void ProcessScrollGesture(float gLength); // Check if the user press position is inside the thumbnail bool CheckZoomThumbnail(Vector2i xCanvas); // Process pan operation (parameter is the gesture vector, i.e., mouse // drag or three-finger gesture) void ProcessThumbnailPanGesture(Vector2i uvOffset); // Process arrow key and pageup/down commands void ProcessKeyNavigation(Vector3i dx); protected: OrthogonalSliceCursorNavigationModel() {} ~OrthogonalSliceCursorNavigationModel() {} // Zoom and pan factors at the beginning of interaction Vector2f m_StartViewPosition; float m_StartViewZoom; GenericSliceModel *m_Parent; }; #endif // ORTHOGONALSLICECURSORNAVIGATIONMODEL_H itksnap-3.4.0/GUI/Model/PaintbrushModel.cxx000066400000000000000000000347041263013355200205070ustar00rootroot00000000000000#include "PaintbrushModel.h" #include "GenericSliceModel.h" #include "GlobalState.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "itkRegionOfInterestImageFilter.h" #include "itkGradientAnisotropicDiffusionImageFilter.h" #include "itkGradientMagnitudeImageFilter.h" #include "itkWatershedImageFilter.h" // TODO: move this into a separate file!!!! class BrushWatershedPipeline { public: typedef itk::Image GreyImageType; typedef itk::Image LabelImageType; typedef itk::Image FloatImageType; typedef itk::Image WatershedImageType; typedef WatershedImageType::IndexType IndexType; BrushWatershedPipeline() { roi = ROIType::New(); adf = ADFType::New(); adf->SetInput(roi->GetOutput()); adf->SetConductanceParameter(0.5); gmf = GMFType::New(); gmf->SetInput(adf->GetOutput()); wf = WFType::New(); wf->SetInput(gmf->GetOutput()); } void PrecomputeWatersheds( GreyImageType *grey, LabelImageType *label, itk::ImageRegion<3> region, itk::Index<3> vcenter, size_t smoothing_iter) { this->region = region; // Get the offset of vcenter in the region if(region.IsInside(vcenter)) for(size_t d = 0; d < 3; d++) this->vcenter[d] = vcenter[d] - region.GetIndex()[d]; else for(size_t d = 0; d < 3; d++) this->vcenter[d] = region.GetSize()[d] / 2; // Create a backup of the label image LROIType::Pointer lroi = LROIType::New(); lroi->SetInput(label); lroi->SetRegionOfInterest(region); lroi->Update(); lsrc = lroi->GetOutput(); lsrc->DisconnectPipeline(); // Initialize the watershed pipeline roi->SetInput(grey); roi->SetRegionOfInterest(region); adf->SetNumberOfIterations(smoothing_iter); // Set the initial level to lowest possible - to get all watersheds wf->SetLevel(1.0); wf->Update(); } void RecomputeWatersheds(double level) { // Reupdate the filter with new level wf->SetLevel(level); wf->Update(); } bool IsPixelInSegmentation(IndexType idx) { // Get the watershed ID at the center voxel unsigned long wctr = wf->GetOutput()->GetPixel(vcenter); unsigned long widx = wf->GetOutput()->GetPixel(idx); return wctr == widx; } bool UpdateLabelImage( LabelImageType *ltrg, CoverageModeType mode, LabelType drawing_color, LabelType overwrt_color) { // Get the watershed ID at the center voxel unsigned long wid = wf->GetOutput()->GetPixel(vcenter); // Keep track of changed voxels bool flagChanged = false; // Do the update typedef itk::ImageRegionConstIterator WIter; typedef itk::ImageRegionIterator LIter; WIter wit(wf->GetOutput(), wf->GetOutput()->GetBufferedRegion()); LIter sit(lsrc, lsrc->GetBufferedRegion()); LIter tit(ltrg, region); for(; !wit.IsAtEnd(); ++sit,++tit,++wit) { LabelType pxLabel = sit.Get(); if(wit.Get() == wid) { // Standard paint mode if (mode == PAINT_OVER_ALL || (mode == PAINT_OVER_ONE && pxLabel == overwrt_color) || (mode == PAINT_OVER_VISIBLE && pxLabel != 0)) { pxLabel = drawing_color; } } if(pxLabel != tit.Get()) { tit.Set(pxLabel); flagChanged = true; } } if(flagChanged) ltrg->Modified(); return flagChanged; } private: typedef itk::RegionOfInterestImageFilter ROIType; typedef itk::RegionOfInterestImageFilter LROIType; typedef itk::GradientAnisotropicDiffusionImageFilter ADFType; typedef itk::GradientMagnitudeImageFilter GMFType; typedef itk::WatershedImageFilter WFType; ROIType::Pointer roi; ADFType::Pointer adf; GMFType::Pointer gmf; WFType::Pointer wf; itk::ImageRegion<3> region; LabelImageType::Pointer lsrc; itk::Index<3> vcenter; }; PaintbrushModel::PaintbrushModel() { m_ReverseMode = false; m_Watershed = new BrushWatershedPipeline(); m_ContextLayerId = (unsigned long) -1; m_IsEngaged = false; } PaintbrushModel::~PaintbrushModel() { delete m_Watershed; } Vector3f PaintbrushModel::ComputeOffset() { // Get the paintbrush properties PaintbrushSettings pbs = m_Parent->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); Vector3f offset(0.0); if(fmod(pbs.radius,1.0)==0) { offset.fill(0.5); offset(m_Parent->GetSliceDirectionInImageSpace()) = 0.0; } return offset; } void PaintbrushModel::ComputeMousePosition(const Vector3f &xSlice) { // Only when an image is loaded if(!m_Parent->GetDriver()->IsMainImageLoaded()) return; // Get the paintbrush properties PaintbrushSettings pbs = m_Parent->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); // Compute the new cross-hairs position in image space Vector3f xCross = m_Parent->MapSliceToImage(xSlice); // Round the cross-hairs position down to integer Vector3i xCrossInteger = to_int(xCross + ComputeOffset()); // Make sure that the cross-hairs position is within bounds by clamping // it to image dimensions Vector3i xSize = to_int(m_Parent->GetDriver()->GetCurrentImageData()->GetVolumeExtents()); Vector3ui newpos = to_unsigned_int( xCrossInteger.clamp(Vector3i(0),xSize - Vector3i(1))); if(newpos != m_MousePosition || m_MouseInside == false) { m_MousePosition = newpos; m_MouseInside = true; InvokeEvent(PaintbrushMovedEvent()); } } bool PaintbrushModel::TestInside(const Vector2d &x, const PaintbrushSettings &ps) { return this->TestInside(Vector3d(x(0), x(1), 0.0), ps); } // TODO: make this faster by precomputing all the repeated quantities. The inside // check should take a lot less time! bool PaintbrushModel::TestInside(const Vector3d &x, const PaintbrushSettings &ps) { // Determine how to scale the voxels Vector3d xTest = x; if(ps.isotropic) { const Vector3f &spacing = m_Parent->GetSliceSpacing(); double xMinVoxelDim = spacing.min_value(); xTest(0) *= spacing(0) / xMinVoxelDim; xTest(1) *= spacing(1) / xMinVoxelDim; xTest(2) *= spacing(2) / xMinVoxelDim; } // Test inside/outside if(ps.mode == PAINTBRUSH_ROUND) { return xTest.squared_magnitude() <= (ps.radius-0.25) * (ps.radius-0.25); } else { return xTest.inf_norm() <= ps.radius - 0.25; } } bool PaintbrushModel ::ProcessPushEvent(const Vector3f &xSlice, const Vector2ui &gridCell, bool reverse_mode) { // Get the paintbrush properties (TODO: should we own them?) PaintbrushSettings pbs = m_Parent->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); // Store the unique ID of the layer in context ImageWrapperBase *layer = m_Parent->GetLayerForNthTile(gridCell[0], gridCell[1]); if(layer) { // Set the layer m_ContextLayerId = layer->GetUniqueId(); m_IsEngaged = true; // Compute the mouse position ComputeMousePosition(xSlice); // Check if the right button was pressed ApplyBrush(reverse_mode, false); // Store the reverse mode m_ReverseMode = reverse_mode; // Store this as the last apply position m_LastApplyX = xSlice; // Eat the event unless cursor chasing is enabled return pbs.chase ? 0 : 1; } else { m_ContextLayerId = (unsigned long) -1; m_IsEngaged = false; return 0; } } bool PaintbrushModel ::ProcessDragEvent(const Vector3f &xSlice, const Vector3f &xSliceLast, double pixelsMoved, bool release) { IRISApplication *driver = m_Parent->GetDriver(); PaintbrushSettings pbs = driver->GetGlobalState()->GetPaintbrushSettings(); if(m_IsEngaged) { // The behavior is different for 'fast' regular brushes and adaptive brush. For the // adaptive brush, dragging is disabled. if(pbs.mode != PAINTBRUSH_WATERSHED || m_ReverseMode) { // See how much we have moved since the last event. If we moved more than // the value of the radius, we interpolate the path and place brush strokes // along the path if(pixelsMoved > pbs.radius) { // Break up the path into steps size_t nSteps = (int) ceil(pixelsMoved / pbs.radius); for(size_t i = 0; i < nSteps; i++) { float t = (1.0 + i) / nSteps; Vector3f X = t * m_LastApplyX + (1.0f - t) * xSlice; ComputeMousePosition(X); ApplyBrush(m_ReverseMode, true); } } else { // Find the pixel under the mouse ComputeMousePosition(xSlice); // Scan convert the points into the slice ApplyBrush(m_ReverseMode, true); } // Store this as the last apply position m_LastApplyX = xSlice; } // If the mouse is being released, we need to commit the drawing if(release) { driver->StoreUndoPoint("Drawing with paintbrush"); // TODO: this is ugly. The code for applying a brush should really be // placed in the IRISApplication. driver->InvokeEvent(SegmentationChangeEvent()); m_IsEngaged = false; m_ContextLayerId = (unsigned long) -1; } // Eat the event unless cursor chasing is enabled return pbs.chase ? 0 : 1; } else return 0; } bool PaintbrushModel::ProcessMouseMoveEvent(const Vector3f &xSlice) { ComputeMousePosition(xSlice); return true; } bool PaintbrushModel::ProcessMouseLeaveEvent() { m_MouseInside = false; InvokeEvent(PaintbrushMovedEvent()); return true; } void PaintbrushModel::AcceptAtCursor() { IRISApplication *driver = m_Parent->GetDriver(); m_MousePosition = m_Parent->GetDriver()->GetCursorPosition(); m_MouseInside = true; ApplyBrush(false, false); // We need to commit the drawing driver->StoreUndoPoint("Drawing with paintbrush"); // TODO: this is ugly. The code for applying a brush should really be // placed in the IRISApplication. driver->InvokeEvent(SegmentationChangeEvent()); } bool PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging) { // Get the global objects IRISApplication *driver = m_Parent->GetDriver(); GlobalState *gs = driver->GetGlobalState(); // Get the segmentation image LabelImageWrapper *imgLabel = driver->GetCurrentImageData()->GetSegmentation(); // Get the paint properties LabelType drawing_color = gs->GetDrawingColorLabel(); DrawOverFilter drawover = gs->GetDrawOverFilter(); // Get the paintbrush properties PaintbrushSettings pbs = gs->GetPaintbrushSettings(); // Whether watershed filter is used (adaptive brush) bool flagWatershed = ( pbs.mode == PAINTBRUSH_WATERSHED && (!reverse_mode) && (!dragging)); // Define a region of interest LabelImageWrapper::ImageType::RegionType xTestRegion; for(size_t i = 0; i < 3; i++) { if(i != imgLabel->GetDisplaySliceImageAxis(m_Parent->GetId()) || pbs.volumetric) { // For watersheds, the radius must be > 2 double rad = (flagWatershed && pbs.radius < 1.5) ? 1.5 : pbs.radius; xTestRegion.SetIndex(i, (long) (m_MousePosition(i) - rad)); // + 1); xTestRegion.SetSize(i, (long) (2 * rad + 1)); // - 1); } else { xTestRegion.SetIndex(i, m_MousePosition(i)); xTestRegion.SetSize(i, 1); } } // Crop the region by the buffered region xTestRegion.Crop(imgLabel->GetImage()->GetBufferedRegion()); // Flag to see if anything was changed bool flagUpdate = false; // Special code for Watershed brush if(flagWatershed) { GenericImageData *gid = driver->GetCurrentImageData(); // Get the currently engaged layer ImageWrapperBase *context_layer = gid->FindLayer(m_ContextLayerId, false); if(!context_layer) context_layer = gid->GetMain(); // Precompute the watersheds m_Watershed->PrecomputeWatersheds( context_layer->GetDefaultScalarRepresentation()->GetCommonFormatImage(), driver->GetCurrentImageData()->GetSegmentation()->GetImage(), xTestRegion, to_itkIndex(m_MousePosition), pbs.watershed.smooth_iterations); m_Watershed->RecomputeWatersheds(pbs.watershed.level); } // Shift vector (different depending on whether the brush has odd/even diameter Vector3f offset = ComputeOffset(); // Iterate over the region LabelImageWrapper::Iterator it(imgLabel->GetImage(), xTestRegion); for(; !it.IsAtEnd(); ++it) { // Check if we are inside the sphere LabelImageWrapper::ImageType::IndexType idx = it.GetIndex(); Vector3f xDelta = offset + to_float(Vector3l(idx.GetIndex())) - to_float(m_MousePosition); Vector3d xDeltaSliceSpace = to_double( m_Parent->GetImageToDisplayTransform().TransformVector(xDelta)); // Check if the pixel is inside if(!TestInside(xDeltaSliceSpace, pbs)) continue; // Check if the pixel is in the watershed if(flagWatershed) { LabelImageWrapper::ImageType::IndexType idxoff = to_itkIndex( Vector3l(idx.GetIndex()) - Vector3l(xTestRegion.GetIndex().GetIndex())); if(!m_Watershed->IsPixelInSegmentation(idxoff)) continue; } // Paint the pixel LabelType pxLabel = it.Get(); // Standard paint mode if(!reverse_mode) { if(drawover.CoverageMode == PAINT_OVER_ALL || (drawover.CoverageMode == PAINT_OVER_ONE && pxLabel == drawover.DrawOverLabel) || (drawover.CoverageMode == PAINT_OVER_VISIBLE && pxLabel != 0)) { it.Set(drawing_color); if(pxLabel != drawing_color) flagUpdate = true; } } // Background paint mode (clear label over current label) else { if(drawing_color != 0 && pxLabel == drawing_color) { it.Set(0); if(pxLabel != 0) flagUpdate = true; } else if(drawing_color == 0 && drawover.CoverageMode == PAINT_OVER_ONE) { it.Set(drawover.DrawOverLabel); if(pxLabel != drawover.DrawOverLabel) flagUpdate = true; } } } // Image has been updated if(flagUpdate) { imgLabel->GetImage()->Modified(); } return flagUpdate; } Vector3f PaintbrushModel::GetCenterOfPaintbrushInSliceSpace() { PaintbrushSettings pbs = m_Parent->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); if(fmod(pbs.radius, 1.0) == 0) return m_Parent->MapImageToSlice(to_float(m_MousePosition)); else return m_Parent->MapImageToSlice(to_float(m_MousePosition) + Vector3f(0.5f)); } itksnap-3.4.0/GUI/Model/PaintbrushModel.h000066400000000000000000000035041263013355200201260ustar00rootroot00000000000000#ifndef PAINTBRUSHMODEL_H #define PAINTBRUSHMODEL_H #include "AbstractModel.h" #include "GlobalState.h" class GenericSliceModel; class BrushWatershedPipeline; class PaintbrushModel : public AbstractModel { public: irisITKObjectMacro(PaintbrushModel, AbstractModel) itkEventMacro(PaintbrushMovedEvent, IRISEvent) irisGetSetMacro(Parent, GenericSliceModel *) irisIsMacro(MouseInside) FIRES(PaintbrushMovedEvent) bool ProcessPushEvent(const Vector3f &xSlice, const Vector2ui &gridCell, bool reverse_mode); bool ProcessDragEvent(const Vector3f &xSlice, const Vector3f &xSliceLast, double pixelsMoved, bool release); bool ProcessMouseMoveEvent(const Vector3f &xSlice); bool ProcessMouseLeaveEvent(); void AcceptAtCursor(); // Get the location in slice coordinates where the center of the paintbrush // should be rendered Vector3f GetCenterOfPaintbrushInSliceSpace(); protected: // Whether we are inverting the paintbrush when drawing bool m_ReverseMode; // Mouse position in voxel coordinates Vector3ui m_MousePosition; bool m_MouseInside; // Mouse position in slice coordinates from which we need to draw the // next segment Vector3f m_LastApplyX; // Layer over which the drawing operation started unsigned long m_ContextLayerId; // Whether the push operation was in a paintable location bool m_IsEngaged; PaintbrushModel(); virtual ~PaintbrushModel(); Vector3f ComputeOffset(); void ComputeMousePosition(const Vector3f &xSlice); bool ApplyBrush(bool reverse_mode, bool dragging); bool TestInside(const Vector2d &x, const PaintbrushSettings &ps); bool TestInside(const Vector3d &x, const PaintbrushSettings &ps); GenericSliceModel *m_Parent; BrushWatershedPipeline *m_Watershed; friend class PaintbrushRenderer; }; #endif // PAINTBRUSHMODEL_H itksnap-3.4.0/GUI/Model/PaintbrushSettingsModel.cxx000066400000000000000000000072411263013355200222240ustar00rootroot00000000000000#include "PaintbrushSettingsModel.h" #include "GlobalUIModel.h" #include "GlobalState.h" PaintbrushSettingsModel::PaintbrushSettingsModel() { m_PaintbrushSettingsModel = wrapGetterSetterPairAsProperty(this, &Self::GetPaintbrushSettings, &Self::SetPaintbrushSettings); // Create models for the fields of PaintbrushSettings m_PaintbrushModeModel = wrapStructMemberAsSimpleProperty( m_PaintbrushSettingsModel, offsetof(PaintbrushSettings, mode)); m_VolumetricBrushModel = wrapStructMemberAsSimpleProperty( m_PaintbrushSettingsModel, offsetof(PaintbrushSettings, volumetric)); m_IsotropicBrushModel = wrapStructMemberAsSimpleProperty( m_PaintbrushSettingsModel, offsetof(PaintbrushSettings, isotropic)); m_ChaseCursorModel = wrapStructMemberAsSimpleProperty( m_PaintbrushSettingsModel, offsetof(PaintbrushSettings, chase)); // The paintbrush size model requires special processing, so it is implemeted // using a getter/setter pair m_BrushSizeModel = wrapGetterSetterPairAsProperty( this, &Self::GetBrushSizeValueAndRange, &Self::SetBrushSizeValue); m_AdaptiveModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetAdaptiveModeValue); m_ThresholdLevelModel = wrapGetterSetterPairAsProperty( this, &Self::GetThresholdLevelValueAndRange, &Self::SetThresholdLevelValue); m_SmoothingIterationsModel = wrapGetterSetterPairAsProperty( this, &Self::GetSmoothingIterationValueAndRange, &Self::SetSmoothingIterationValue); } PaintbrushSettingsModel::~PaintbrushSettingsModel() { } PaintbrushSettings PaintbrushSettingsModel::GetPaintbrushSettings() { return m_ParentModel->GetGlobalState()->GetPaintbrushSettings(); } void PaintbrushSettingsModel::SetPaintbrushSettings(PaintbrushSettings ps) { m_ParentModel->GetGlobalState()->SetPaintbrushSettings(ps); InvokeEvent(ModelUpdateEvent()); } bool PaintbrushSettingsModel ::GetBrushSizeValueAndRange(int &value, NumericValueRange *domain) { PaintbrushSettings pbs = GetPaintbrushSettings(); // Round just in case value = (int) (pbs.radius * 2 + 0.5); if(domain) domain->Set(1, 40, 1); return true; } void PaintbrushSettingsModel::SetBrushSizeValue(int value) { PaintbrushSettings pbs = GetPaintbrushSettings(); pbs.radius = 0.5 * value; SetPaintbrushSettings(pbs); } bool PaintbrushSettingsModel::GetAdaptiveModeValue(bool &value) { PaintbrushSettings pbs = GetPaintbrushSettings(); value = (pbs.mode == PAINTBRUSH_WATERSHED); return true; } bool PaintbrushSettingsModel::GetThresholdLevelValueAndRange(double &value, NumericValueRange *domain) { PaintbrushSettings pbs = GetPaintbrushSettings(); value = pbs.watershed.level * 100.0; if(domain) { domain->Set(0, 100, 1); } return true; } void PaintbrushSettingsModel::SetThresholdLevelValue(double value) { PaintbrushSettings pbs = GetPaintbrushSettings(); pbs.watershed.level = 0.01 * value; SetPaintbrushSettings(pbs); } bool PaintbrushSettingsModel::GetSmoothingIterationValueAndRange( int &value, NumericValueRange *domain) { PaintbrushSettings pbs = GetPaintbrushSettings(); value = pbs.watershed.smooth_iterations; if(domain) { domain->Set(0, 100, 1); } return true; } void PaintbrushSettingsModel::SetSmoothingIterationValue(int value) { PaintbrushSettings pbs = GetPaintbrushSettings(); pbs.watershed.smooth_iterations = value; SetPaintbrushSettings(pbs); } itksnap-3.4.0/GUI/Model/PaintbrushSettingsModel.h000066400000000000000000000046041263013355200216510ustar00rootroot00000000000000#ifndef PAINTBRUSHSETTINGSMODEL_H #define PAINTBRUSHSETTINGSMODEL_H #include "AbstractModel.h" #include "PropertyModel.h" #include "GlobalState.h" class GlobalUIModel; class PaintbrushSettingsModel : public AbstractModel { public: irisITKObjectMacro(PaintbrushSettingsModel, AbstractModel) typedef AbstractPropertyModel AbstractPaintbrushModeModel; typedef ConcretePropertyModel ConcretePaintbrushModeModel; irisGetSetMacro(ParentModel, GlobalUIModel *) irisGetMacro(PaintbrushModeModel, AbstractPaintbrushModeModel *) irisGetMacro(BrushSizeModel, AbstractRangedIntProperty *) irisGetMacro(VolumetricBrushModel, AbstractSimpleBooleanProperty *) irisGetMacro(IsotropicBrushModel, AbstractSimpleBooleanProperty *) irisGetMacro(ChaseCursorModel, AbstractSimpleBooleanProperty *) irisGetMacro(AdaptiveModeModel, AbstractSimpleBooleanProperty *) irisGetMacro(ThresholdLevelModel, AbstractRangedDoubleProperty *) irisGetMacro(SmoothingIterationsModel, AbstractRangedIntProperty *) protected: PaintbrushSettingsModel(); virtual ~PaintbrushSettingsModel(); GlobalUIModel *m_ParentModel; // Model that sets and retrieves the PaintbrushSettings typedef AbstractPropertyModel PaintbrushSettingsStructModel; SmartPtr m_PaintbrushSettingsModel; PaintbrushSettings GetPaintbrushSettings(); void SetPaintbrushSettings(PaintbrushSettings ps); SmartPtr m_PaintbrushModeModel; SmartPtr m_VolumetricBrushModel; SmartPtr m_IsotropicBrushModel; SmartPtr m_ChaseCursorModel; SmartPtr m_BrushSizeModel; bool GetBrushSizeValueAndRange(int &value, NumericValueRange *domain); void SetBrushSizeValue(int value); SmartPtr m_AdaptiveModeModel; bool GetAdaptiveModeValue(bool &value); SmartPtr m_ThresholdLevelModel; bool GetThresholdLevelValueAndRange(double &value, NumericValueRange *domain); void SetThresholdLevelValue(double value); SmartPtr m_SmoothingIterationsModel; bool GetSmoothingIterationValueAndRange(int &value, NumericValueRange *domain); void SetSmoothingIterationValue(int value); }; #endif // PAINTBRUSHSETTINGSMODEL_H itksnap-3.4.0/GUI/Model/PolygonDrawingModel.cxx000066400000000000000000000500471263013355200213310ustar00rootroot00000000000000#include "PolygonDrawingModel.h" #include "PolygonScanConvert.h" #include "SNAPOpenGL.h" #include #include #include #include #include #include "GenericSliceModel.h" #include "itkImage.h" #include "itkPointSet.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "IRISApplication.h" #include #include // Enable this model to be used with the flag engine template class SNAPUIFlag; using namespace std; PolygonDrawingModel ::PolygonDrawingModel() { m_CachedPolygon = false; m_State = INACTIVE_STATE; m_SelectedVertices = false; m_DraggingPickBox = false; m_StartX = 0; m_StartY = 0; m_PolygonSlice = PolygonSliceType::New(); m_HoverOverFirstVertex = false; m_FreehandFittingRateModel = NewRangedConcreteProperty(8.0, 0.0, 100.0, 1.0); } PolygonDrawingModel ::~PolygonDrawingModel() { } Vector2f PolygonDrawingModel::GetPixelSize() { float vppr = m_Parent->GetSizeReporter()->GetViewportPixelRatio(); Vector3f x = m_Parent->MapWindowToSlice(Vector2f(vppr)) - m_Parent->MapWindowToSlice(Vector2f(0.0f)); return Vector2f(x[0],x[1]); } bool PolygonDrawingModel ::CheckNearFirstVertex(float x, float y, float pixel_x, float pixel_y) { if(m_Vertices.size() > 2) { Vector2d A(m_Vertices.front().x / pixel_x, m_Vertices.front().y / pixel_y); Vector2d C(x / pixel_x, y / pixel_y); if((A-C).inf_norm() < 4) return true; } return false; } bool PolygonDrawingModel ::ProcessPushEvent(float x, float y, bool shift_state) { bool handled = false; Vector2f pxsize = GetPixelSize(); float pixel_x = pxsize(0), pixel_y = pxsize(1); if(m_State == INACTIVE_STATE) { SetState(DRAWING_STATE); m_Vertices.push_back( Vertex(x, y, false, true) ); handled = true; } else if(m_State == DRAWING_STATE) { // Restart Dragging m_DragVertices.clear(); // The hover state is false m_HoverOverFirstVertex = false; // Left click means to add a vertex to the polygon. However, for // compatibility reasons, we must make sure that there are no duplicates // in the polygon (otherwise, division by zero occurs). if(m_Vertices.size() == 0 || m_Vertices.back().x != x || m_Vertices.back().y != y) { // Check if the user wants to close the polygon if(CheckNearFirstVertex(x, y, pixel_x, pixel_y)) ClosePolygon(); else m_Vertices.push_back( Vertex(x, y, false, true) ); } handled = true; } else if(m_State == EDITING_STATE) { m_StartX = x; m_StartY = y; if(!shift_state && m_SelectedVertices && (x >= (m_EditBox[0] - 4.0*pixel_x)) && (x <= (m_EditBox[1] + 4.0*pixel_x)) && (y >= (m_EditBox[2] - 4.0*pixel_y)) && (y <= (m_EditBox[3] + 4.0*pixel_y))) { // user not holding shift key; if user clicked inside edit box, // edit box will be moved in drag event } else { if(!shift_state) { // clicked outside of edit box & shift not held, this means the // current selection will be cleared for(VertexIterator it = m_Vertices.begin(); it!=m_Vertices.end(); ++it) it->selected = false; m_SelectedVertices = false; } // check if vertex clicked if(CheckClickOnVertex(x,y,pixel_x,pixel_y,4)) ComputeEditBox(); // check if clicked near a line segment else if(CheckClickOnLineSegment(x,y,pixel_x,pixel_y,4)) ComputeEditBox(); // otherwise start dragging pick box else { m_DraggingPickBox = true; m_SelectionBox[0] = m_SelectionBox[1] = x; m_SelectionBox[2] = m_SelectionBox[3] = y; } } handled = true; } if(handled) InvokeEvent(StateMachineChangeEvent()); return handled; } bool PolygonDrawingModel ::ProcessMouseMoveEvent(float x, float y) { if(m_State == DRAWING_STATE) { // Check if we are hovering over the starting vertex Vector2f pxsize = GetPixelSize(); float pixel_x = pxsize(0), pixel_y = pxsize(1); bool hover = CheckNearFirstVertex(x, y, pixel_x, pixel_y); if(hover != m_HoverOverFirstVertex) { m_HoverOverFirstVertex = hover; return true; } } return false; } bool PolygonDrawingModel ::ProcessDragEvent(float x, float y) { bool handled = false; if(m_State == DRAWING_STATE) { if(m_Vertices.size() == 0) { m_Vertices.push_back(Vertex(x,y,false,true)); } else { // Check/set the hover state ProcessMouseMoveEvent(x, y); // Check if a point should be added here if(this->GetFreehandFittingRate() == 0) { m_Vertices.push_back(Vertex(x,y,false,false)); } else { Vector2f pxsize = GetPixelSize(); Vertex &v = m_Vertices.back(); double dx = (v.x-x) / pxsize[0]; double dy = (v.y-y) / pxsize[1]; double d = dx*dx+dy*dy; if(d >= this->GetFreehandFittingRate() * this->GetFreehandFittingRate()) m_Vertices.push_back(Vertex(x,y,false,true)); } } handled = true; } else if(m_State == EDITING_STATE) { if (m_DraggingPickBox) { m_SelectionBox[1] = x; m_SelectionBox[3] = y; } else { m_EditBox[0] += (x - m_StartX); m_EditBox[1] += (x - m_StartX); m_EditBox[2] += (y - m_StartY); m_EditBox[3] += (y - m_StartY); // If the selection is bounded by control vertices, we simply shift it for(VertexIterator it = m_Vertices.begin(); it!=m_Vertices.end(); ++it) { if (it->selected) { it->x += (x - m_StartX); it->y += (y - m_StartY); } } // If the selection is bounded by freehand vertices, we apply a smooth m_StartX = x; m_StartY = y; } handled = true; } if(handled) InvokeEvent(StateMachineChangeEvent()); return handled; } bool PolygonDrawingModel ::ProcessReleaseEvent(float x, float y) { bool handled = false; Vector2f pxsize = GetPixelSize(); float pixel_x = pxsize(0), pixel_y = pxsize(1); if(m_State == DRAWING_STATE) { // Check if we've closed the loop if(CheckNearFirstVertex(x, y, pixel_x, pixel_y)) { ClosePolygon(); } // Make sure the last point is a control point if(m_Vertices.size() && m_Vertices.back().control == false) m_Vertices.back().control = true; handled = true; } else if(m_State == EDITING_STATE) { if (m_DraggingPickBox) { m_DraggingPickBox = false; float temp; if (m_SelectionBox[0] > m_SelectionBox[1]) { temp = m_SelectionBox[0]; m_SelectionBox[0] = m_SelectionBox[1]; m_SelectionBox[1] = temp; } if (m_SelectionBox[2] > m_SelectionBox[3]) { temp = m_SelectionBox[2]; m_SelectionBox[2] = m_SelectionBox[3]; m_SelectionBox[3] = temp; } for(VertexIterator it = m_Vertices.begin(); it!=m_Vertices.end(); ++it) { if((it->x >= m_SelectionBox[0]) && (it->x <= m_SelectionBox[1]) && (it->y >= m_SelectionBox[2]) && (it->y <= m_SelectionBox[3])) it->selected = 1; } ComputeEditBox(); } handled = true; } if(handled) InvokeEvent(StateMachineChangeEvent()); return handled; } /** * ComputeEditBox() * * purpose: * compute the bounding box around selected vertices * * post: * if m_Vertices are selected, sets m_SelectedVertices to 1, else 0 */ void PolygonDrawingModel ::ComputeEditBox() { VertexIterator it; // Find the first selected vertex and initialize the selection box m_SelectedVertices = false; for (it = m_Vertices.begin(); it!=m_Vertices.end();++it) { if (it->selected) { m_EditBox[0] = m_EditBox[1] = it->x; m_EditBox[2] = m_EditBox[3] = it->y; m_SelectedVertices = true; break; } } // Continue only if a selection exists if (!m_SelectedVertices) return; // Grow selection box to fit all selected vertices for(it = m_Vertices.begin(); it!=m_Vertices.end();++it) { if (it->selected) { if (it->x < m_EditBox[0]) m_EditBox[0] = it->x; else if (it->x > m_EditBox[1]) m_EditBox[1] = it->x; if (it->y < m_EditBox[2]) m_EditBox[2] = it->y; else if (it->y > m_EditBox[3]) m_EditBox[3] = it->y; } } } void PolygonDrawingModel ::DropLastPoint() { if(m_State == DRAWING_STATE) { if(m_Vertices.size()) m_Vertices.pop_back(); InvokeEvent(StateMachineChangeEvent()); } } void PolygonDrawingModel ::ClosePolygon() { if(m_State == DRAWING_STATE) { SetState(EDITING_STATE); m_SelectedVertices = true; for(VertexIterator it = m_Vertices.begin(); it!=m_Vertices.end(); ++it) it->selected = false; ComputeEditBox(); InvokeEvent(StateMachineChangeEvent()); } } /** * Delete() * * purpose: * delete all vertices that are selected * * post: * if all m_Vertices removed, m_State becomes INACTIVE_STATE * length of m_Vertices array does not decrease */ void PolygonDrawingModel ::Delete() { VertexIterator it=m_Vertices.begin(); while(it!=m_Vertices.end()) { if(it->selected) it = m_Vertices.erase(it); else ++it; } if (m_Vertices.empty()) { SetState(INACTIVE_STATE); m_SelectedVertices = false; } ComputeEditBox(); InvokeEvent(StateMachineChangeEvent()); } void PolygonDrawingModel ::Reset() { SetState(INACTIVE_STATE); m_Vertices.clear(); ComputeEditBox(); InvokeEvent(StateMachineChangeEvent()); } /** * Insert() * * purpose: * insert vertices between adjacent selected vertices * * post: * length of m_Vertices array does not decrease */ void PolygonDrawingModel ::Insert() { // Insert a vertex between every pair of adjacent vertices VertexIterator it = m_Vertices.begin(); while(it != m_Vertices.end()) { // Get the itNext iterator to point to the next point in the list VertexIterator itNext = it; if(++itNext == m_Vertices.end()) itNext = m_Vertices.begin(); // Check if the insertion is needed if(it->selected && itNext->selected) { // Insert a new vertex Vertex vNew(0.5 * (it->x + itNext->x), 0.5 * (it->y + itNext->y), true, true); it = m_Vertices.insert(++it, vNew); } // On to the next point ++it; } InvokeEvent(StateMachineChangeEvent()); } int PolygonDrawingModel ::GetNumberOfSelectedSegments() { int isel = 0; for(VertexIterator it = m_Vertices.begin(); it != m_Vertices.end(); it++) { // Get the itNext iterator to point to the next point in the list VertexIterator itNext = it; if(++itNext == m_Vertices.end()) itNext = m_Vertices.begin(); // Check if the insertion is needed if(it->selected && itNext->selected) isel++; } return isel; } void PolygonDrawingModel ::ProcessFreehandCurve() { // Special case: no fitting if(this->GetFreehandFittingRate() == 0.0) { for(VertexIterator it = m_DragVertices.begin(); it != m_DragVertices.end(); ++it) { m_Vertices.push_back(*it); } m_DragVertices.clear(); return; } // We will fit a b-spline of the 0-th order to the freehand curve if(m_Vertices.size() > 0) { // Prepend the last vertex before freehand drawing m_DragVertices.push_front(m_Vertices.back()); m_Vertices.pop_back(); } // Create a list of input points typedef itk::Vector VectorType; typedef itk::Image ImageType; typedef itk::PointSet PointSetType; PointSetType::Pointer pointSet = PointSetType::New(); double len = 0; double t = 0, dt = 1.0 / (m_DragVertices.size()); size_t i = 0; Vertex last; for(VertexIterator it = m_DragVertices.begin(); it != m_DragVertices.end(); ++it) { PointSetType::PointType point; point[0] = t; pointSet->SetPoint(i,point); VectorType v; v[0] = it->x; v[1] = it->y; pointSet->SetPointData(i, v); t+=dt; i++; if(it != m_DragVertices.begin()) { double dx = last.x - it->x; double dy = last.y - it->y; len += sqrt(dx * dx + dy * dy); } last = *it; } // Compute the number of control points size_t nctl = (size_t)ceil(len / this->GetFreehandFittingRate()); if(nctl < 3) nctl = 3; // Compute the number of levels and the control points at coarsest level size_t nl = 1; size_t ncl = nctl; while(ncl >= 8) { ncl >>= 1; nl++; } // Create the scattered interpolator typedef itk::BSplineScatteredDataPointSetToImageFilter< PointSetType, ImageType> FilterType; FilterType::Pointer filter = FilterType::New(); ImageType::SpacingType spacing; spacing.Fill( 0.001 ); ImageType::SizeType size; size.Fill((int)(1.0/spacing[0])); ImageType::PointType origin; origin.Fill(0.0); filter->SetSize( size ); filter->SetOrigin( origin ); filter->SetSpacing( spacing ); filter->SetInput( pointSet ); filter->SetSplineOrder( 1 ); FilterType::ArrayType ncps; ncps.Fill(ncl); filter->SetNumberOfLevels(nl); filter->SetNumberOfControlPoints(ncps); filter->SetGenerateOutputImage(false); // Run the filter filter->Update(); ImageType::Pointer lattice = filter->GetPhiLattice(); size_t n = lattice->GetBufferedRegion().GetNumberOfPixels(); for(size_t i = 0; i < n; i++) { ImageType::IndexType idx; idx.Fill(i); VectorType v = lattice->GetPixel(idx); m_Vertices.push_back(Vertex(v[0],v[1],false,true)); } /* // Get the control points? double du = 1.0 / nctl; for(double u = 0; u < 1.00001; u += du) { if(u > 1.0) u = 1.0; PointSetType::PointType point; point[0] = u; VectorType v; filter->Evaluate(point,v); m_Vertices.push_back(Vertex(v[0],v[1],false)); } */ // Empty the drag list // m_DragVertices.clear(); } bool PolygonVertexTest(const PolygonVertex &v1, const PolygonVertex &v2) { return v1.x == v2.x && v1.y == v2.y; } /** * AcceptPolygon() * * purpose: * to rasterize the current polygon into a buffer & copy the edited polygon * into the polygon m_Cache * * parameters: * buffer - an array of unsigned chars interpreted as an RGBA buffer * width - the width of the buffer * height - the height of the buffer * * pre: * buffer array has size width*height*4 * m_State == EDITING_STATE * * post: * m_State == INACTIVE_STATE */ void PolygonDrawingModel ::AcceptPolygon(std::vector &warnings) { assert(m_State == EDITING_STATE); // Allocate the polygon to match current image size. This will only // allocate new memory if the slice size changed itk::Size<2> sz = {{ m_Parent->GetSliceSize()[0], m_Parent->GetSliceSize()[1] }}; m_PolygonSlice->SetRegions(sz); m_PolygonSlice->Allocate(); // Remove duplicates from the vertex array VertexIterator itEnd = std::unique(m_Vertices.begin(), m_Vertices.end(), PolygonVertexTest); m_Vertices.erase(itEnd, m_Vertices.end()); // There may still be duplicates in the array, in which case we should // add a tiny offset to them. Thanks to Jeff Tsao for this bug fix! std::set< std::pair > xVertexSet; vnl_random rnd; for(VertexIterator it = m_Vertices.begin(); it != m_Vertices.end(); ++it) { while(xVertexSet.find(make_pair(it->x, it->y)) != xVertexSet.end()) { it->x += 0.0001 * rnd.drand32(-1.0, 1.0); it->y += 0.0001 * rnd.drand32(-1.0, 1.0); } xVertexSet.insert(make_pair(it->x, it->y)); } // Scan convert the points into the slice typedef PolygonScanConvert< unsigned char, GL_UNSIGNED_BYTE, VertexIterator> ScanConvertType; ScanConvertType::RasterizeFilled( m_Vertices.begin(), m_Vertices.size(), m_PolygonSlice); // Apply the segmentation to the main segmentation int nUpdates = m_Parent->MergeSliceSegmentation(m_PolygonSlice); if(nUpdates == 0) { warnings.push_back( IRISWarning("Warning: No voxels updated." "No voxels in the segmentation image were changed as the " "result of accepting this polygon. Check that the foreground " "and background labels are set correctly.")); } // Copy polygon into polygon m_Cache m_CachedPolygon = true; m_Cache = m_Vertices; // Reset the vertex array for next time m_Vertices.clear(); m_SelectedVertices = false; // Set the state SetState(INACTIVE_STATE); InvokeEvent(StateMachineChangeEvent()); } /** * PastePolygon() * * purpose: * copy the m_Cached polygon to the edited polygon * * pre: * m_CachedPolygon == 1 * m_State == INACTIVE_STATE * * post: * m_State == EDITING_STATE */ void PolygonDrawingModel ::PastePolygon(void) { // Copy the cache into the vertices m_Vertices = m_Cache; // Select everything for(VertexIterator it = m_Vertices.begin(); it!=m_Vertices.end();++it) it->selected = false; // Set the state m_SelectedVertices = false; SetState(EDITING_STATE); // Compute the edit box ComputeEditBox(); InvokeEvent(StateMachineChangeEvent()); } /** * Check if a click is within k pixels of a vertex, if so select the vertices * of that line segment */ bool PolygonDrawingModel ::CheckClickOnVertex( float x, float y, float pixel_x, float pixel_y, int k) { // check if clicked within 4 pixels of a node (use closest node) VertexIterator itmin = m_Vertices.end(); double distmin = k; for(VertexIterator it = m_Vertices.begin(); it!=m_Vertices.end(); ++it) { Vector2d A(it->x / pixel_x, it->y / pixel_y); Vector2d C(x / pixel_x, y / pixel_y); double dist = (A-C).inf_norm(); if(distmin > dist) { distmin = dist; itmin = it; } } if(itmin != m_Vertices.end()) { itmin->selected = true; return true; } else return false; } /** * Check if a click is within k pixels of a line segment, if so select the vertices * of that line segment */ bool PolygonDrawingModel ::CheckClickOnLineSegment( float x, float y, float pixel_x, float pixel_y, int k) { // check if clicked near a line segment VertexIterator itmin1 = m_Vertices.end(), itmin2 = m_Vertices.end(); double distmin = k; for(VertexIterator it = m_Vertices.begin(); it!=m_Vertices.end(); ++it) { VertexIterator itnext = it; if(++itnext == m_Vertices.end()) itnext = m_Vertices.begin(); Vector2d A(it->x / pixel_x, it->y / pixel_y); Vector2d B(itnext->x / pixel_x, itnext->y / pixel_y); Vector2d C(x / pixel_x, y / pixel_y); double ab = (A - B).squared_magnitude(); if(ab > 0) { double alpha = - dot_product(A-B, B-C) / ab; if(alpha > 0 && alpha < 1) { double dist = (alpha * A + (1-alpha) * B - C).magnitude(); if(distmin > dist) { distmin = dist; itmin1 = it; itmin2 = itnext; } } } } if(itmin1 != m_Vertices.end()) { itmin1->selected = true; itmin2->selected = true; return true; } else return false; } /* Can the polygon be closed? */ bool PolygonDrawingModel ::CanClosePolygon() { return m_Vertices.size() > 2; } /* Can last point be dropped? */ bool PolygonDrawingModel ::CanDropLastPoint() { return m_Vertices.size() > 0; } /* Can edges be split? */ bool PolygonDrawingModel ::CanInsertVertices() { return GetNumberOfSelectedSegments() > 0; } void PolygonDrawingModel::SetState(PolygonDrawingModel::PolygonState state) { if(m_State != state) { m_State = state; InvokeEvent(StateMachineChangeEvent()); } } bool PolygonDrawingModel::CheckState(PolygonDrawingUIState state) { switch(state) { case UIF_HAVE_VERTEX_SELECTION: return this->GetSelectedVertices(); case UIF_HAVE_EDGE_SELECTION: return this->CanInsertVertices(); case UIF_INACTIVE: return m_State == INACTIVE_STATE; case UIF_DRAWING: return m_State == DRAWING_STATE; case UIF_EDITING: return m_State == EDITING_STATE; case UIF_HAVEPOLYGON: return m_Vertices.size() > 0; case UIF_HAVECACHED: return m_CachedPolygon; } return false; } itksnap-3.4.0/GUI/Model/PolygonDrawingModel.h000066400000000000000000000116101263013355200207470ustar00rootroot00000000000000#ifndef POLYGONDRAWINGMODEL_H #define POLYGONDRAWINGMODEL_H #include "AbstractModel.h" #include "PropertyModel.h" #include #include namespace itk { template class Image; } class GenericSliceModel; struct PolygonVertex { float x, y; bool selected; bool control; PolygonVertex(float x_, float y_, bool on_, bool ctl_) : x(x_), y(y_), selected(on_), control(ctl_) {} PolygonVertex() : x(0.0f), y(0.0f), selected(false), control(true) {} float &operator[](unsigned int i) { return (i==0) ? x : y; } }; enum PolygonDrawingUIState { UIF_INACTIVE, UIF_DRAWING, UIF_EDITING, UIF_HAVEPOLYGON, UIF_HAVECACHED, UIF_HAVE_VERTEX_SELECTION, UIF_HAVE_EDGE_SELECTION }; class PolygonDrawingModel : public AbstractModel { public: irisITKObjectMacro(PolygonDrawingModel, AbstractModel) FIRES(StateMachineChangeEvent) irisSetMacro(Parent, GenericSliceModel *) irisGetMacro(Parent, GenericSliceModel *) /** Vertex structure */ typedef PolygonVertex Vertex; typedef std::list VertexList; typedef VertexList::iterator VertexIterator; typedef VertexList::reverse_iterator VertexRIterator; typedef vnl_vector_fixed BoxType; /** States that the polygon drawing is in */ enum PolygonState { INACTIVE_STATE = 0, DRAWING_STATE, EDITING_STATE }; /** Render polygon onto a target image */ void AcceptPolygon(std::vector &warnings); /** Copies cached polygon to current polygon */ void PastePolygon(void); /** Deletes selected vertices */ void Delete(); /** Inserts vertices in selected edges */ void Insert(); /** Clears the drawing */ void Reset(); /** In drawing mode, remove the last point drawn */ void DropLastPoint(); /** In drawing mode, close the polygon (same as RMB) */ void ClosePolygon(); /** Can the polygon be closed? */ bool CanClosePolygon(); /** Can last point be dropped? */ bool CanDropLastPoint(); /** Can edges be split? */ bool CanInsertVertices(); /** Get the current state of the polygon editor */ irisGetMacro(State, PolygonState) /** How many vertices are selected */ irisGetMacro(SelectedVertices,bool) /** How many vertices are selected */ irisGetMacro(CachedPolygon,bool) /** Set the accuracy of freehand curve fitting */ irisRangedPropertyAccessMacro(FreehandFittingRate, double) /** Access to the vertices */ irisGetMacro(Vertices, const VertexList &) irisSetMacro(Vertices, const VertexList &) /** Access to the vertices */ irisGetMacro(DragVertices, const VertexList &) irisSetMacro(DragVertices, const VertexList &) /** Current selection box */ irisGetMacro(SelectionBox, const BoxType &) /** Current edit box */ irisGetMacro(EditBox, const BoxType &) /** Are we dragging the selection box? */ irisIsMacro(DraggingPickBox) /** Is the cursor hovering over the starting voxel */ irisIsMacro(HoverOverFirstVertex) bool ProcessPushEvent(float x, float y, bool shift_state); bool ProcessDragEvent(float x, float y); bool ProcessMouseMoveEvent(float x, float y); bool ProcessReleaseEvent(float x, float y); /** * Return the size of a screen logical pixel (as opposed to a physical * pixel on the retina screen) in slice coordinate units */ Vector2f GetPixelSize(); bool CheckState(PolygonDrawingUIState state); protected: PolygonDrawingModel(); virtual ~PolygonDrawingModel(); GenericSliceModel *m_Parent; // Array of vertices, cached vertices from last operation VertexList m_Vertices, m_Cache; VertexList m_DragVertices; // State of the system PolygonState m_State; bool m_CachedPolygon; bool m_SelectedVertices; bool m_DraggingPickBox; // contains selected points BoxType m_EditBox; // box the user drags to select new points BoxType m_SelectionBox; float m_StartX, m_StartY; // Whether we are hovering over the starting vertex bool m_HoverOverFirstVertex; void ComputeEditBox(); void Add(float x, float y, int selected); void ProcessFreehandCurve(); bool CheckClickOnVertex(float x, float y, float pixel_x, float pixel_y, int k); bool CheckClickOnLineSegment(float x, float y, float pixel_x, float pixel_y, int k); // Check if the cursor (x,y) is near the first vertex (i.e., we should be // closing the polygon) bool CheckNearFirstVertex(float x, float y, float pixel_x, float pixel_y); int GetNumberOfSelectedSegments(); void SetState(PolygonState state); // Freehand fitting rate SmartPtr m_FreehandFittingRateModel; // Type definition for the slice used for polygon rendering typedef itk::Image PolygonSliceType; typedef SmartPtr PolygonSlicePointer; /** Slice used for polygon drawing and merging */ PolygonSlicePointer m_PolygonSlice; }; #endif // POLYGONDRAWINGMODEL_H itksnap-3.4.0/GUI/Model/PolygonSettingsModel.cxx000066400000000000000000000041711263013355200215330ustar00rootroot00000000000000#include "PolygonSettingsModel.h" #include "GlobalUIModel.h" #include "PolygonDrawingModel.h" #include "NumericPropertyToggleAdaptor.h" void PolygonSettingsModel::SetParentModel(GlobalUIModel *model) { m_ParentModel = model; m_FreehandSegmentLengthModel->RebroadcastFromSourceProperty( m_ParentModel->GetPolygonDrawingModel(0)->GetFreehandFittingRateModel()); m_FreehandIsPiecewiseModel->RebroadcastFromSourceProperty( m_ParentModel->GetPolygonDrawingModel(0)->GetFreehandFittingRateModel()); } PolygonSettingsModel::PolygonSettingsModel() { m_ParentModel = NULL; m_FreehandSegmentLengthModel = wrapGetterSetterPairAsProperty( this, &Self::GetFreehandSegmentLengthValueAndRange, &Self::SetFreehandSegmentLengthValue); m_FreehandIsPiecewiseModel = wrapGetterSetterPairAsProperty( this, &Self::GetFreehandIsPiecewiseValue, &Self::SetFreehandIsPiecewiseValue); } bool PolygonSettingsModel::GetFreehandIsPiecewiseValue(bool &value) { if(!m_ParentModel) return false; int rate = (int) m_ParentModel->GetPolygonDrawingModel(0)->GetFreehandFittingRate(); value = (rate != 0); return true; } void PolygonSettingsModel::SetFreehandIsPiecewiseValue(bool value) { int rate = (int) m_ParentModel->GetPolygonDrawingModel(0)->GetFreehandFittingRate(); if(value) { for(int i = 0; i < 3; i++) m_ParentModel->GetPolygonDrawingModel(i)->SetFreehandFittingRate(m_LastFreehandRate); } else { m_LastFreehandRate = rate; for(int i = 0; i < 3; i++) m_ParentModel->GetPolygonDrawingModel(i)->SetFreehandFittingRate(0.0); } } bool PolygonSettingsModel ::GetFreehandSegmentLengthValueAndRange(int &value, NumericValueRange *range) { if(!m_ParentModel) return false; value = (int) m_ParentModel->GetPolygonDrawingModel(0)->GetFreehandFittingRate(); if(value == 0) value = m_LastFreehandRate; if(range) range->Set(1, 20, 1); return true; } void PolygonSettingsModel::SetFreehandSegmentLengthValue(int value) { for(int i = 0; i < 3; i++) m_ParentModel->GetPolygonDrawingModel(i)->SetFreehandFittingRate(value); } itksnap-3.4.0/GUI/Model/PolygonSettingsModel.h000066400000000000000000000017521263013355200211620ustar00rootroot00000000000000#ifndef POLYGONSETTINGSMODEL_H #define POLYGONSETTINGSMODEL_H #include "PropertyModel.h" class GlobalUIModel; class PolygonSettingsModel : public AbstractModel { public: irisITKObjectMacro(PolygonSettingsModel, AbstractModel) irisGetMacro(ParentModel, GlobalUIModel *) void SetParentModel(GlobalUIModel *model); irisSimplePropertyAccessMacro(FreehandIsPiecewise, bool) irisRangedPropertyAccessMacro(FreehandSegmentLength, int) protected: PolygonSettingsModel(); virtual ~PolygonSettingsModel() {} GlobalUIModel *m_ParentModel; SmartPtr m_FreehandIsPiecewiseModel; bool GetFreehandIsPiecewiseValue(bool &value); void SetFreehandIsPiecewiseValue(bool value); SmartPtr m_FreehandSegmentLengthModel; bool GetFreehandSegmentLengthValueAndRange(int &value, NumericValueRange *range); void SetFreehandSegmentLengthValue(int value); private: int m_LastFreehandRate; }; #endif // POLYGONSETTINGSMODEL_H itksnap-3.4.0/GUI/Model/RandomAccessCollectionModel.cxx000066400000000000000000000000521263013355200227330ustar00rootroot00000000000000#include "RandomAccessCollectionModel.h" itksnap-3.4.0/GUI/Model/RandomAccessCollectionModel.h000066400000000000000000000133721263013355200223710ustar00rootroot00000000000000#ifndef RANDOMACCESSCOLLECTIONMODEL_H #define RANDOMACCESSCOLLECTIONMODEL_H #include #include #include template class StandardMapContainerTraits { public: typedef TKey KeyType; typedef TItem ItemType; typedef std::map ContainerType; typedef ContainerType * ContainerPointer; typedef typename ContainerType::iterator Iterator; static Iterator begin(ContainerType *p) { return p->begin(); } static Iterator end(ContainerType *p) { return p->end(); } static KeyType key(ContainerType *p, Iterator &it) { return it->first; } static TItem value(ContainerType *p, Iterator &it) { return it->second; } static Iterator find(ContainerType *p, KeyType key) { return p->find(key); } }; template class StandardVectorContainerTraits { public: typedef unsigned int KeyType; typedef TItem ItemType; typedef std::vector ContainerType; typedef ContainerType * ContainerPointer; typedef typename ContainerType::iterator Iterator; static Iterator begin(ContainerType *p) { return p->begin(); } static Iterator end(ContainerType *p) { return p->end(); } static KeyType key(ContainerType *p, Iterator &it) { return it - p->begin(); } static TItem value(ContainerType *p, Iterator &it) { return *it; } static Iterator find(ContainerType *p, KeyType key) { return p->begin() + key; } }; template class RandomAccessCollectionNullFilter { public: typedef typename TContainerTraits::KeyType KeyType; typedef typename TContainerTraits::ItemType ItemType; bool Check(KeyType &key, ItemType &item) { return true; } }; /** This class exposes an indexed collection of items (map/vector) as a random access list. This makes it possible to couple this collection with GUI widgets such as lists, combo boxes, etc. Internally, the model caches an array of indices into the source list. If the source collection changes, the model must be notified with an event so that the cache can be rebuilt. */ template > class RandomAccessCollectionModel : public AbstractRandomAccessCollectionModel { public: // They key/item types typedef typename TContainerTraits::KeyType KeyType; typedef typename TContainerTraits::ItemType ItemType; // ITK typedefs typedef RandomAccessCollectionModel Self; typedef AbstractRandomAccessCollectionModel Superclass; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkTypeMacro(RandomAccessCollectionModel, AbstractRandomAccessCollectionModel) itkNewMacro(Self) // The type of the source array typedef typename TContainerTraits::ContainerPointer ContainerPointer; RandomAccessCollectionModel() { m_Filter = NULL; m_SourceContainer = NULL; } void SetSourceContainer(ContainerPointer p) { if(p != m_SourceContainer) { m_SourceContainer = p; m_ArrayDirty = true; } } void SetFilter(TFilter *filter) { if(filter != m_Filter) { m_Filter = filter; m_ArrayDirty = true; } } // Number of items in the collection unsigned int GetSize() { // Respond to accumulated events this->Update(); // If the cache is dirty, rebuild it if(m_ArrayDirty) RebuildArray(); // Return the cached array size return m_Array.size(); } // Access to the elements ItemType operator[](unsigned int n) { // Respond to accumulated events this->Update(); // If the cache is dirty, rebuild it if(m_ArrayDirty) RebuildArray(); // Get the item typename TContainerTraits::Iterator it = TContainerTraits::find(m_SourceContainer, m_Array[n]); if(it == TContainerTraits::end(m_SourceContainer)) throw IRISException("Access violation in RandomAccessCollectionModel"); return TContainerTraits::value(m_SourceContainer, it); } protected: // Rebuild the array (in response to an event( void RebuildArray() { m_Array.clear(); typename TContainerTraits::Iterator it; for(it = TContainerTraits::begin(m_SourceContainer); it != TContainerTraits::end(m_SourceContainer); ++it) { // What is the key for this iterator? KeyType key = TContainerTraits::key(m_SourceContainer, it); // Apply filter! if(m_Filter) { ItemType item = TContainerTraits::value(m_SourceContainer, it); if(m_Filter->Check(key, item)) m_Array.push_back(key); } else m_Array.push_back(key); } m_ArrayDirty = false; } virtual void OnUpdate() { // If any event has been received, dirty the cache m_ArrayDirty = true; } // Pointer to the source array ContainerPointer m_SourceContainer; // Filter, if present TFilter *m_Filter; // Cached array of indices into the source array std::vector m_Array; // Whether the array is dirty, needs to be rebuilt bool m_ArrayDirty; }; /** Factory object to create a model wrapping around an existing STL array */ template SmartPtr > newRandomAccessContainerModel(std::vector &vec) { typedef AbstractRandomAccessCollectionModel AbstractModelType; typedef StandardVectorContainerTraits TraitsType; typedef RandomAccessCollectionModel ModelType; typename ModelType::Pointer model = ModelType::New(); model->SetSourceContainer(&vec); SmartPtr retptr(model); return retptr; } #endif // RANDOMACCESSCOLLECTIONMODEL_H itksnap-3.4.0/GUI/Model/RegistryEntryPropertyModel.h000066400000000000000000000034321263013355200224060ustar00rootroot00000000000000#ifndef REGISTRYENTRYPROPERTYMODEL_H #define REGISTRYENTRYPROPERTYMODEL_H #include "PropertyModel.h" #include "Registry.h" /** * A model encapsulating a registry entry. The registry mechanism does not * support metadata, so the registry entry can only store the data itself. * The domain is supplied by the user. */ template class RegistryEntryPropertyModel : public AbstractModel { public: // Standard ITK stuff (can't use irisITKObjectMacro because of two template // parameters, comma breaks the macro). typedef RegistryEntryPropertyModel Self; typedef AbstractPropertyModel Superclass; typedef SmartPtr Pointer; typedef SmartPtr ConstPointer; itkTypeMacro(RegistryEntryPropertyModel, AbstractPropertyModel) itkNewMacro(Self) /** * Initialize the model with a pointer to the registry (which must remain * valid), the key, and a default value for the property */ void Initialize(Registry *registry, const char *key, TVal deflt) { m_Registry = registry; m_Key = key; m_DefaultValue = deflt; // Listen to events from the corresponding folder (how) } irisSetWithEventMacro(Domain, TDomain, DomainChangedEvent) virtual bool GetValueAndDomain(TVal &value, TDomain *domain) { if(!m_Registry) return false; value = (*m_Registry)[m_Key][m_DefaultValue]; if(domain) *domain = m_Domain; return true; } virtual void SetValue(TVal value) { assert(m_Registry); (*m_Registry)[m_Key] << value; } protected: // The registry Registry *m_Registry; // Key const char *m_Key; // Default value TVal m_DefaultValue; // The domain is stored in the model TDomain m_Domain; }; #endif // REGISTRYENTRYPROPERTYMODEL_H itksnap-3.4.0/GUI/Model/ReorientImageModel.cxx000066400000000000000000000227111263013355200211150ustar00rootroot00000000000000#include "ReorientImageModel.h" #include "GlobalUIModel.h" #include "IRISException.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "GenericSliceModel.h" ReorientImageModel::ReorientImageModel() { // Initialized cached values m_CurrentRAIValue = ""; m_CurrentOrientationIsOblique = false; // Create the sub-models m_NewRAICodeModel = ConcreteSimpleStringProperty::New(); m_NewRAICodeModel->SetValue(""); m_CurrentRAICodeModel = wrapGetterSetterPairAsProperty( this, &Self::GetCurrentRAICodeValue); m_InvalidStatusModel = wrapGetterSetterPairAsProperty( this, &Self::GetInvalidStatusValue); // Invalid status model listens to changes to the new RAI model m_InvalidStatusModel->Rebroadcast(m_NewRAICodeModel, ValueChangedEvent(), ValueChangedEvent()); // Changes to the new RAI model are rebroadcast as state change events // for the widget activation system Rebroadcast(m_NewRAICodeModel, ValueChangedEvent(), StateMachineChangeEvent()); // Create the axis direction models for(int axis = 0; axis < 3; axis++) { m_NewAxisDirectionModel[axis] = wrapIndexedGetterSetterPairAsProperty( this, axis, &Self::GetNthNewAxisDirectionValueAndDomain, &Self::SetNthNewAxisDirectionValue); m_NewAxisDirectionModel[axis]->Rebroadcast( m_NewRAICodeModel, ValueChangedEvent(), ValueChangedEvent()); m_NewAxisDirectionModel[axis]->Rebroadcast( m_NewRAICodeModel, ValueChangedEvent(), DomainChangedEvent()); m_CurrentAxisDirectionModel[axis] = wrapIndexedGetterSetterPairAsProperty( this, axis, &Self::GetNthCurrentAxisDirectionValue); } // The current NIFTI matrix m_CurrentWorldMatrixModel = ConcreteMatrixProperty::New(); // The current direction matrix m_CurrentDirectionMatrixModel = ConcreteMatrixProperty::New(); // The new NIFTI matrix m_NewWorldMatrixModel = wrapGetterSetterPairAsProperty( this, &Self::GetNewWorldMatrixValue); m_NewWorldMatrixModel->Rebroadcast( m_NewRAICodeModel, ValueChangedEvent(), ValueChangedEvent()); // The new NIFTI matrix m_NewDirectionMatrixModel = wrapGetterSetterPairAsProperty( this, &Self::GetNewDirectionMatrixValue); m_NewDirectionMatrixModel->Rebroadcast( m_NewRAICodeModel, ValueChangedEvent(), ValueChangedEvent()); } /** Initialize with the parent model */ void ReorientImageModel::SetParentModel(GlobalUIModel *parent) { // Store the model m_Parent = parent; // Listen to changes to the main image Rebroadcast(m_Parent->GetDriver(), MainImageDimensionsChangeEvent(), ModelUpdateEvent()); // Listen to changes to the main image Rebroadcast(m_Parent->GetDriver(), MainImagePoseChangeEvent(), ModelUpdateEvent()); } AbstractSimpleStringProperty *ReorientImageModel::GetCurrentAxisDirectionModel(int axis) const { return m_CurrentAxisDirectionModel[axis]; } ReorientImageModel::AbstractAxisDirectionProperty *ReorientImageModel::GetNewAxisDirectionModel(int axis) const { return m_NewAxisDirectionModel[axis]; } void ReorientImageModel::ApplyCurrentRAI() { IRISApplication *driver = m_Parent->GetDriver(); // Check that the current RAI is valid std::string rai = this->m_NewRAICodeModel->GetValue(); assert(ImageCoordinateGeometry::IsRAICodeValid(rai.c_str())); // Convert the rai code to a direction matrix ImageCoordinateGeometry::DirectionMatrix dm = ImageCoordinateGeometry::ConvertRAICodeToDirectionMatrix(rai); // Set the direction in the image driver->ReorientImage(dm); // Tell the display slices to reinitialize for(int i = 0; i < 3; i++) { m_Parent->GetSliceModel(i)->InitializeSlice(driver->GetCurrentImageData()); } } void ReorientImageModel::ReverseAxisDirection(int axis) { AxisDirection dir; bool valid = m_NewAxisDirectionModel[axis]->GetValueAndDomain(dir, NULL); if(valid) { AxisDirection reverse = static_cast(- static_cast(dir)); m_NewAxisDirectionModel[axis]->SetValue(reverse); } } bool ReorientImageModel::CheckState(ReorientImageModel::UIState state) { AxisDirection dummy; switch(state) { case UIF_IMAGE_LOADED: return m_Parent->GetDriver()->IsMainImageLoaded(); case UIF_VALID_NEW_RAI: { std::string rai = this->m_NewRAICodeModel->GetValue(); return ImageCoordinateGeometry::IsRAICodeValid(rai.c_str()); } case UIF_VALID_AXIS_DIRECTION_X: return GetNthNewAxisDirectionValueAndDomain(0, dummy, NULL); case UIF_VALID_AXIS_DIRECTION_Y: return GetNthNewAxisDirectionValueAndDomain(1, dummy, NULL); case UIF_VALID_AXIS_DIRECTION_Z: return GetNthNewAxisDirectionValueAndDomain(2, dummy, NULL); default: return false; } } bool ReorientImageModel::GetCurrentRAICodeValue(std::string &value) { if(m_CurrentRAIValue.size()) { value = m_CurrentOrientationIsOblique ? std::string("Oblique (closest to ") + m_CurrentRAIValue + std::string(")") : m_CurrentRAIValue; return true; } else return false; } bool ReorientImageModel ::GetNthCurrentAxisDirectionValue(int axis, std::string &value) { // Get the current rai code value if(m_CurrentRAIValue.size()) { char letter = m_CurrentRAIValue[axis]; AxisDirection dir = ImageCoordinateGeometry::ConvertRAILetterToAxisDirection(letter); std::string dirstr = ImageCoordinateGeometry::GetAxisDirectionDescriptionMap()[dir]; value = m_CurrentOrientationIsOblique ? std::string("Oblique (appr. ") + dirstr + std::string(")") : dirstr; return true; } else return false; } bool ReorientImageModel::GetInvalidStatusValue(std::string &value) { // Check that the current RAI is valid std::string rai = this->m_NewRAICodeModel->GetValue(); if(ImageCoordinateGeometry::IsRAICodeValid(rai.c_str())) value = ""; else value = "Invalid RAI code"; return true; } bool ReorientImageModel ::GetNthNewAxisDirectionValueAndDomain( int axis, AxisDirection &value, AxisDirectionDomain *domain) { // Check that the current RAI is 'sort of' valid std::string rai = this->m_NewRAICodeModel->GetValue(); // Make sure that the rai is long enough for us to process if(rai.size() <= axis) return false; // Make sure that the letter in the RAI is a valid letter value = ImageCoordinateGeometry::ConvertRAILetterToAxisDirection(rai[axis]); // Is this a valid direction? if(value == ImageCoordinateGeometry::INVALID_DIRECTION) return false; // Now deal with the domain if(domain) { domain->SetWrappedMap( &ImageCoordinateGeometry::GetAxisDirectionDescriptionMap()); } return true; } void ReorientImageModel ::SetNthNewAxisDirectionValue(int axis, AxisDirection value) { // Get the letter for the direction char letter = ImageCoordinateGeometry::ConvertAxisDirectionToRAILetter(value); // Make sure the RAI code is long enough for the letter to fit std::string rai = this->m_NewRAICodeModel->GetValue(); while(rai.size() <= axis) rai.append(" "); // Set the letter in the RAI code rai[axis] = letter; // Set the rai string in the model m_NewRAICodeModel->SetValue(rai); } bool ReorientImageModel ::GetNewWorldMatrixValue(ReorientImageModel::WorldMatrix &value) { // Get the RAI std::string rai = this->m_NewRAICodeModel->GetValue(); if(!ImageCoordinateGeometry::IsRAICodeValid(rai.c_str())) return false; // Get the direction matrix ImageCoordinateGeometry::DirectionMatrix dm = ImageCoordinateGeometry::ConvertRAICodeToDirectionMatrix(rai); ImageWrapperBase *im = m_Parent->GetDriver()->GetCurrentImageData()->GetMain(); value = ImageWrapperBase::ConstructNiftiSform( dm, im->GetImageBase()->GetOrigin().GetVnlVector(), im->GetImageBase()->GetSpacing().GetVnlVector()); return true; } bool ReorientImageModel ::GetNewDirectionMatrixValue(ReorientImageModel::WorldMatrix &value) { // Get the RAI std::string rai = this->m_NewRAICodeModel->GetValue(); if(!ImageCoordinateGeometry::IsRAICodeValid(rai.c_str())) return false; // Get the direction matrix value = ImageCoordinateGeometry::ConvertRAICodeToDirectionMatrix(rai); return true; } void ReorientImageModel::OnUpdate() { if(this->m_EventBucket->HasEvent(MainImageDimensionsChangeEvent()) || this->m_EventBucket->HasEvent(MainImagePoseChangeEvent())) { // Obtain the current RAI value IRISApplication *app = m_Parent->GetDriver(); if(app->IsMainImageLoaded()) { // Get teh RAI value m_CurrentRAIValue = app->GetImageToAnatomyRAI(); // Get the obliqueness m_CurrentOrientationIsOblique = app->IsImageOrientationOblique(); // Get the direction matrix ImageWrapperBase *mainImage = app->GetCurrentImageData()->GetMain(); WorldMatrix dm = mainImage->GetImageBase()->GetDirection().GetVnlMatrix(); m_CurrentDirectionMatrixModel->SetIsValid(true); m_CurrentDirectionMatrixModel->SetValue(dm); // Get the NIFTI matrix WorldMatrix mw = mainImage->GetNiftiSform(); m_CurrentWorldMatrixModel->SetIsValid(true); m_CurrentWorldMatrixModel->SetValue(mw); } else { m_CurrentRAIValue = ""; m_CurrentWorldMatrixModel->SetIsValid(false); m_CurrentDirectionMatrixModel->SetIsValid(false); } // Set it as the new RAI value m_NewRAICodeModel->SetValue(m_CurrentRAIValue); } } itksnap-3.4.0/GUI/Model/ReorientImageModel.h000066400000000000000000000107711263013355200205450ustar00rootroot00000000000000#ifndef REORIENTIMAGEMODEL_H #define REORIENTIMAGEMODEL_H #include #include #include class GlobalUIModel; /** * @brief The ReorientImageModel class * Model for the reorient dialog */ class ReorientImageModel : public AbstractModel { public: // A representation for an axis-direction pair typedef ImageCoordinateGeometry::AxisDirection AxisDirection; typedef STLMapWrapperItemSetDomain AxisDirectionDomain; typedef AbstractPropertyModel AbstractAxisDirectionProperty; // A model encapsulating the 4x4 NIFTI matrix typedef vnl_matrix WorldMatrix; typedef AbstractPropertyModel AbstractMatrixProperty; typedef ConcretePropertyModel ConcreteMatrixProperty; // Standard ITK stuff irisITKObjectMacro(ReorientImageModel, AbstractModel) /** Initialize with the parent model */ void SetParentModel(GlobalUIModel *parent); /** A model for the current RAI field */ irisGetMacro(NewRAICodeModel, AbstractSimpleStringProperty *) /** A model for the current RAI field */ irisGetMacro(CurrentRAICodeModel, AbstractSimpleStringProperty *) /** A model for the invalid status */ irisGetMacro(InvalidStatusModel, AbstractSimpleStringProperty *) /** Models for the current direction of the three coordinate axes */ AbstractSimpleStringProperty *GetCurrentAxisDirectionModel(int axis) const; /** Models for the new direction of the three coordinate axes */ AbstractAxisDirectionProperty *GetNewAxisDirectionModel(int axis) const; /** Model for the current NIFTI matrix */ irisGetMacro(CurrentWorldMatrixModel, AbstractMatrixProperty *) /** Model for the new NIFTI matrix */ irisGetMacro(NewWorldMatrixModel, AbstractMatrixProperty *) /** Model for the current ITK Direction matrix */ irisGetMacro(CurrentDirectionMatrixModel, AbstractMatrixProperty *) /** Model for the new ITK Direction matrix */ irisGetMacro(NewDirectionMatrixModel, AbstractMatrixProperty *) /** Apply current RAI code to the image */ void ApplyCurrentRAI(); /** Reverse the direction of one of the axes */ void ReverseAxisDirection(int axis); /** States in which the model can be, which allow the activation and deactivation of various widgets in the interface */ enum UIState { UIF_IMAGE_LOADED, UIF_VALID_NEW_RAI, UIF_VALID_AXIS_DIRECTION_X, UIF_VALID_AXIS_DIRECTION_Y, UIF_VALID_AXIS_DIRECTION_Z }; /** Check the state flags above */ bool CheckState(UIState state); protected: // Constructor ReorientImageModel(); virtual ~ReorientImageModel() {} // Model for the new RAI code SmartPtr m_NewRAICodeModel; // Model for the current RAI code SmartPtr m_CurrentRAICodeModel; bool GetCurrentRAICodeValue(std::string &value); // Model for the error message SmartPtr m_InvalidStatusModel; bool GetInvalidStatusValue(std::string &value); // Models for the three current axis directions SmartPtr m_CurrentAxisDirectionModel[3]; bool GetNthCurrentAxisDirectionValue(int axis, std::string &value); // Models for the three new axis directions SmartPtr m_NewAxisDirectionModel[3]; bool GetNthNewAxisDirectionValueAndDomain( int axis, AxisDirection &value, AxisDirectionDomain *domain); void SetNthNewAxisDirectionValue(int axis, AxisDirection value); // Current world matrix SmartPtr m_CurrentWorldMatrixModel; // Current direction matrix SmartPtr m_CurrentDirectionMatrixModel; // New world matrix SmartPtr m_NewWorldMatrixModel; bool GetNewWorldMatrixValue(WorldMatrix &value); // New direction matrix SmartPtr m_NewDirectionMatrixModel; bool GetNewDirectionMatrixValue(WorldMatrix &value); // Respond to events received by the model virtual void OnUpdate(); // The parent model GlobalUIModel *m_Parent; // The current RAI of the loaded image. This is updated in the OnUpdate // method, and is equal to the GetImageToAnatomyRAI in IRISApplication. // We keep a copy of this value to avoid unnecessary computation of the RAI // in response to widget events. std::string m_CurrentRAIValue; // Whether the current rai value is oblique bool m_CurrentOrientationIsOblique; }; #endif // REORIENTIMAGEMODEL_H itksnap-3.4.0/GUI/Model/SNAPUIFlag.h000066400000000000000000000033241263013355200166170ustar00rootroot00000000000000#ifndef SNAPUIFLAG_H #define SNAPUIFLAG_H #include "StateManagement.h" #include "UIState.h" #include "SNAPEventListenerCallbacks.h" class GlobalUIModel; /** A BooleanCondition implementation that queries a TModel about different UI states of type TStateEnum. The Model must implement a function bool CheckState(TStateEnum state), which is called to check the state. The model must also fire an event StateMachineChangeEvent() in order for the activation machinery to work. The implementation of this class is in a .txx file to allow use with not yet known Models and Enums. */ template class SNAPUIFlag : public BooleanCondition { public: typedef SNAPUIFlag Self; typedef BooleanCondition Superclass; itkTypeMacro(Self, Superclass) static SmartPtr New(TModel *model, TStateEnum state) { SmartPtr p = new Self(model, state); p->UnRegister(); return p; } bool operator() () const { if(m_Model) return m_Model->CheckState(m_State); else return false; } protected: SNAPUIFlag(TModel *model, TStateEnum state) { m_Model = model; m_State = state; m_Tag = AddListener( m_Model, StateMachineChangeEvent(), this, &Self::OnStateChange); m_DeleteTag = AddListener( m_Model, itk::DeleteEvent(), this, &Self::OnModelDeletion); } virtual ~SNAPUIFlag() { if(m_Model) { m_Model->RemoveObserver(m_Tag); m_Model->RemoveObserver(m_DeleteTag); } } virtual void OnModelDeletion() { m_Model = NULL; } private: TModel *m_Model; TStateEnum m_State; unsigned long m_Tag, m_DeleteTag; }; #endif // SNAPUIFLAG_H itksnap-3.4.0/GUI/Model/SNAPUIFlag.txx000066400000000000000000000000001263013355200171770ustar00rootroot00000000000000itksnap-3.4.0/GUI/Model/SaveModifiedLayersModel.cxx000066400000000000000000000176171263013355200221130ustar00rootroot00000000000000#include "SaveModifiedLayersModel.h" #include "ImageWrapperBase.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "IRISImageData.h" #include "ImageIODelegates.h" bool AbstractSaveableItem::IsSaveable() { for(DependencyList::iterator it = m_Dependencies.begin(); it != m_Dependencies.end(); ++it) if((*it)->NeedsDecision()) return false; return true; } void AbstractSaveableItem::AddDependency(AbstractSaveableItem *dep) { m_Dependencies.push_back(dep); } void ImageLayerSaveableItem ::Initialize(SaveModifiedLayersModel *model, ImageWrapperBase *wrapper, LayerRole role) { m_Model = model; m_Wrapper = wrapper; m_Role = role; } std::string ImageLayerSaveableItem::GetDescription() const { if(m_Wrapper->GetCustomNickname().length()) return m_Wrapper->GetCustomNickname(); else return m_Wrapper->GetDefaultNickname(); } std::string ImageLayerSaveableItem::GetFilename() const { return m_Wrapper->GetFileName(); } bool ImageLayerSaveableItem::IsSaved() { return !m_Wrapper->HasUnsavedChanges(); } bool ImageLayerSaveableItem::Save(AbstractSaveModifiedLayersInteractionDelegate *cb_delegate) { // Create a delegate for saving this image return cb_delegate->SaveImageLayer(m_Model->GetParentModel(), m_Wrapper, m_Role); } bool ImageLayerSaveableItem::RequiresInteraction() { return GetFilename().size() == 0; } void WorkspaceSaveableItem::Initialize(SaveModifiedLayersModel *model) { m_Model = model; m_Saved = false; } std::string WorkspaceSaveableItem::GetDescription() const { return std::string("Workspace"); } std::string WorkspaceSaveableItem::GetFilename() const { return m_Model->GetParentModel()->GetGlobalState()->GetProjectFilename(); } bool WorkspaceSaveableItem::IsSaved() { return m_Saved; } bool WorkspaceSaveableItem::Save(AbstractSaveModifiedLayersInteractionDelegate *cb_delegate) { m_Saved = cb_delegate->SaveProject(m_Model->GetParentModel()); return m_Saved; } bool WorkspaceSaveableItem::RequiresInteraction() { return this->GetFilename().size() == 0; } SaveModifiedLayersModel::SaveModifiedLayersModel() { m_CurrentItemModel = wrapGetterSetterPairAsProperty( this, &Self::GetCurrentItemValue, &Self::SetCurrentItemValue); m_CurrentItem = 0; } bool SaveModifiedLayersModel::GetCurrentItemValue(int &out_value) { out_value = m_CurrentItem; return true; } void SaveModifiedLayersModel::SetCurrentItemValue(int value) { m_CurrentItem = value; // Do something? InvokeEvent(StateMachineChangeEvent()); } void SaveModifiedLayersModel::BuildUnsavedItemsList(std::list layers) { // Clear the list m_UnsavedItems.clear(); // Get the list of all the layers that are applicable. Notice that we only // consider the IRIS image data, we don't support saving of layers from // SNAP mode. This might change in the future. GenericImageData *gid = m_ParentModel->GetDriver()->GetIRISImageData(); // Are there any layers without filenames bool flag_unnamed_layers = false; // Is the main image layer included (i.e., the workspace is being closed) bool flag_main_included = false; // For each layer, check if it needs saving // These are the types of layers that we consider "worth saving". // TODO: this should be a parameter of the class, probably for(LayerIterator lit = gid->GetLayers(MAIN_ROLE | LABEL_ROLE | OVERLAY_ROLE); !lit.IsAtEnd(); ++lit) { ImageWrapperBase *w = lit.GetLayer(); if(layers.size() == 0 || std::find(layers.begin(), layers.end(), w) != layers.end()) { // Is the main layer being included in what's discarded? if(w == gid->GetMain()) flag_main_included = true; if(w->HasUnsavedChanges()) { // Add a new item SmartPtr item = ImageLayerSaveableItem::New(); item->Initialize(this, w, lit.GetRole()); item->SetId(m_UnsavedItems.size()); m_UnsavedItems.push_back(item.GetPointer()); if(item->RequiresInteraction()) flag_unnamed_layers = true; } } } // The workspace should only be included in the list if the main image is being // unloaded. Otherwise, any changes (loading/unloading) should be considered as // part of editing the workspace, and thus we should not be prompting to save the // workspace. Also, the workspace must already exist. if(flag_main_included && m_ParentModel->GetGlobalState()->GetProjectFilename().size()) { // Additional conditions are that either the project has unsaved changes, or one of // the items proposed for saving does not have a filename yet (and so saving it would // cause a modification to the workspace) if(flag_unnamed_layers | m_ParentModel->GetDriver()->IsProjectUnsaved()) { SmartPtr item = WorkspaceSaveableItem::New(); item->Initialize(this); item->SetId(m_UnsavedItems.size()); // Add all of the other unsaved items as dependencies for(int i = 0; i < m_UnsavedItems.size(); i++) item->AddDependency(m_UnsavedItems[i]); m_UnsavedItems.push_back(item.GetPointer()); } } // Adjust the current item m_CurrentItem = 0; } void SaveModifiedLayersModel::Initialize(GlobalUIModel *parent, std::list layers) { m_ParentModel = parent; // The model is not listening to changes in the layers because the dialog // it supports is meant to be modal, i.e. the user can't make changes. // Update the list of unsaved items BuildUnsavedItemsList(layers); } bool SaveModifiedLayersModel::CheckState(SaveModifiedLayersModel::UIState state) { int i; switch(state) { case SaveModifiedLayersModel::UIF_CAN_SAVE_ALL: for(i = 0; i < m_UnsavedItems.size(); i++) if(m_UnsavedItems[i]->NeedsDecision() && m_UnsavedItems[i]->RequiresInteraction()) return false; return true; case SaveModifiedLayersModel::UIF_CAN_SAVE_CURRENT: return (m_CurrentItem >= 0 && m_CurrentItem < m_UnsavedItems.size() && m_UnsavedItems[m_CurrentItem]->IsSaveable()); case SaveModifiedLayersModel::UIF_CAN_DISCARD_CURRENT: return true; case SaveModifiedLayersModel::UIF_CAN_QUICKSAVE_CURRENT: break; } return false; } void SaveModifiedLayersModel::UpdateCurrentItem() { for(int delta = 1; delta < m_UnsavedItems.size(); delta++) { int testItem = (m_CurrentItem + delta) % m_UnsavedItems.size(); if(m_UnsavedItems[testItem]->NeedsDecision() && m_UnsavedItems[testItem]->IsSaveable()) { SetCurrentItem(testItem); return; } } } void SaveModifiedLayersModel::SaveCurrent() { assert(m_CurrentItem >= 0 && m_CurrentItem < m_UnsavedItems.size()); // Try saving if(m_UnsavedItems[m_CurrentItem]->Save(m_UIDelegate)) { // Change the current item to the next unsaved item this->UpdateCurrentItem(); // Fire an update event this->InvokeEvent(ModelUpdateEvent()); } } void SaveModifiedLayersModel::DiscardCurrent() { assert(m_CurrentItem >= 0 && m_CurrentItem < m_UnsavedItems.size()); // Discarding does not cause any action. It just means that the item should // no longer appear on the 'to save' list. m_UnsavedItems[m_CurrentItem]->SetDiscarded(true); // Change the current item to the next unsaved item this->UpdateCurrentItem(); // Fire an update event this->InvokeEvent(ModelUpdateEvent()); } void SaveModifiedLayersModel::SaveAll() { // For all items that need decision, save them, respecting dependencies int n_Unsaved = m_UnsavedItems.size(); while(n_Unsaved > 0) { n_Unsaved = 0; for(int i = 0; i < m_UnsavedItems.size(); i++) { if(m_UnsavedItems[i]->NeedsDecision()) { if(m_UnsavedItems[i]->IsSaveable()) m_UnsavedItems[i]->Save(m_UIDelegate); else n_Unsaved++; } } } // Fire an update event this->InvokeEvent(ModelUpdateEvent()); } itksnap-3.4.0/GUI/Model/SaveModifiedLayersModel.h000066400000000000000000000126231263013355200215300ustar00rootroot00000000000000#ifndef SAVEMODIFIEDLAYERSMODEL_H #define SAVEMODIFIEDLAYERSMODEL_H #include "PropertyModel.h" class ImageWrapperBase; class GlobalUIModel; class ImageIOWizardModel; class SaveModifiedLayersModel; /** * In this dialog, the interaction between the model and the GUI class is a * bit complicated. Because some of the files that need to be saved may be * without a filename, there is the need to prompt the user for the filename * during the save operation. The easiest way to do so is via a callback. So * the GUI has to provide a pointer to a child of this abstract delegate class */ class AbstractSaveModifiedLayersInteractionDelegate { public: virtual bool SaveImageLayer( GlobalUIModel *model, ImageWrapperBase *wrapper, LayerRole role) = 0; virtual bool SaveProject(GlobalUIModel *model) = 0; }; /** * An abstract item that we might want to save. We inherit from AbstractModel * in order to use the smart pointer support, etc. */ class AbstractSaveableItem : public AbstractModel { public: irisITKAbstractObjectMacro(AbstractSaveableItem, AbstractModel) virtual std::string GetDescription() const = 0; virtual std::string GetFilename() const = 0; irisGetSetMacro(Id, int) /** Mark item as discarded */ irisGetSetMacro(Discarded, bool) /** Save the item (pass in the delegate to allow call-back to GUI */ virtual bool Save(AbstractSaveModifiedLayersInteractionDelegate *delegate) = 0; /** Whether this item requires interaction to be saved */ virtual bool RequiresInteraction() = 0; /** Whether this item needs decision from the user */ bool NeedsDecision() { return !(m_Discarded || IsSaved()); } /** Whether this item can be saved. This is true if all dependencies have * been saved or discarded */ virtual bool IsSaveable(); // Add a dependency (the item is not saveable until all dependencies have // been saved). Be careful not to create circular dependencies! void AddDependency(AbstractSaveableItem *dep); protected: AbstractSaveableItem() : m_Id(-1), m_Discarded(false) {} /** Whether the user needs to decide about this item */ virtual bool IsSaved() = 0; int m_Id; bool m_Discarded; typedef std::list DependencyList; DependencyList m_Dependencies; }; /** * A saveable item representing an image layer */ class ImageLayerSaveableItem : public AbstractSaveableItem { public: irisITKObjectMacro(ImageLayerSaveableItem, AbstractSaveableItem) void Initialize(SaveModifiedLayersModel *model, ImageWrapperBase *wrapper, LayerRole role); virtual std::string GetDescription() const; virtual std::string GetFilename() const; virtual bool IsSaved(); virtual bool Save(AbstractSaveModifiedLayersInteractionDelegate *cb_delegate); /** Whether this item requires interaction to be saved */ virtual bool RequiresInteraction(); protected: // The image wrapper ImageWrapperBase *m_Wrapper; LayerRole m_Role; SaveModifiedLayersModel *m_Model; }; /** * A saveable item representing a workspace state (aka project) */ class WorkspaceSaveableItem : public AbstractSaveableItem { public: irisITKObjectMacro(WorkspaceSaveableItem, AbstractSaveableItem) void Initialize(SaveModifiedLayersModel *model); virtual std::string GetDescription() const; virtual std::string GetFilename() const; virtual bool IsSaved(); virtual bool Save(AbstractSaveModifiedLayersInteractionDelegate *cb_delegate); /** Whether this item requires interaction to be saved */ virtual bool RequiresInteraction(); protected: // The image wrapper SaveModifiedLayersModel *m_Model; // Saved state - based on the return code from save method bool m_Saved; }; /** * This model supports saving or discarding of image layers. It is used when * unloading images and segmentations, quitting, etc. */ class SaveModifiedLayersModel : public AbstractModel { public: irisITKObjectMacro(SaveModifiedLayersModel, AbstractModel) void Initialize(GlobalUIModel *parent, std::list layers); irisGetMacro(ParentModel, GlobalUIModel *) irisGetSetMacro(UIDelegate, AbstractSaveModifiedLayersInteractionDelegate *) enum UIState { UIF_CAN_SAVE_ALL = 0, UIF_CAN_SAVE_CURRENT, UIF_CAN_DISCARD_CURRENT, UIF_CAN_QUICKSAVE_CURRENT }; bool CheckState(UIState state); irisSimplePropertyAccessMacro(CurrentItem, int) // Unsaved item definition typedef SmartPtr SaveableItemPtr; typedef std::vector SaveableItemPtrArray; // Get the unsaved items irisGetMacro(UnsavedItems, const SaveableItemPtrArray &) // Save the current item (interactively or non-interactively) void SaveCurrent(); // Discard the current item virtual void DiscardCurrent(); // Save all items that need saving virtual void SaveAll(); protected: SaveModifiedLayersModel(); // Parent model GlobalUIModel *m_ParentModel; // Delegate that allows callbacks to the GUI AbstractSaveModifiedLayersInteractionDelegate *m_UIDelegate; // List of unsaved items SaveableItemPtrArray m_UnsavedItems; // Currently selected unsaved item int m_CurrentItem; SmartPtr m_CurrentItemModel; bool GetCurrentItemValue(int &out_value); void SetCurrentItemValue(int value); // Update the list of unsaved items void BuildUnsavedItemsList(std::list layers); // Update the current imate void UpdateCurrentItem(); }; #endif // SAVEMODIFIEDLAYERSMODEL_H itksnap-3.4.0/GUI/Model/SliceWindowCoordinator.cxx000066400000000000000000000277311263013355200220440ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SliceWindowCoordinator.cxx,v $ Language: C++ Date: $Date: 2010/10/12 17:57:11 $ Version: $Revision: 1.5 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #include "SliceWindowCoordinator.h" #include "GlobalState.h" #include "IRISException.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "GlobalUIModel.h" #include "DisplayLayoutModel.h" #include SliceWindowCoordinator ::SliceWindowCoordinator() { // Set up the zoom model m_CommonZoomFactorModel = wrapGetterSetterPairAsProperty( this, &Self::GetCommonZoomValueAndRange, &Self::SetCommonZoomValue, ZoomLevelUpdateEvent(), ZoomLevelUpdateEvent()); m_LinkedZoomModel = wrapGetterSetterPairAsProperty( this, &Self::GetLinkedZoomValue, &Self::SetLinkedZoomValue, ZoomLevelUpdateEvent(), ZoomLevelUpdateEvent()); // Initialize to defaults m_SliceModel[0] = m_SliceModel[1] = m_SliceModel[2] = NULL; m_LinkedZoom = false; m_WindowsRegistered = false; m_ParentModel = NULL; } SliceWindowCoordinator ::~SliceWindowCoordinator() { } void SliceWindowCoordinator ::SetParentModel(GlobalUIModel *model) { m_ParentModel = model; for(unsigned int i=0;i<3;i++) { m_SliceModel[i] = m_ParentModel->GetSliceModel(i); m_SliceModel[i]->SetManagedZoom(m_LinkedZoom); // Listen to updates to the viewport size. These require zoom factors to be // recomputed Rebroadcast(m_SliceModel[i], GenericSliceModel::ViewportResizeEvent(), ModelUpdateEvent()); } m_WindowsRegistered = true; // Listen to image dimension change events Rebroadcast(m_ParentModel->GetDriver(), MainImageDimensionsChangeEvent(), ModelUpdateEvent()); // Listen to changes in the layout of the slice view into cells. When // this change occurs, we have to modify the size of the slice views DisplayLayoutModel *dlm = m_ParentModel->GetDisplayLayoutModel(); Rebroadcast(dlm, DisplayLayoutModel::LayerLayoutChangeEvent(), ModelUpdateEvent()); } void SliceWindowCoordinator::OnUpdate() { // Has a new main image been loaded if(this->m_EventBucket->HasEvent(MainImageDimensionsChangeEvent())) { // Update each of the slice models, allowing them to respond to the main image // dimensions change for(unsigned int i = 0; i < 3; i++) m_SliceModel[i]->Update(); // Reset the view to fit (depending on linked zoom) if(m_ParentModel->GetDriver()->IsMainImageLoaded()) this->ResetViewToFitInAllWindows(); } if(this->m_EventBucket->HasEvent(GenericSliceModel::ViewportResizeEvent()) || this->m_EventBucket->HasEvent(DisplayLayoutModel::LayerLayoutChangeEvent())) { // If we are maintaining linked zoom, then this class is going to manage the // recomputation of optimal zoom in each window and resetting of the zoom. if(m_LinkedZoom && AreSliceModelsInitialized()) { // Is the current zoom same as the optimal zoom? If so, we will force a // reset after the optimal zooms have been computed double common_opt_zoom = ComputeSmallestOptimalZoomLevel(); double common_zoom = GetCommonZoomLevel(); bool rezoom = (common_zoom == common_opt_zoom); // Recompute the optimal zoom in each of the views for(unsigned int i = 0; i < 3; i++) m_SliceModel[i]->ComputeOptimalZoom(); // Optionally, reset the view if(rezoom) this->ResetViewToFitInAllWindows(); } // Update each of the slice models. This will cause them to recompute their // optimal zoom. } } double SliceWindowCoordinator ::ComputeSmallestOptimalZoomLevel() { assert(m_WindowsRegistered); // How this is computed depends on whether all four views are visible or not DisplayLayoutModel *dlm = m_ParentModel->GetDisplayLayoutModel(); // Figure out what is the optimal zoom in each window bool foundVisible = false; double minoptzoom = 0; for(int i = 0; i < 3; i++) { if(dlm->GetViewPanelVisibilityModel(i)->GetValue()) { double optzoom = m_SliceModel[i]->GetOptimalZoom(); if(!foundVisible || minoptzoom > optzoom) { minoptzoom = optzoom; foundVisible = true; } } } // If nothing is visible, use the optimal zoom from the first window return minoptzoom; } void SliceWindowCoordinator ::ResetViewToFitInAllWindows() { // Only if initialized assert(m_WindowsRegistered); // Reset the view in all windows (center slices) for(unsigned int i=0;i<3;i++) { m_SliceModel[i]->ResetViewToFit(); } // If linked zoom, use the smallest optimal zoom level if(m_LinkedZoom) { double optzoom = ComputeSmallestOptimalZoomLevel(); if(optzoom > 0.0) SetZoomLevelAllWindows(optzoom); } } void SliceWindowCoordinator ::SetZoomPercentageInAllWindows(float x) { // x screen pixels = smallest voxel dimension // zf = x / (smallest voxel dimension) SetZoomLevelAllWindows(x / m_SliceModel[0]->GetSliceSpacing().min_value()); } void SliceWindowCoordinator ::SetZoomFactorAllWindows(float factor) { // Only if initialized assert(m_WindowsRegistered); // If linked zoom, use the smallest optimal zoom level if(m_LinkedZoom) { SetZoomLevelAllWindows(ComputeSmallestOptimalZoomLevel() * factor); } else { for(unsigned int i=0;i<3;i++) { m_SliceModel[i]->SetViewZoom( m_SliceModel[i]->GetOptimalZoom() * factor); } } } void SliceWindowCoordinator ::SetZoomLevelAllWindows(float level) { // Now scale the zoom in each window for(unsigned int i=0;i<3;i++) { m_SliceModel[i]->SetViewZoom(level); } // Invoke event if(m_LinkedZoom) { InvokeEvent(ZoomLevelUpdateEvent()); } } void SliceWindowCoordinator ::ResetViewToFitInOneWindow(unsigned int window) { // Only if initialized assert(m_WindowsRegistered); // Reset zoom to fit in the current window if(m_LinkedZoom) { SetZoomLevelAllWindows(m_SliceModel[window]->GetOptimalZoom()); } m_SliceModel[window]->ResetViewToFit(); } void SliceWindowCoordinator ::ZoomInOrOutInOneWindow(unsigned int window, float factor) { // Only if initialized assert(m_WindowsRegistered); // Apply in the current window m_SliceModel[window]->ZoomInOrOut(factor); // Reset zoom to fit in the current window if(m_LinkedZoom) { SetZoomLevelAllWindows(m_SliceModel[window]->GetViewZoom()); } } void SliceWindowCoordinator::CenterViewOnCursorInAllWindows() { for(int i = 0; i < 3; i++) m_SliceModel[i]->CenterViewOnCursor(); } void SliceWindowCoordinator ::OnZoomUpdateInWindow(unsigned int irisNotUsed(window), float zoom) { // Only if initialized assert(m_WindowsRegistered); if(m_LinkedZoom) { SetZoomLevelAllWindows(zoom); } } void SliceWindowCoordinator ::OnWindowResize() { if(m_LinkedZoom) { SetCommonZoomToSmallestWindowZoom(); } } void SliceWindowCoordinator ::SetCommonZoomToSmallestWindowZoom() { // Compute the minimum zoom float minZoom = 0; for(unsigned int i=0; i<3; i++) { if(i == 0 || minZoom > m_SliceModel[i]->GetViewZoom()) minZoom = m_SliceModel[i]->GetViewZoom(); } // Assign the minimum zoom SetZoomLevelAllWindows(minZoom); } float SliceWindowCoordinator ::GetCommonZoomLevel() { if(m_LinkedZoom && m_WindowsRegistered) return m_SliceModel[0]->GetViewZoom(); else return std::numeric_limits::quiet_NaN(); } float SliceWindowCoordinator ::GetCommonOptimalFitZoomLevel() { assert(m_LinkedZoom && m_WindowsRegistered); return m_SliceModel[0]->GetOptimalZoom(); } void SliceWindowCoordinator ::GetZoomRange(unsigned int window, float &minZoom, float &maxZoom) { assert(m_WindowsRegistered); maxZoom = 0.0f; minZoom = 0.0f; for(unsigned int i=0;i<3;i++) { if(m_LinkedZoom || window == i) { double w = (double) m_SliceModel[i]->GetSize()[0]; double h = (double) m_SliceModel[i]->GetSize()[1]; // Maximum zoom is constrained by the requirement that at least four // pixels are visible in at least one dimensions float zMax1 = 0.25 * w / m_SliceModel[i]->GetSliceSpacing()[0]; float zMax2 = 0.25 * h / m_SliceModel[i]->GetSliceSpacing()[1]; float zMax = zMax1 > zMax2 ? zMax1 : zMax2; maxZoom = (maxZoom == 0.0f || maxZoom < zMax) ? zMax : maxZoom; // Minimum zoom is just 0.25 of the optimal zoom float zMin = 0.25 * m_SliceModel[i]->GetOptimalZoom(); minZoom = (minZoom == 0.0f || minZoom > zMin) ? zMin : minZoom; } } } float SliceWindowCoordinator ::ClampZoom(unsigned int window,float zoom) { assert(m_WindowsRegistered); float minZoom, maxZoom; GetZoomRange(window, minZoom, maxZoom); // Apply the clamp if(zoom < minZoom) return minZoom; if(zoom > maxZoom) return maxZoom; return zoom; } /** Compute a round step size so that there are approximately n_steps steps between a and b */ double round_step_size(double a, double b, double nsteps) { double delta = fabs(b-a); double k = pow(10, floor(log((delta) / nsteps) / log(10.0))); double s = floor(0.5 + delta / (nsteps * k)) * k; return s; } bool SliceWindowCoordinator::GetCommonZoomValueAndRange( double &zoom, NumericValueRange *range) { // Linked zoom required if(!GetLinkedZoom() || !m_ParentModel->GetDriver()->IsMainImageLoaded()) return false; // Get the zoom zoom = (double) GetCommonZoomLevel(); // Get the range if(range) { float fmin, fmax; GetZoomRange(0, fmin, fmax); range->Minimum = fmin; range->Maximum = fmax; // Compute a reasonable step value. This is tricky, because zoom is not // really a linear variable, at high zoom levels, you want steps to be // larger than at small zoom levels. So how about a step that's just on // the order of one hundredth of the current level? range->StepSize = CalculatePowerOfTenStepSize(0, zoom, 10); } return true; } void SliceWindowCoordinator::SetCommonZoomValue(double zoom) { this->SetZoomLevelAllWindows((float) zoom); } bool SliceWindowCoordinator::GetLinkedZoomValue(bool &out_value) { out_value = m_LinkedZoom; return true; } void SliceWindowCoordinator::SetLinkedZoomValue(bool value) { if(m_LinkedZoom != value) { m_LinkedZoom = value; if(m_WindowsRegistered) { // Tell the windows whether they are managed or not for(unsigned int i=0;i<3;i++) m_SliceModel[i]->SetManagedZoom(m_LinkedZoom); // Set the common zoom if an image is loaded if(m_LinkedZoom && m_ParentModel->GetDriver()->IsMainImageLoaded()) SetCommonZoomToSmallestWindowZoom(); } // Fire the appropriate event InvokeEvent(LinkedZoomUpdateEvent()); } } bool SliceWindowCoordinator::AreSliceModelsInitialized() { if(!m_WindowsRegistered) return false; for(unsigned int i=0;i<3;i++) if(!m_SliceModel[i]->IsSliceInitialized()) return false; return true; } itksnap-3.4.0/GUI/Model/SliceWindowCoordinator.h000066400000000000000000000147721263013355200214720ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SliceWindowCoordinator.h,v $ Language: C++ Date: $Date: 2009/05/04 20:15:57 $ Version: $Revision: 1.5 $ Copyright (c) 2007 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----- Copyright (c) 2003 Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/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 notices for more information. =========================================================================*/ #ifndef __SliceWindowCoordinator_h_ #define __SliceWindowCoordinator_h_ #include "GenericSliceModel.h" #include #include #include "PropertyModel.h" class GlobalUIModel; /* Who fires the events? Widgets interacting with the Zoom Factor model expect events from the model. We don't want the parent object to fire events separately. But this means, everyone should interact with the model, not directly with the slice window coordinator. */ /** * \class SliceWindowCoordinator * \brief Coordinates the zoom (and perhaps other) aspects of behavior between * three orthogonal slice windows. * * Helps manage linked zoom (the concept that 1 mm in image space is always the * same number of pixels in each slice view). */ class SliceWindowCoordinator : public AbstractModel { public: irisITKObjectMacro(SliceWindowCoordinator, itk::Object) FIRES(LinkedZoomUpdateEvent) /** Initialize the model. Assigns three windows for the coordinator to manage */ void SetParentModel(GlobalUIModel *model); /** Respond to updates */ virtual void OnUpdate(); /** Specify whether the coordinator should maintain linked zoom * in the three slice windows */ irisSimplePropertyAccessMacro(LinkedZoom, bool) /** Set the zoom to a fraction of the optimal zoom. This makes * the most sense when the zoom level is linked, but can be performed * regardless */ void SetZoomFactorAllWindows(float factor); /** Set the zoom to an absolute value in all windows */ void SetZoomLevelAllWindows(float level); /** This sets the zoom 'percentage'. For example, if x=1, the zoom is set such that one screen pixel is matched to the smallest of voxel dims. If x=2, two pixels match the smallest voxel dim, and so on. The idea is that if your image is isotropic, by setting x=1,2,4, etc., you can avoid aliasing the displayed image */ void SetZoomPercentageInAllWindows(float x); /** Reset the zoom in all windows to an optimal value, ie, such a zoom * that the image fits into each of the windows. Depending on whether * the zoom is linked or not, this will either zoom each slice as much * as possible, or zoom the largest of the 3 slices as much as possible */ void ResetViewToFitInAllWindows(); /** Reset the zoom in one window to optimal value. When linked zoom is * maintained, this has the same effect as ResetViewToFitInAllWindows, * and if not, it only affects the given window */ void ResetViewToFitInOneWindow(unsigned int window); /** Update zoom by a specified factor in a window */ void ZoomInOrOutInOneWindow(unsigned int window, float factor); /** Center the view on the cursor in all slice windows */ void CenterViewOnCursorInAllWindows(); /** When one of the windows wants to change the zoom w.r.t. a user * action, this class will adjust, if necessary, the zoom in the other * windows */ void OnZoomUpdateInWindow(unsigned int window, float zoom); /** React to a resizing of the windows. This will try to maintain the current view * depending on the state. If the zooms are in 'reset' state, this will keep * them in the reset state, and otherwise, it will maintain the zoom levels */ void OnWindowResize(); /** Get the common zoom factor */ float GetCommonZoomLevel(); /** Get the zoom factor that will fit all three slices optimally */ float GetCommonOptimalFitZoomLevel(); /** Get the model representing the optimal zoom */ irisGetMacro(CommonZoomFactorModel, AbstractRangedDoubleProperty*) /** Constrain a zoom factor to reasonable limits */ float ClampZoom(unsigned int window,float zoom); /** Get the range of zoom allowed */ void GetZoomRange(unsigned int window, float &minZoom, float &maxZoom); /** Get the window number n */ GenericSliceModel *GetSliceModel(unsigned int window) { return m_SliceModel[window]; } protected: /** Constructor */ SliceWindowCoordinator(); /** Virtual destructor */ virtual ~SliceWindowCoordinator(); /** The parent model */ GlobalUIModel *m_ParentModel; /** The pointers to three window interactors managed by this class */ GenericSliceModel *m_SliceModel[3]; /** Whether or not linked zoom is maintained */ bool m_LinkedZoom; /** Whether the windows have been registered */ bool m_WindowsRegistered; /** Method that sets all the zooms to a common value */ void SetCommonZoomToSmallestWindowZoom(); /** Compute the smallest of the optimal zoom levels of the slice views */ double ComputeSmallestOptimalZoomLevel(); // Child model governing linked zoom properties SmartPtr m_CommonZoomFactorModel; // Methods that the model above wraps around bool GetCommonZoomValueAndRange(double &zoom, NumericValueRange *range); void SetCommonZoomValue(double zoom); // Child model for the linked zoom flag SmartPtr m_LinkedZoomModel; // Methods that the model above wraps around bool GetLinkedZoomValue(bool &out_value); void SetLinkedZoomValue(bool value); // Are the slice models initialized bool AreSliceModelsInitialized(); }; #endif // __SliceWindowCoordinator_h_ itksnap-3.4.0/GUI/Model/SnakeParameterModel.cxx000066400000000000000000000244041263013355200212660ustar00rootroot00000000000000#include "SnakeParameterModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "GlobalState.h" #include "SnakeParametersPreviewPipeline.h" #include "itkImageFileReader.h" #include "itkShiftScaleImageFilter.h" #include "UIReporterDelegates.h" #include "SNAPRegistryIO.h" #include "HistoryManager.h" #include "SNAPImageData.h" SnakeParameterModel::SnakeParameterModel() { // Create the derived models for(int i = 0; i < 3; i++) { m_WeightModel[i] = wrapIndexedGetterSetterPairAsProperty( this, i, &Self::GetWeightValueAndRange, &Self::SetWeightValue); m_ExponentModel[i] = wrapIndexedGetterSetterPairAsProperty( this, i, &Self::GetExponentValueAndRange, &Self::SetExponentValue); } m_SpeedupFactorModel = wrapGetterSetterPairAsProperty( this, &Self::GetSpeedupFactorValueAndRange, &Self::SetSpeedupFactorValue); m_AdvancedEquationModeModel = NewSimpleConcreteProperty(false); m_CasellesOrAdvancedModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetCasellesOrAdvancedModeValue); m_AnimateDemoModel = NewSimpleConcreteProperty(false); // Treat changes to the state as model updates Rebroadcast(m_AdvancedEquationModeModel, ValueChangedEvent(), ModelUpdateEvent()); m_PreviewPipeline = NULL; } SnakeParameterModel::~SnakeParameterModel() { if(m_PreviewPipeline) delete m_PreviewPipeline; } void SnakeParameterModel::SetParentModel(GlobalUIModel *model) { this->m_ParentModel = model; this->m_ParametersModel = m_ParentModel->GetGlobalState()->GetSnakeParametersModel(); // Listen and rebroadcast changes to the internally stored snake parameters Rebroadcast(m_ParametersModel, ValueChangedEvent(), ModelUpdateEvent()); // Set up the preview pipeline this->SetupPreviewPipeline(); } void SnakeParameterModel::SetupPreviewPipeline() { // Initialize the pipeline if(m_PreviewPipeline) delete m_PreviewPipeline; m_PreviewPipeline = new SnakeParametersPreviewPipeline(m_ParentModel->GetGlobalState()); // Get the parent's system object SystemInterface *si = m_ParentModel->GetSystemInterface(); // Get the edge and region example image file names std::string fnImage[] = { "EdgeForcesExample.png", "RegionForcesExample.png"}; // Typedefs typedef itk::Image InputImageType; typedef itk::ImageFileReader ReaderType; typedef itk::ImageRegionIterator IteratorType; typedef itk::ShiftScaleImageFilter ScaleShiftType; // Load each of these images static const float scale_factor[] = { 1.0f / 255.0f, -2.0f / 255.0f }; static const float shift_factor[] = { 0.0f, -127.5f }; for(unsigned int i = 0; i < 2; i++) { try { // Initialize the image SmartPtr imgInput = InputImageType::New(); // Load the example image using the application resource system si->GetSystemInfoDelegate()->LoadResourceAsImage2D(fnImage[i], imgInput); // Read the image in ScaleShiftType::Pointer scaler = ScaleShiftType::New(); scaler->SetScale(0x7fff * scale_factor[i]); scaler->SetShift(shift_factor[i]); scaler->SetInput(imgInput); scaler->Update(); // Allocate the example image m_ExampleImage[i] = scaler->GetOutput(); } catch(...) { // An exception occurred. We don't want to freak out about this, so // just print out a message std::cerr << "Unable to read example image " << fnImage[i] << "; Snake preview will me missing an image" << std::endl; // Initialize the image to zeros itk::ImageRegion<2> defregion; defregion.SetSize(0, 128); defregion.SetSize(1, 128); m_ExampleImage[i] = ExampleImageType::New(); m_ExampleImage[i]->SetRegions(defregion); m_ExampleImage[i]->Allocate(); m_ExampleImage[i]->FillBuffer(0); } } // Pass one of the images to the pipeline if(this->IsRegionSnake()) m_PreviewPipeline->SetSpeedImage(m_ExampleImage[1]); else m_PreviewPipeline->SetSpeedImage(m_ExampleImage[0]); // Load the points from the registry std::vector points; try { Registry regPoints; si->GetSystemInfoDelegate()-> LoadResourceAsRegistry("SnakeParameterPreviewCurve.txt", regPoints); points = regPoints.Folder("Points").GetArray(Vector2d(0.0)); } catch(...) { // Again, we don't want to freak out. std::cerr << "Unable to read example point data for snake preview" << std::endl; } // If there are some points in there, draw them if(points.size() >= 4) { // Set spline points, etc m_PreviewPipeline->SetControlPoints(points); } // Pass the parameters to the preview pipeline m_PreviewPipeline->SetSnakeParameters(m_ParametersModel->GetValue()); } void SnakeParameterModel::OnUpdate() { if(this->m_EventBucket->HasEvent(ValueChangedEvent(), m_ParametersModel)) { // Send the parameters to the segmentation pipeline. This is kind of // stupid, because we are using a model to coordinate between two copies // of the parameters, but it should work SNAPImageData *sid = m_ParentModel->GetDriver()->GetSNAPImageData(); if(sid && sid->IsSegmentationActive()) { sid->SetSegmentationParameters(m_ParametersModel->GetValue()); } // Update the speed image used by the preview pipeline ExampleImageType *speed = this->IsRegionSnake() ? m_ExampleImage[1] : m_ExampleImage[0]; if(m_PreviewPipeline->GetSpeedImage() != speed) m_PreviewPipeline->SetSpeedImage(speed); m_PreviewPipeline->SetSnakeParameters(m_ParametersModel->GetValue()); } } bool SnakeParameterModel::IsRegionSnake() { SnakeParameters param = m_ParametersModel->GetValue(); return param.GetSnakeType() == SnakeParameters::REGION_SNAKE; } void SnakeParameterModel::PerformAnimationStep() { m_PreviewPipeline->AnimationCallback(); InvokeEvent(DemoLoopEvent()); } void SnakeParameterModel::ResetAnimation() { m_PreviewPipeline->AnimationRestart(); } void SnakeParameterModel::LoadParameters(const std::string &file) { // Create default parameters SnakeParameters param = (this->IsRegionSnake()) ? SnakeParameters::GetDefaultInOutParameters() : SnakeParameters::GetDefaultEdgeParameters(); // Read parameters from file SNAPRegistryIO io; Registry regParameters(file.c_str()); param = io.ReadSnakeParameters(regParameters, param); // Update the history m_ParentModel->GetDriver()->GetHistoryManager()-> UpdateHistory("SnakeParameters", file, true); // Set the parameters m_ParametersModel->SetValue(param); } void SnakeParameterModel::SaveParameters(const std::string &file) { // Load the parameters SnakeParameters param = m_ParametersModel->GetValue(); SNAPRegistryIO io; Registry regParameters; io.WriteSnakeParameters(param, regParameters); regParameters.WriteToFile(file.c_str()); // Update the history m_ParentModel->GetDriver()->GetHistoryManager()-> UpdateHistory("SnakeParameters", file, true); } void SnakeParameterModel::RestoreDefaults() { // Create default parameters SnakeParameters param = (this->IsRegionSnake()) ? SnakeParameters::GetDefaultInOutParameters() : SnakeParameters::GetDefaultEdgeParameters(); // Set the parameters m_ParametersModel->SetValue(param); } bool SnakeParameterModel ::GetWeightValueAndRange( int index, double &value, NumericValueRange *domain) { SnakeParameters param = m_ParametersModel->GetValue(); if(index == ALHPA) { value = param.GetPropagationWeight(); if(domain) domain->Set(this->IsRegionSnake() ? 0.0 : -1.0, 1.0, 0.01); return true; } else if(index == BETA) { value = param.GetCurvatureWeight(); if(domain) domain->Set(0.0, 1.0, 0.01); return true; } else if(index == GAMMA && this->GetCasellesOrAdvancedModeValue()) { value = param.GetAdvectionWeight(); if(domain) domain->Set(0.0, 5.0, 0.05); return true; } return false; } void SnakeParameterModel ::SetWeightValue(int index, double value) { SnakeParameters param = m_ParametersModel->GetValue(); switch(index) { case ALHPA: param.SetPropagationWeight(value); break; case BETA: param.SetCurvatureWeight(value); break; case GAMMA: param.SetAdvectionWeight(value); break; } m_ParametersModel->SetValue(param); } bool SnakeParameterModel ::GetExponentValueAndRange( int index, int &value, NumericValueRange *domain) { // Only valid in advanced mode if(!this->m_AdvancedEquationModeModel->GetValue()) return false; SnakeParameters param = m_ParametersModel->GetValue(); if(index == ALHPA) { value = param.GetPropagationSpeedExponent(); if(domain) domain->Set(0, 2, 1); return true; } else if(index == BETA) { value = param.GetCurvatureSpeedExponent(); if(domain) domain->Set(0, 2, 1); return true; } else if(index == GAMMA) { value = param.GetAdvectionSpeedExponent(); if(domain) domain->Set(0, 2, 1); return true; } return false; } void SnakeParameterModel ::SetExponentValue(int index, int value) { SnakeParameters param = m_ParametersModel->GetValue(); switch(index) { case ALHPA: param.SetPropagationSpeedExponent(value); break; case BETA: param.SetCurvatureSpeedExponent(value); break; case GAMMA: param.SetAdvectionSpeedExponent(value); break; } m_ParametersModel->SetValue(param); } bool SnakeParameterModel ::GetSpeedupFactorValueAndRange(double &value, NumericValueRange *domain) { SnakeParameters param = m_ParametersModel->GetValue(); if(param.GetAutomaticTimeStep()) value = 1; else value = param.GetTimeStepFactor(); if(domain) domain->Set(1.0, 10.0, 0.25); return true; } void SnakeParameterModel ::SetSpeedupFactorValue(double value) { SnakeParameters param = m_ParametersModel->GetValue(); if(value == 1.0) { param.SetAutomaticTimeStep(true); param.SetTimeStepFactor(1.0f); } else { param.SetAutomaticTimeStep(false); param.SetTimeStepFactor((float) value); } m_ParametersModel->SetValue(param); } bool SnakeParameterModel::GetCasellesOrAdvancedModeValue() { return this->GetAdvancedEquationModeModel()->GetValue() || (!this->IsRegionSnake()); } itksnap-3.4.0/GUI/Model/SnakeParameterModel.h000066400000000000000000000067621263013355200207220ustar00rootroot00000000000000#ifndef SNAKEPARAMETERMODEL_H #define SNAKEPARAMETERMODEL_H #include "SNAPCommon.h" #include "AbstractModel.h" #include "PropertyModel.h" #include "SnakeParameters.h" class GlobalUIModel; class SnakeParametersPreviewPipeline; namespace itk { template class Image; } /** * @brief The SnakeParameterModel class * This model handles interaction with snake parameters */ class SnakeParameterModel : public AbstractModel { public: irisITKObjectMacro(SnakeParameterModel, AbstractModel) // This model fires a custom DemoLoopEvent when the demo loop is run itkEventMacro(DemoLoopEvent, IRISEvent) FIRES(DemoLoopEvent) irisGetMacro(ParentModel, GlobalUIModel *) void SetParentModel(GlobalUIModel *model); virtual void OnUpdate(); // State machine enums enum UIState { UIF_CASELLES_MODE, UIF_ZHU_MODE, UIF_EXPONENTS_SHOWN }; // To make life easier, we access the parameters using an index, instead // of having an endless list of models. enum ParamIndex { ALHPA = 0, BETA, GAMMA }; // Access one of the models for the weights alpha, beta, gamma AbstractRangedDoubleProperty *GetWeightModel(int index) { return m_WeightModel[index]; } // Access one of the models for the exponents of corresp. terms AbstractRangedIntProperty *GetExponentModel(int index) { return m_ExponentModel[index]; } // Speedup factor irisRangedPropertyAccessMacro(SpeedupFactor, double) // The model for whether the advanced mode (exponents) is on irisSimplePropertyAccessMacro(AdvancedEquationMode, bool) irisSimplePropertyAccessMacro(CasellesOrAdvancedMode, bool) // Whether the demo is being animated irisSimplePropertyAccessMacro(AnimateDemo, bool) // Whether or not we are in Zhu (region competition) mode bool IsRegionSnake(); // Preview pipeline irisGetMacro(PreviewPipeline, SnakeParametersPreviewPipeline *) // Run a loop of the demo animation void PerformAnimationStep(); // Run a loop of the demo animation void ResetAnimation(); // Parameter IO void LoadParameters(const std::string &file); void SaveParameters(const std::string &file); void RestoreDefaults(); protected: SnakeParameterModel(); virtual ~SnakeParameterModel(); SmartPtr m_WeightModel[3]; bool GetWeightValueAndRange( int index, double &value, NumericValueRange *domain); void SetWeightValue(int index, double value); SmartPtr m_ExponentModel[3]; bool GetExponentValueAndRange( int index, int &value, NumericValueRange *domain); void SetExponentValue(int index, int value); SmartPtr m_SpeedupFactorModel; bool GetSpeedupFactorValueAndRange( double &value, NumericValueRange *domain); void SetSpeedupFactorValue(double value); SmartPtr m_AdvancedEquationModeModel; SmartPtr m_CasellesOrAdvancedModeModel; bool GetCasellesOrAdvancedModeValue(); SmartPtr m_AnimateDemoModel; void SetupPreviewPipeline(); // Preview pipeline SnakeParametersPreviewPipeline *m_PreviewPipeline; // Example images for the preview pipeline typedef itk::Image ExampleImageType; SmartPtr m_ExampleImage[2]; // Parent model GlobalUIModel *m_ParentModel; // Model governing the parameters AbstractPropertyModel *m_ParametersModel; }; #endif // SNAKEPARAMETERMODEL_H itksnap-3.4.0/GUI/Model/SnakeROIModel.cxx000066400000000000000000000200231263013355200177700ustar00rootroot00000000000000#include "SnakeROIModel.h" #include "GenericSliceModel.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "GlobalState.h" SnakeROIModel::SnakeROIModel() { } void SnakeROIModel::SetParent(GenericSliceModel *parent) { // Set up the parent m_Parent = parent; // Listen to changes in the parent's segmentation ROI settings Rebroadcast( m_Parent->GetDriver()->GetGlobalState()->GetSegmentationROISettingsModel(), ValueChangedEvent(), ModelUpdateEvent()); // Layer change events too? Rebroadcast(m_Parent->GetDriver(), LayerChangeEvent(), ModelUpdateEvent()); } // Return true if the selection state has changed SnakeROISideSelectionState SnakeROIModel ::ComputeSelection(Vector2f &uvSlice, Vector3f corners[]) { // This code computes the current selection based on the mouse coordinates // Flag indicating whether we respond to this event or not SnakeROISideSelectionState h; // Repeat for vertical and horizontal edges for(unsigned int dir = 0; dir < 2; dir++) { // Variables used to find the closest edge that's within delta int iClosest = -1; float dToClosest = m_PixelDelta; // Search for the closest edge for(unsigned int i=0;i<2;i++) { float d = GetEdgeDistance(dir,i,uvSlice,corners); if(d < dToClosest) { dToClosest = d; iClosest = i; } } // Highlight the selected edge if(iClosest >= 0) { h.Highlighted[dir][iClosest] = true; } } // Also test for an inside click (all selected) if(!h.AnyHighlighted()) { // Are we inside? if(uvSlice[0] > std::min(corners[0][0], corners[1][0]) && uvSlice[0] < std::max(corners[0][0], corners[1][0]) && uvSlice[1] > std::min(corners[0][1], corners[1][1]) && uvSlice[1] < std::max(corners[0][1], corners[1][1])) { h.Highlighted[0][0]=true; h.Highlighted[0][1]=true; h.Highlighted[1][0]=true; h.Highlighted[1][1]=true; } } return h; } bool SnakeROIModel::ProcessPushEvent(float x, float y) { // Convert the event location into slice u,v coordinates Vector2f uvSlice(x, y); // Record the system's corners at the time of drag start GetSystemROICorners(m_CornerDragStart); // Compute the selection SnakeROISideSelectionState hl = ComputeSelection(uvSlice, m_CornerDragStart); if(hl != m_Highlight) { m_Highlight = hl; InvokeEvent(ModelUpdateEvent()); } // If nothing was highlighted, then return and let the next handler process // the event return m_Highlight.AnyHighlighted(); } bool SnakeROIModel ::ProcessMoveEvent(float x, float y) { // Convert the event location into slice u,v coordinates Vector2f uvSlice(x, y); // Record the system's corners at the time of drag start GetSystemROICorners(m_CornerDragStart); // Compute the selection SnakeROISideSelectionState hl = ComputeSelection(uvSlice, m_CornerDragStart); if(hl != m_Highlight) { m_Highlight = hl; InvokeEvent(ModelUpdateEvent()); } return true; } bool SnakeROIModel::ProcessDragEvent( float x, float y, float xStart, float yStart, bool release) { // Only do something if there is a highlight if(m_Highlight.AnyHighlighted()) { // Update the corners in response to the dragging UpdateCorners(Vector2f(x, y), Vector2f(xStart,yStart)); // Event has been handled return true; } // Event has not been handled return false; } // The click detection radius (delta) const unsigned int SnakeROIModel::m_PixelDelta = 8; void SnakeROIModel ::GetEdgeVertices(unsigned int direction,unsigned int index, Vector2f &x0,Vector2f &x1, const Vector3f corner[2]) { x0(direction) = corner[0](direction); x1(direction) = corner[1](direction); x0(1-direction) = x1(1-direction) = corner[index](1-direction); } float SnakeROIModel ::GetEdgeDistance(unsigned int direction, unsigned int index, const Vector2f &x, const Vector3f corner[2]) { // Compute the vertices of the edge Vector2f x0,x1; GetEdgeVertices(direction,index,x0,x1,corner); // Compute the squared distance between the vertices float l2 = (x1-x0).squared_magnitude(); float l = sqrt(l2); // Compute the projection of x onto x1-x0 float p = dot_product(x-x0,x1-x0) / sqrt(l2); float p2 = p*p; // Compute the squared distance to the line of the edge float q2 = (x-x0).squared_magnitude() - p2; // Compute the total distance float d = sqrt(q2 + (p < 0 ? p2 : 0) + (p > l ? (p-l)*(p-l) : 0)); // Return this distance return d; } void SnakeROIModel ::GetSystemROICorners(Vector3f corner[2]) { // Get the region of interest in image coordinates GlobalState *gs = m_Parent->GetDriver()->GetGlobalState(); GlobalState::RegionType roi = gs->GetSegmentationROI(); // Get the lower-valued corner Vector3l ul(roi.GetIndex().GetIndex()); // Get the higher valued corner Vector3ul sz(roi.GetSize().GetSize()); // Remap to slice coordinates corner[0] = m_Parent->MapImageToSlice(to_float(ul)); corner[1] = m_Parent->MapImageToSlice(to_float(ul+to_long(sz))); } void SnakeROIModel ::UpdateCorners(const Vector2f &uvSliceNow, const Vector2f &uvSlicePress) { // Compute the corners in slice coordinates Vector3f corner[2]; GetSystemROICorners(corner); // Get the current bounds and extents of the region of interest Vector3f xCornerImage[2] = { m_Parent->MapSliceToImage(corner[0]), m_Parent->MapSliceToImage(corner[1]) }; Vector3f clamp[2][2] = { { Vector3f(0.0f,0.0f,0.0f), xCornerImage[1] - Vector3f(1.0f,1.0f,1.0f) }, { xCornerImage[0] + Vector3f(1.0f,1.0f,1.0f), to_float(m_Parent->GetDriver()->GetCurrentImageData()->GetVolumeExtents()) } }; // For each highlighted edge, update the coordinates of the affected vertex // by clamping to the maximum range for (unsigned int dir=0;dir<2;dir++) { for (unsigned int i=0;i<2;i++) { if (m_Highlight.Highlighted[dir][i]) { // Horizontal edge affects the y of the vertex and vice versa corner[i](1-dir) = m_CornerDragStart[i](1-dir) + uvSliceNow(1-dir) - uvSlicePress(1-dir); // Map the affected vertex to image space Vector3f vImage = m_Parent->MapSliceToImage(corner[i]); // Clamp the affected vertex in image space Vector3f vImageClamped = vImage.clamp(clamp[i][0],clamp[i][1]); // Map the affected vertex back into slice space corner[i] = m_Parent->MapImageToSlice(vImageClamped); } } } // Update the region of interest in the system Vector3i xImageLower = to_int(m_Parent->MapSliceToImage(corner[0])); Vector3i xImageUpper = to_int(m_Parent->MapSliceToImage(corner[1])); // Create a region based on the corners GlobalState::RegionType roiCorner( to_itkIndex(xImageLower),to_itkSize(xImageUpper-xImageLower)); // Get the system's region of interest GlobalState *gs = m_Parent->GetDriver()->GetGlobalState(); GlobalState::RegionType roiSystem = gs->GetSegmentationROI(); // The slice z-direction index and size in the ROI should retain the system's // previous value because we are only manipulating the slice in 2D unsigned int idx = m_Parent->GetSliceDirectionInImageSpace(); roiCorner.SetIndex(idx,roiSystem.GetIndex(idx)); roiCorner.SetSize(idx,roiSystem.GetSize(idx)); // Update the system's ROI (TODO: make this fire an event!) gs->SetSegmentationROI(roiCorner); } void SnakeROIModel::ResetROI() { // Region of interest from the main image GlobalState::RegionType roi = m_Parent->GetDriver()->GetCurrentImageData()->GetImageRegion(); // Can't be empty! assert(roi.GetNumberOfPixels()); // Update m_Parent->GetDriver()->GetGlobalState()->SetSegmentationROI(roi); } void SnakeROIModel::ProcessLeaveEvent() { // Turn off the highlight SnakeROISideSelectionState blank; if(m_Highlight != blank) { m_Highlight = blank; InvokeEvent(ModelUpdateEvent()); } } void SnakeROIModel::ProcessEnterEvent() { // Nothing to do here! } itksnap-3.4.0/GUI/Model/SnakeROIModel.h000066400000000000000000000076121263013355200174260ustar00rootroot00000000000000#ifndef SNAKEROIMODEL_H #define SNAKEROIMODEL_H #include #include "AbstractModel.h" #include #include "PropertyModel.h" class GenericSliceModel; struct SnakeROISideSelectionState { /** * The four edges in the rectangle, ordered first by orientation * (0 = horizontal), (1 = vertical) and then by adjacency to the * first or second vertex of the cardinal cube defining the region * of interest (0 = closest to 0,0,0), (1 = closest to sx,sy,sz) */ bool Highlighted[2][2]; bool AnyHighlighted() { return (Highlighted[0][0] || Highlighted[0][1] || Highlighted[1][0] || Highlighted[1][1]); } SnakeROISideSelectionState() { Highlighted[0][0] = Highlighted[0][1] = Highlighted[1][0] = Highlighted[1][1] = false; } SnakeROISideSelectionState(const SnakeROISideSelectionState &ref) { Highlighted[0][0] = ref.Highlighted[0][0]; Highlighted[0][1] = ref.Highlighted[0][1]; Highlighted[1][0] = ref.Highlighted[1][0]; Highlighted[1][1] = ref.Highlighted[1][1]; } bool operator == (const SnakeROISideSelectionState &ref) { return (Highlighted[0][0] == ref.Highlighted[0][0] && Highlighted[0][1] == ref.Highlighted[0][1] && Highlighted[1][0] == ref.Highlighted[1][0] && Highlighted[1][1] == ref.Highlighted[1][1]); } bool operator != (const SnakeROISideSelectionState &ref) { return !(*this == ref); } }; /** This class handles the interaction with the selection box. TODO: The interaction could be improved to do things like highlight the edge under the mouse on mouse motion, to change the cursor when the mouse is over the draggable region, etc. Also it would be nice to always show the box (even when the cursor is outside of the selection) and to use a fill to indicate that a region is outside of the ROI. But all this is easy to change. The model also provides the property models for the dialog used to resample the snake ROI. */ class SnakeROIModel : public AbstractModel { public: irisITKObjectMacro(SnakeROIModel, AbstractModel) irisGetMacro(Parent, GenericSliceModel *) void SetParent(GenericSliceModel *); bool ProcessPushEvent(float x, float y); bool ProcessDragEvent( float x, float y, float xStart, float yStart, bool release); bool ProcessMoveEvent(float x, float y); void ProcessLeaveEvent(); void ProcessEnterEvent(); void ResetROI(); friend class SnakeROIRenderer; protected: // The click detection radius (delta) static const unsigned int m_PixelDelta; // Four vertices in the region box (correspond to the two corners // of the 3D region of interest Vector3f m_CornerDragStart[2]; /** * The four edges in the rectangle, ordered first by orientation * (0 = horizontal), (1 = vertical) and then by adjacency to the * first or second vertex of the cardinal cube defining the region * of interest (0 = closest to 0,0,0), (1 = closest to sx,sy,sz) */ SnakeROISideSelectionState m_Highlight; /** Map from system's ROI in image coordinates to 2D slice coords */ void GetSystemROICorners(Vector3f corner[2]); /** Compute the slice-space vertices corresponding to an edge */ void GetEdgeVertices(unsigned int direction, unsigned int index,Vector2f &x0,Vector2f &x1,const Vector3f corner[2]); /** Compute a distance to an edge */ float GetEdgeDistance(unsigned int direction, unsigned int index,const Vector2f &point,const Vector3f corner[2]); /** * Update the region of interest in response to the dragging or release * operations. */ void UpdateCorners(const Vector2f &xPress, const Vector2f &xDrag); SnakeROISideSelectionState ComputeSelection(Vector2f &uvSlice, Vector3f corners[]); // Parent model GenericSliceModel *m_Parent; // Private constructor stuff SnakeROIModel(); virtual ~SnakeROIModel() {} }; #endif // SNAKEROIMODEL_H itksnap-3.4.0/GUI/Model/SnakeROIResampleModel.cxx000066400000000000000000000172451263013355200214750ustar00rootroot00000000000000#include "SnakeROIResampleModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "SNAPSegmentationROISettings.h" SnakeROIResampleModel::SnakeROIResampleModel() { // Set up the property models for(int i = 0; i < 3; i++) { m_InputSpacingModel[i] = wrapIndexedGetterSetterPairAsProperty( this, i, &Self::GetInputSpacingValueAndRange); m_InputDimensionsModel[i] = wrapIndexedGetterSetterPairAsProperty( this, i, &Self::GetInputDimensionsValueAndRange); m_OutputSpacingModel[i] = wrapIndexedGetterSetterPairAsProperty( this, i, &Self::GetOutputSpacingValueAndRange, &Self::SetOutputSpacingValue); m_OutputDimensionsModel[i] = wrapIndexedGetterSetterPairAsProperty( this, i, &Self::GetOutputDimensionsValueAndRange, &Self::SetOutputDimensionsValue); } // Aspect ratio m_FixedAspectRatioModel = NewSimpleConcreteProperty(false); // Create the interpolation model m_InterpolationModeModel = ConcreteInterpolationModeModel::New(); } void SnakeROIResampleModel::SetParentModel(GlobalUIModel *model) { m_Parent = model; m_ROISettingsModel = m_Parent->GetDriver()->GetGlobalState()->GetSegmentationROISettingsModel(); // Listen to changes in the parent's segmentation ROI settings Rebroadcast(m_ROISettingsModel, ValueChangedEvent(), ModelUpdateEvent()); // Layer change events too Rebroadcast(m_Parent->GetDriver(), LayerChangeEvent(), ModelUpdateEvent()); } void SnakeROIResampleModel::Reset() { SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); m_ResampleDimensions = roi.GetROI().GetSize(); m_InterpolationModeModel->SetValue(SNAPSegmentationROISettings::TRILINEAR); InvokeEvent(ModelUpdateEvent()); } void SnakeROIResampleModel::Accept() { SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); roi.SetResampleDimensions(m_ResampleDimensions); roi.SetInterpolationMethod(m_InterpolationModeModel->GetValue()); m_ROISettingsModel->SetValue(roi); } void SnakeROIResampleModel::ApplyPreset(SnakeROIResampleModel::ResamplePreset preset) { // Get the current spacing IRISApplication *app = m_Parent->GetDriver(); Vector3d in_spacing = app->GetCurrentImageData()->GetImageSpacing(); Vector3d out_spacing; if(preset == SUPER_2) { out_spacing = in_spacing / 2.0; } else if(preset == SUB_2) { out_spacing = in_spacing * 2.0; } if(preset == SUPER_ISO) { double mindim = in_spacing.min_value(); out_spacing.fill(mindim); this->SetFixedAspectRatio(false); } else if(preset == SUB_ISO) { double maxdim = in_spacing.max_value(); out_spacing.fill(maxdim); this->SetFixedAspectRatio(false); } for(int i = 0; i < 3; i++) m_OutputSpacingModel[i]->SetValue(out_spacing[i]); } void SnakeROIResampleModel::ComputeCachedDomains() { IRISApplication *app = m_Parent->GetDriver(); Vector3d spacing = app->GetCurrentImageData()->GetImageSpacing(); SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); // The reasonable range for the value is not obvious. The largest possible // value should be the dimension of the image times the voxel size. The // smallest should be something reasonable, like voxel size/10, rounded, // since any larger interpolation would be infeasible. The step should probably // equal the smallest value Vector3ui sz = roi.GetROI().GetSize(); for(int i = 0; i < 3; i++) { double step = pow(10, floor(log10(spacing[i] / 10.0))); m_SpacingDomain[i].Set(step, sz[i] * spacing[i], step); m_DimensionsDomain[i].Set(1, sz[i]*10, 1); } // TODO: higher order interpolation methods are currently unsupported for // vector images. This is a problem and should be fixed // Set up the interpolation mode map m_InterpolationModeDomain[SNAPSegmentationROISettings::NEAREST_NEIGHBOR] = "Nearest neighbor (fast)"; m_InterpolationModeDomain[SNAPSegmentationROISettings::TRILINEAR] = "Linear interpolation (better quality)"; // m_InterpolationModeDomain[SNAPSegmentationROISettings::TRICUBIC] = // "Cubic interpolation (high quality)"; // m_InterpolationModeDomain[SNAPSegmentationROISettings::SINC_WINDOW_05] = // "Windowed sinc interpolation (best quality)"; m_InterpolationModeModel->SetDomain(m_InterpolationModeDomain); } void SnakeROIResampleModel::EnforceAspectRatio(int source_idx) { SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); double aspect = m_ResampleDimensions[source_idx] * 1.0 / roi.GetROI().GetSize()[source_idx]; for(int i = 0; i < 3; i++) { if(i != source_idx) { unsigned int new_dim = itk::Math::Round(aspect * roi.GetROI().GetSize()[i]); m_ResampleDimensions[i] = new_dim >= 1 ? new_dim : 1; } } } void SnakeROIResampleModel::OnUpdate() { if(this->m_EventBucket->HasEvent(LayerChangeEvent()) || this->m_EventBucket->HasEvent(ValueChangedEvent(), m_ROISettingsModel)) { if(m_Parent->GetDriver()->IsMainImageLoaded()) { ComputeCachedDomains(); SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); m_ResampleDimensions = roi.GetResampleDimensions(); m_InterpolationModeModel->SetValue(roi.GetInterpolationMethod()); } } } bool SnakeROIResampleModel::GetInputSpacingValueAndRange(int index, double &value, NumericValueRange *domain) { IRISApplication *app = m_Parent->GetDriver(); if(app->IsMainImageLoaded()) { value = app->GetCurrentImageData()->GetImageSpacing()[index]; if(domain) *domain = m_SpacingDomain[index]; return true; } return false; } bool SnakeROIResampleModel ::GetInputDimensionsValueAndRange( int index, unsigned int &value, NumericValueRange *domain) { IRISApplication *app = m_Parent->GetDriver(); if(app->IsMainImageLoaded()) { SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); value = roi.GetROI().GetSize()[index]; return true; } return false; } bool SnakeROIResampleModel::GetOutputSpacingValueAndRange( int index, double &value, NumericValueRange *domain) { IRISApplication *app = m_Parent->GetDriver(); if(app->IsMainImageLoaded()) { Vector3d inSpacing = app->GetCurrentImageData()->GetImageSpacing(); SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); value = roi.GetROI().GetSize()[index] * inSpacing[index] / m_ResampleDimensions[index]; if(domain) *domain = m_SpacingDomain[index]; return true; } return false; } void SnakeROIResampleModel::SetOutputSpacingValue(int index, double value) { IRISApplication *app = m_Parent->GetDriver(); SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); Vector3d inSpacing = app->GetCurrentImageData()->GetImageSpacing(); // Calculate the dimensions m_ResampleDimensions[index] = itk::Math::Round( roi.GetROI().GetSize()[index] * inSpacing[index] / value); if(GetFixedAspectRatio()) EnforceAspectRatio(index); InvokeEvent(ModelUpdateEvent()); } bool SnakeROIResampleModel ::GetOutputDimensionsValueAndRange(int index, unsigned int &value, NumericValueRange *domain) { IRISApplication *app = m_Parent->GetDriver(); SNAPSegmentationROISettings roi = m_ROISettingsModel->GetValue(); if(app->IsMainImageLoaded()) { value = m_ResampleDimensions[index]; if(domain) { *domain = m_DimensionsDomain[index]; } return true; } return false; } void SnakeROIResampleModel ::SetOutputDimensionsValue(int index, unsigned int value) { m_ResampleDimensions[index] = value; if(GetFixedAspectRatio()) EnforceAspectRatio(index); InvokeEvent(ModelUpdateEvent()); } itksnap-3.4.0/GUI/Model/SnakeROIResampleModel.h000066400000000000000000000064071263013355200211200ustar00rootroot00000000000000#ifndef SNAKEROIRESAMPLEMODEL_H #define SNAKEROIRESAMPLEMODEL_H #include "PropertyModel.h" #include "SNAPSegmentationROISettings.h" class GlobalUIModel; /** * @brief The SnakeROIResampleModel class * Handles the resample snake dialog. */ class SnakeROIResampleModel : public AbstractModel { public: irisITKObjectMacro(SnakeROIResampleModel, AbstractModel) AbstractRangedDoubleProperty *GetInputSpacingModel(int index) { return m_InputSpacingModel[index]; } AbstractRangedUIntProperty *GetInputDimensionsModel(int index) { return m_InputDimensionsModel[index]; } AbstractRangedDoubleProperty *GetOutputSpacingModel(int index) { return m_OutputSpacingModel[index]; } AbstractRangedUIntProperty *GetOutputDimensionsModel(int index) { return m_OutputDimensionsModel[index]; } irisSimplePropertyAccessMacro(FixedAspectRatio, bool) typedef SNAPSegmentationROISettings::InterpolationMethod InterpolationMode; typedef SimpleItemSetDomain InterpolationModeDomain; typedef AbstractPropertyModel AbstractInterpolationModeModel; irisGetMacro(InterpolationModeModel, AbstractInterpolationModeModel *) void SetParentModel(GlobalUIModel *model); /** Reset to default (no scaling) */ void Reset(); /** Accept the user's changes */ void Accept(); /** Availabel quick presets */ enum ResamplePreset { SUPER_2, SUB_2, SUPER_ISO, SUB_ISO }; void ApplyPreset(ResamplePreset preset); protected: SnakeROIResampleModel(); virtual ~SnakeROIResampleModel() {} GlobalUIModel *m_Parent; AbstractPropertyModel *m_ROISettingsModel; SmartPtr m_InputSpacingModel[3]; bool GetInputSpacingValueAndRange(int index, double &value, NumericValueRange *domain); SmartPtr m_InputDimensionsModel[3]; bool GetInputDimensionsValueAndRange(int index, unsigned int &value, NumericValueRange *domain); SmartPtr m_OutputSpacingModel[3]; bool GetOutputSpacingValueAndRange(int index, double &value, NumericValueRange *domain); void SetOutputSpacingValue(int index, double value); SmartPtr m_OutputDimensionsModel[3]; bool GetOutputDimensionsValueAndRange(int index, unsigned int &value, NumericValueRange *domain); void SetOutputDimensionsValue(int index, unsigned int value); SmartPtr m_FixedAspectRatioModel; void ComputeCachedDomains(); void EnforceAspectRatio(int source_idx); virtual void OnUpdate(); // Cached information about the ROI. This is where this model stores its // state until the user says 'ok' Vector3ui m_ResampleDimensions; // Cached domain values - to avoid recomputing all the time NumericValueRange m_SpacingDomain[3]; NumericValueRange m_DimensionsDomain[3]; // Map for interpolation modes InterpolationModeDomain m_InterpolationModeDomain; // Model for the interpolation modes typedef ConcretePropertyModel ConcreteInterpolationModeModel; SmartPtr m_InterpolationModeModel; }; #endif // SNAKEROIRESAMPLEMODEL_H itksnap-3.4.0/GUI/Model/SnakeWizardModel.cxx000066400000000000000000001476141263013355200206170ustar00rootroot00000000000000#include "SnakeWizardModel.h" #include "GlobalUIModel.h" #include "IRISException.h" #include "IRISApplication.h" #include "GlobalState.h" #include "GenericImageData.h" #include "SNAPImageData.h" #include "SmoothBinaryThresholdImageFilter.h" #include "EdgePreprocessingImageFilter.h" #include "ColorMap.h" #include "SlicePreviewFilterWrapper.h" #include "UnsupervisedClustering.h" #include "PreprocessingFilterConfigTraits.h" #include "GMMClassifyImageFilter.h" #include "RFClassificationEngine.h" #include "RandomForestClassifier.h" #include "RandomForestClassifyImageFilter.h" #include "NumericPropertyToggleAdaptor.h" SnakeWizardModel::SnakeWizardModel() { // Set up the child models m_ThresholdUpperModel = wrapGetterSetterPairAsProperty( this, &Self::GetThresholdUpperValueAndRange, &Self::SetThresholdUpperValue, ThresholdSettingsUpdateEvent(), ThresholdSettingsUpdateEvent()); m_ThresholdLowerModel = wrapGetterSetterPairAsProperty( this, &Self::GetThresholdLowerValueAndRange, &Self::SetThresholdLowerValue, ThresholdSettingsUpdateEvent(), ThresholdSettingsUpdateEvent()); m_ThresholdSmoothnessModel = wrapGetterSetterPairAsProperty( this, &Self::GetThresholdSmoothnessValueAndRange, &Self::SetThresholdSmoothnessValue, ThresholdSettingsUpdateEvent(), ThresholdSettingsUpdateEvent()); m_ThresholdModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetThresholdModeValue, &Self::SetThresholdModeValue, ThresholdSettingsUpdateEvent(), ThresholdSettingsUpdateEvent()); m_ThresholdActiveLayerModel = wrapGetterSetterPairAsProperty( this, &Self::GetThresholdActiveLayerValueAndRange, &Self::SetThresholdActiveLayerValue, ThresholdSettingsUpdateEvent(), ThresholdSettingsUpdateEvent()); m_ThresholdActiveScalarRepModel = wrapGetterSetterPairAsProperty( this, &Self::GetThresholdActiveScalarRepValueAndRange, &Self::SetThresholdActiveScalarRepValue, ThresholdSettingsUpdateEvent(), ThresholdSettingsUpdateEvent()); m_PreviewModel = wrapGetterSetterPairAsProperty( this, &Self::GetPreviewValue, &Self::SetPreviewValue); m_BlueWhiteSpeedModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetBlueWhiteSpeedModeValue, &Self::SetBlueWhiteSpeedModeValue); m_RedTransparentSpeedModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetRedTransparentSpeedModeValue, &Self::SetRedTransparentSpeedModeValue); // TODO: which events from the parent model should be rebroadcast by the // preview model? // EdgePreprocessingSettingsUpdateEvent(), // EdgePreprocessingSettingsUpdateEvent() m_EdgePreprocessingSigmaModel = wrapGetterSetterPairAsProperty( this, &Self::GetEdgePreprocessingSigmaValueAndRange, &Self::SetEdgePreprocessingSigmaValue, EdgePreprocessingSettingsUpdateEvent(), EdgePreprocessingSettingsUpdateEvent()); m_EdgePreprocessingKappaModel = wrapGetterSetterPairAsProperty( this, &Self::GetEdgePreprocessingKappaValueAndRange, &Self::SetEdgePreprocessingKappaValue, EdgePreprocessingSettingsUpdateEvent(), EdgePreprocessingSettingsUpdateEvent()); m_EdgePreprocessingExponentModel = wrapGetterSetterPairAsProperty( this, &Self::GetEdgePreprocessingExponentValueAndRange, &Self::SetEdgePreprocessingExponentValue, EdgePreprocessingSettingsUpdateEvent(), EdgePreprocessingSettingsUpdateEvent()); m_SnakeTypeModel = wrapGetterSetterPairAsProperty( this, &Self::GetSnakeTypeValueAndRange, &Self::SetSnakeTypeValue); // Preprocessing mode model initialization m_PreprocessingModeModel = wrapGetterSetterPairAsProperty( this, &Self::GetPreprocessingModeValueAndRange, &Self::SetPreprocessingModeValue); m_ActiveBubbleModel = wrapGetterSetterPairAsProperty( this, &Self::GetActiveBubbleValue, &Self::SetActiveBubbleValue, ActiveBubbleUpdateEvent()); m_BubbleRadiusModel = wrapGetterSetterPairAsProperty( this, &Self::GetBubbleRadiusValueAndRange, &Self::SetBubbleRadiusValue, BubbleDefaultRadiusUpdateEvent(), BubbleDefaultRadiusUpdateEvent()); m_StepSizeModel = NewRangedConcreteProperty(1, 1, 100, 1); // Need to define a null setter function void (Self::*nullsetter)(int) = NULL; m_EvolutionIterationModel = wrapGetterSetterPairAsProperty( this, &Self::GetEvolutionIterationValue, nullsetter, EvolutionIterationEvent()); m_NumberOfClustersModel = wrapGetterSetterPairAsProperty( this, &Self::GetNumberOfClustersValueAndRange, &Self::SetNumberOfClustersValue, GMMModifiedEvent(), GMMModifiedEvent()); m_NumberOfGMMSamplesModel = wrapGetterSetterPairAsProperty( this, &Self::GetNumberOfGMMSamplesValueAndRange, &Self::SetNumberOfGMMSamplesValue, GMMModifiedEvent(), GMMModifiedEvent()); m_ForegroundClusterModel = wrapGetterSetterPairAsProperty( this, &Self::GetForegroundClusterValueAndRange, &Self::SetForegroundClusterValue, GMMModifiedEvent(), GMMModifiedEvent()); m_ForestSizeModel = wrapGetterSetterPairAsProperty( this, &Self::GetForestSizeValueAndRange, &Self::SetForestSizeValue, RFClassifierModifiedEvent(), RFClassifierModifiedEvent()); m_TreeDepthModel = wrapGetterSetterPairAsProperty( this, &Self::GetTreeDepthValueAndRange, &Self::SetTreeDepthValue, RFClassifierModifiedEvent(), RFClassifierModifiedEvent()); m_ClassifierPatchRadiusModel = wrapGetterSetterPairAsProperty( this, &Self::GetClassifierPatchRadiusValueAndRange, &Self::SetClassifierPatchRadiusValue, RFClassifierModifiedEvent(), RFClassifierModifiedEvent()); m_ClassifierUsePatchModel = NewNumericPropertyToggleAdaptor(m_ClassifierPatchRadiusModel.GetPointer(), 0, 2); m_ClassifierUseCoordinatesModel = wrapGetterSetterPairAsProperty( this, &Self::GetClassifierUseCoordinatesValue, &Self::SetClassifierUseCoordinatesValue, RFClassifierModifiedEvent(), RFClassifierModifiedEvent()); m_ClassifierBiasModel = wrapGetterSetterPairAsProperty( this, &Self::GetClassifierBiasValueAndRange, &Self::SetClassifierBiasValue, RFClassifierModifiedEvent(), RFClassifierModifiedEvent()); // Set up the more complex model governing classifier weights m_ClassifierLabelForegroundModel = wrapGetterSetterPairAsProperty( this, &Self::GetClassifierLabelForegroundValueAndRange, &Self::SetClassifierLabelForegroundValue, RFClassifierModifiedEvent(), RFClassifierModifiedEvent()); // The domain of the foreground cluster model depends on the number of clusters m_ForegroundClusterModel->Rebroadcast( m_NumberOfClustersModel, ValueChangedEvent(), DomainChangedEvent()); m_ClusterPlottedComponentModel = ComponentIndexModel::New(); this->UpdateClusterPlottedComponentModel(); m_InteractionMode = MODE_NONE; } void SnakeWizardModel::SetParentModel(GlobalUIModel *model) { m_Parent = model; m_Driver = m_Parent->GetDriver(); m_GlobalState = m_Driver->GetGlobalState(); // Layer changes are rebroadcast as model changes, causing all child // models to update themselves. Rebroadcast(m_Driver, LayerChangeEvent(), ModelUpdateEvent()); // Model update events are the "big events", and are rebroadcast // as the specialized events as well. Rebroadcast(this, ModelUpdateEvent(), ThresholdSettingsUpdateEvent()); // Changes to the threshold settings are rebroadcast as own own events Rebroadcast(m_Driver, WrapperProcessingSettingsChangeEvent(), ThresholdSettingsUpdateEvent()); // Changes to the preview pipeline (preview status) are broadcast as events Rebroadcast(m_Driver->GetPreprocessingFilterPreviewer(PREPROCESS_THRESHOLD), itk::ModifiedEvent(), ThresholdSettingsUpdateEvent()); // Repeat the same code for the edge preprocessing Rebroadcast(this, ModelUpdateEvent(), EdgePreprocessingSettingsUpdateEvent()); Rebroadcast(m_Driver->GetEdgePreprocessingSettings(), itk::ModifiedEvent(), EdgePreprocessingSettingsUpdateEvent()); Rebroadcast(m_Driver->GetPreprocessingFilterPreviewer(PREPROCESS_EDGE), itk::ModifiedEvent(), EdgePreprocessingSettingsUpdateEvent()); Rebroadcast(m_Driver->GetPreprocessingFilterPreviewer(PREPROCESS_GMM), itk::ModifiedEvent(), GMMModifiedEvent()); Rebroadcast(m_Driver->GetPreprocessingFilterPreviewer(PREPROCESS_RF), itk::ModifiedEvent(), RFClassifierModifiedEvent()); // Changes to the snake mode are cast as model update events Rebroadcast(m_GlobalState->GetSnakeTypeModel(), ValueChangedEvent(), ModelUpdateEvent()); // We also need to rebroadcast these events as state change events Rebroadcast(this, ThresholdSettingsUpdateEvent(), StateMachineChangeEvent()); Rebroadcast(this, EdgePreprocessingSettingsUpdateEvent(), StateMachineChangeEvent()); Rebroadcast(this, ModelUpdateEvent(), StateMachineChangeEvent()); Rebroadcast(this, ActiveBubbleUpdateEvent(), StateMachineChangeEvent()); Rebroadcast(this, RFClassifierModifiedEvent(), StateMachineChangeEvent()); // The two appearance mode models depend on changes to the color map and // the metadata of the speed image wrapper m_BlueWhiteSpeedModeModel->Rebroadcast( m_Driver, WrapperChangeEvent(), ValueChangedEvent()); m_RedTransparentSpeedModeModel->Rebroadcast( m_Driver, WrapperChangeEvent(), ValueChangedEvent()); m_ClassifierLabelForegroundModel->Rebroadcast( m_Driver->GetColorLabelTable(), SegmentationLabelChangeEvent(), DomainChangedEvent()); } bool SnakeWizardModel::CheckState(SnakeWizardModel::UIState state) { ThresholdSettings *ts = this->GetThresholdSettings(); switch(state) { case UIF_BUBBLE_MODE: return m_InteractionMode == MODE_BUBBLES; case UIF_THESHOLDING_ENABLED: return AreThresholdModelsActive(); case UIF_LOWER_THRESHOLD_ENABLED: return ts && ts->IsLowerThresholdEnabled(); case UIF_UPPER_THRESHOLD_ENABLED: return ts && ts->IsUpperThresholdEnabled(); case UIF_EDGEPROCESSING_ENABLED: return AreEdgePreprocessingModelsActive(); case UIF_CLASSIFIER_TRAINED: return IsClassifierTrained(); case UIF_CAN_GENERATE_SPEED: return CanGenerateSpeedVolume(); case UIF_SPEED_AVAILABLE: return m_GlobalState->GetSpeedValid(); case UIF_PREPROCESSING_ACTIVE: return m_Driver->GetPreprocessingMode() != PREPROCESS_NONE; case UIF_BUBBLE_SELECTED: return m_GlobalState->GetActiveBubble() >= 0; case UIF_INITIALIZATION_VALID: return m_GlobalState->GetSnakeInitializedWithManualSegmentation() || m_Driver->GetBubbleArray().size() > 0; } return false; } void SnakeWizardModel::OnUpdate() { // If there is a change in available layers, we must rebuild the list // of available components. if(m_EventBucket->HasEvent(LayerChangeEvent())) { m_ComponentInfo.clear(); LayerIterator it = m_Driver->GetSNAPImageData()->GetLayers( MAIN_ROLE | OVERLAY_ROLE); for(; !it.IsAtEnd(); ++it) { if(VectorImageWrapperBase *viw = it.GetLayerAsVector()) { for(int comp = 0; comp < it.GetLayerAsVector()->GetNumberOfComponents(); ++comp) { ComponentInfo ci; ci.ImageWrapper = viw; ci.ComponentWrapper = viw->GetScalarRepresentation( SCALAR_REP_COMPONENT, comp); ci.ComponentIndex = comp; m_ComponentInfo.push_back(ci); } } else { ComponentInfo ci; ci.ImageWrapper = ci.ComponentWrapper = it.GetLayerAsScalar(); ci.ComponentIndex = 0; m_ComponentInfo.push_back(ci); } } this->UpdateClusterPlottedComponentModel(); } } ScalarImageWrapperBase *SnakeWizardModel::GetActiveScalarLayer(PreprocessingMode mode) { return m_Driver->GetPreprocessingFilterPreviewer(mode)->GetActiveScalarLayer(); } void SnakeWizardModel::OnBubbleModeBack() { SetInteractionMode(MODE_PREPROCESSING); // Set the current preprocessing mode. PreprocessingMode lastMode = m_GlobalState->GetLastUsedPreprocessingMode(); m_PreprocessingModeModel->SetValue(lastMode); } void SnakeWizardModel::UpdateClusterPlottedComponentModel() { this->m_ClusterPlottedComponentModel->SetValue(0); ComponentDomain cd; for(int i = 0; i < m_ComponentInfo.size(); i++) { std::ostringstream oss; oss << (i+1) << " : " << m_ComponentInfo[i].ImageWrapper->GetNickname(); cd[i] = oss.str(); } this->m_ClusterPlottedComponentModel->SetDomain(cd); } /* bool SnakeWizardModel::GetForegroundClassColorLabelValueAndRange( LabelType &value, SnakeWizardModel::ForegroundClassDomain *range) { // Must have a classification engine RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); if(!rfe) return false; // Must have a classifier RandomForestClassifier *rfc = rfe->GetClassifier(); if(!rfc) return false; // The classifier must be valid (2 or more classes) if(!rfc->IsValidClassifier()) return false; // Set the class label to the one stored in the classifier value = rfc->GetForegroundClassLabel(); // Set the range to the correct range if(range) range->SetWrappedMap(&m_ActiveClasses); return true; } void SnakeWizardModel::SetForegroundClassColorLabelValue(LabelType value) { RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); assert(rfe); RandomForestClassifier *rfc = rfe->GetClassifier(); assert(rfc); rfc->SetForegroundClassLabel(value); InvokeEvent(RFClassifierModifiedEvent()); // TODO: this is a hack! TagRFPreprocessingFilterModified(); } */ bool SnakeWizardModel::GetForestSizeValueAndRange(int &value, NumericValueRange *range) { // Must have a classification engine RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); if(!rfe) return false; value = rfe->GetForestSize(); if(range) range->Set(10, 500, 10); return true; } void SnakeWizardModel::SetForestSizeValue(int value) { RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); assert(rfe); rfe->SetForestSize(value); InvokeEvent(RFClassifierModifiedEvent()); } bool SnakeWizardModel::GetTreeDepthValueAndRange(int &value, NumericValueRange *range) { // Must have a classification engine RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); if(!rfe) return false; value = rfe->GetTreeDepth(); if(range) range->Set(10, 100, 5); return true; } void SnakeWizardModel::SetTreeDepthValue(int value) { RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); assert(rfe); rfe->SetTreeDepth(value); InvokeEvent(RFClassifierModifiedEvent()); } bool SnakeWizardModel::GetClassifierPatchRadiusValueAndRange(int &value, NumericValueRange *range) { // Must have a classification engine RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); if(!rfe) return false; // Get the value RFClassificationEngine::RadiusType radius = rfe->GetPatchRadius(); value = (int) radius[0]; if(range) range->Set(0, 4, 1); return true; } void SnakeWizardModel::SetClassifierPatchRadiusValue(int value) { RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); assert(rfe); assert(value >= 0); RFClassificationEngine::RadiusType radius; radius.Fill((unsigned int) value); rfe->SetPatchRadius(radius); InvokeEvent(RFClassifierModifiedEvent()); } bool SnakeWizardModel::GetClassifierBiasValueAndRange(double &value, NumericValueRange *range) { // Must have a classification engine RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); if(!rfe) return false; // Must have a classifier RandomForestClassifier *rfc = rfe->GetClassifier(); if(!rfc) return false; // The classifier must be valid (2 or more classes) if(!rfc->IsValidClassifier()) return false; value = rfc->GetBiasParameter(); if(range) range->Set(0.0, 1.0, 0.01); return true; } void SnakeWizardModel::SetClassifierBiasValue(double value) { RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); assert(rfe); RandomForestClassifier *rfc = rfe->GetClassifier(); assert(rfc); rfc->SetBiasParameter(value); InvokeEvent(RFClassifierModifiedEvent()); // TODO: this is a hack! TagRFPreprocessingFilterModified(); } bool SnakeWizardModel::GetClassifierUseCoordinatesValue(bool &value) { // Must have a classification engine RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); if(!rfe) return false; value = rfe->GetUseCoordinateFeatures(); return true; } void SnakeWizardModel::SetClassifierUseCoordinatesValue(bool value) { RFClassificationEngine *rfe = m_Driver->GetClassificationEngine(); assert(rfe); rfe->SetUseCoordinateFeatures(value); InvokeEvent(RFClassifierModifiedEvent()); } bool SnakeWizardModel ::GetClassifierLabelForegroundValueAndRange( ClassifierLabelForegroundMap &value, ClassifierLabelForegroundMapDomain *range) { // Get the classification engine RFClassificationEngine *rfengine = m_Driver->GetClassificationEngine(); if(!rfengine || !rfengine->GetClassifier()->IsValidClassifier()) return false; // Clear the output map value.clear(); if(range) range->clear(); // Add all of the labels RandomForestClassifier *rfc = rfengine->GetClassifier(); const RandomForestClassifier::WeightArray &wa = rfc->GetClassWeights(); for(size_t i = 0; i < wa.size(); i++) { RandomForestClassifier::MappingType::const_iterator itl = rfc->GetClassToLabelMapping().find(i); if(itl != rfc->GetClassToLabelMapping().end()) { LabelType label = itl->second; value[label] = wa[i] > 0.0; if(range) (*range)[label] = m_Driver->GetColorLabelTable()->GetColorLabel(label); } } return true; } void SnakeWizardModel ::SetClassifierLabelForegroundValue( ClassifierLabelForegroundMap value) { // Get the classification engine RFClassificationEngine *rfengine = m_Driver->GetClassificationEngine(); assert(rfengine && rfengine->GetClassifier()->IsValidClassifier()); // Check if anything was actually modified - to avoid loops bool changed = false; // Update the weight of each class RandomForestClassifier *rfc = rfengine->GetClassifier(); for(RandomForestClassifier::MappingType::const_iterator it = rfc->GetClassToLabelMapping().begin(); it != rfc->GetClassToLabelMapping().end(); ++it) { double old_weight = rfc->GetClassWeights()[it->first]; double new_weight = old_weight; ClassifierLabelForegroundMap::const_iterator it2 = value.find(it->second); if(it2 != value.end()) new_weight = it2->second ? 1.0 : -1.0; if(old_weight != new_weight) { rfc->SetClassWeight(it->first, new_weight); changed = true; } } if(changed) { // Fire the event InvokeEvent(RFClassifierModifiedEvent()); // TODO: this is a hack! TagRFPreprocessingFilterModified(); } } void SnakeWizardModel::OnBubbleModeEnter() { // When entering bubble mode, we should not use the overlay display of the // speed image, as tht clashes with bubble placement visually if(this->GetRedTransparentSpeedModeModel()->GetValue()) this->SetBlueWhiteSpeedModeValue(true); // In bubble mode, we want the main toolbar to be in crosshairs mode m_GlobalState->SetToolbarMode(CROSSHAIRS_MODE); // We are in bubble mode SetInteractionMode(MODE_BUBBLES); } bool SnakeWizardModel::AreThresholdModelsActive() { return (m_Driver->IsSnakeModeActive() && m_Driver->GetPreprocessingMode() == PREPROCESS_THRESHOLD); } bool SnakeWizardModel::AreEdgePreprocessingModelsActive() { return (m_Driver->IsSnakeModeActive() && m_Driver->GetPreprocessingMode() == PREPROCESS_EDGE); } bool SnakeWizardModel::CanGenerateSpeedVolume() { // The answer depends on the proprocessing mode switch(m_Driver->GetPreprocessingMode()) { case PREPROCESS_NONE: return false; case PREPROCESS_THRESHOLD: return true; case PREPROCESS_EDGE: return true; case PREPROCESS_GMM: return true; case PREPROCESS_RF: { RFClassificationEngine *cfe = m_Driver->GetClassificationEngine(); RandomForestClassifier *rfc = cfe->GetClassifier(); return rfc->IsValidClassifier(); } } } ScalarImageWrapperBase *SnakeWizardModel::GetSelectedScalarLayer() { // TODO: this should be set by the wizard through user interaction. // This is just a placeholder return m_Driver->GetCurrentImageData()->GetMain()->GetDefaultScalarRepresentation(); } bool SnakeWizardModel ::GetThresholdUpperValueAndRange( double &x, NumericValueRange *range) { if(!AreThresholdModelsActive()) return false; ScalarImageWrapperBase *iw = this->GetActiveScalarLayer(PREPROCESS_THRESHOLD); ThresholdSettings *ts = this->GetThresholdSettings(); // The thresholds are stored in internal image representation, but are // presented to the user in native image representation. x = iw->GetNativeIntensityMapping()->MapInternalToNative(ts->GetUpperThreshold()); if(range) { range->Minimum = iw->GetImageMinNative(); range->Maximum = iw->GetImageMaxNative(); range->StepSize = CalculatePowerOfTenStepSize(range->Minimum, range->Maximum, 1000); } return true; } bool SnakeWizardModel ::GetThresholdLowerValueAndRange( double &x, NumericValueRange *range) { if(!AreThresholdModelsActive()) return false; ScalarImageWrapperBase *iw = this->GetActiveScalarLayer(PREPROCESS_THRESHOLD); ThresholdSettings *ts = this->GetThresholdSettings(); // The thresholds are stored in internal image representation, but are // presented to the user in native image representation. x = iw->GetNativeIntensityMapping()->MapInternalToNative(ts->GetLowerThreshold()); if(range) { range->Minimum = iw->GetImageMinNative(); range->Maximum = iw->GetImageMaxNative(); range->StepSize = CalculatePowerOfTenStepSize(range->Minimum, range->Maximum, 1000); } return true; } void SnakeWizardModel ::SetThresholdUpperValue(double x) { // Map the value to internal format ScalarImageWrapperBase *iw = this->GetActiveScalarLayer(PREPROCESS_THRESHOLD); float z = (float) iw->GetNativeIntensityMapping()->MapNativeToInternal(x); // Get the current settings ThresholdSettings *ts = this->GetThresholdSettings(); if(z < ts->GetLowerThreshold()) ts->SetLowerThreshold(z); ts->SetUpperThreshold(z); } void SnakeWizardModel ::SetThresholdLowerValue(double x) { // Map the value to internal format ScalarImageWrapperBase *iw = this->GetActiveScalarLayer(PREPROCESS_THRESHOLD); float z = (float) iw->GetNativeIntensityMapping()->MapNativeToInternal(x); // Get the current settings ThresholdSettings *ts = this->GetThresholdSettings(); if(z > ts->GetUpperThreshold()) ts->SetUpperThreshold(z); ts->SetLowerThreshold(z); } bool SnakeWizardModel ::GetThresholdSmoothnessValueAndRange(double &x, NumericValueRange *range) { if(!AreThresholdModelsActive()) return false; ThresholdSettings *ts = this->GetThresholdSettings(); x = ts->GetSmoothness(); if(range) range->Set(0, 10, 0.1); return true; } void SnakeWizardModel::SetThresholdSmoothnessValue(double x) { ThresholdSettings *ts = this->GetThresholdSettings(); ts->SetSmoothness(x); } bool SnakeWizardModel::GetThresholdModeValue(ThresholdSettings::ThresholdMode &x) { if(!AreThresholdModelsActive()) return false; ThresholdSettings *ts = this->GetThresholdSettings(); x = ts->GetThresholdMode(); return true; } void SnakeWizardModel::SetThresholdModeValue(ThresholdSettings::ThresholdMode x) { ThresholdSettings *ts = this->GetThresholdSettings(); ts->SetThresholdMode(x); } bool SnakeWizardModel::GetThresholdActiveLayerValueAndRange(unsigned long &value, SnakeWizardModel::LayerSelectionDomain *range) { // Get the currently selected layer ScalarImageWrapperBase *active = GetActiveScalarLayer(PREPROCESS_THRESHOLD); if(active) { // Figure out the top level wrapper for the selected component ImageWrapperBase *parent = active->GetParentWrapper(); if(parent) value = parent->GetUniqueId(); else value = active->GetUniqueId(); // Set up the domain if(range) { range->clear(); for(LayerIterator it = m_Driver->GetSNAPImageData()->GetLayers( MAIN_ROLE | OVERLAY_ROLE); !it.IsAtEnd(); ++it) { (*range)[it.GetLayer()->GetUniqueId()] = it.GetLayer()->GetNickname(); } } return true; } return false; } void SnakeWizardModel::SetThresholdActiveLayerValue(unsigned long value) { // Find the layer ImageWrapperBase *layer = m_Driver->GetSNAPImageData()->FindLayer(value, false); if(layer) { m_Driver->GetPreprocessingFilterPreviewer(PREPROCESS_THRESHOLD)->SetActiveScalarLayer( layer->GetDefaultScalarRepresentation()); } } bool SnakeWizardModel ::GetThresholdActiveScalarRepValueAndRange( SnakeWizardModel::LayerScalarRepIndex &value, SnakeWizardModel::ScalarRepSelectionDomain *range) { // Get the currently selected scalar layer ScalarImageWrapperBase *active = GetActiveScalarLayer(PREPROCESS_THRESHOLD); VectorImageWrapperBase *parent = (active) ? dynamic_cast(active->GetParentWrapper()) : NULL; // If the scalar layer is its own parent, there should be nothing selected if(active && parent && parent->GetNumberOfComponents() > 1) { // Now we must figure out the index of the layer in the parent if(parent->FindScalarRepresentation(active, value.first, value.second)) { // Configure the domain if(range) { range->clear(); for(int sr = SCALAR_REP_COMPONENT; sr < NUMBER_OF_SCALAR_REPS; sr++) { (*range)[std::make_pair(SCALAR_REP_MAGNITUDE, 0)] = "Magnitude of Components"; (*range)[std::make_pair(SCALAR_REP_MAX, 0)] = "Maximum Component"; (*range)[std::make_pair(SCALAR_REP_AVERAGE, 0)] = "Average Component"; for(int k = 0; k < parent->GetNumberOfComponents(); k++) { std::ostringstream oss; oss << "Component " << (k+1); (*range)[std::make_pair(SCALAR_REP_COMPONENT, k)] = oss.str(); } } } return true; } } return false; } void SnakeWizardModel ::SetThresholdActiveScalarRepValue(LayerScalarRepIndex value) { // Get the currently selected scalar layer ScalarImageWrapperBase *active =GetActiveScalarLayer(PREPROCESS_THRESHOLD); VectorImageWrapperBase *parent = (active) ? dynamic_cast(active->GetParentWrapper()) : NULL; // The parent should be not null! assert(parent); // Set the component within the parent ScalarImageWrapperBase *comp = parent->GetScalarRepresentation(value.first, value.second); m_Driver->GetPreprocessingFilterPreviewer(PREPROCESS_THRESHOLD)->SetActiveScalarLayer(comp); } bool SnakeWizardModel::GetPreviewValue(bool &value) { PreprocessingMode mode = m_Driver->GetPreprocessingMode(); if(mode != PREPROCESS_NONE) { value = m_Driver->GetPreprocessingFilterPreviewer(mode)->IsPreviewMode(); return true; } return false; } void SnakeWizardModel::SetPreviewValue(bool value) { PreprocessingMode mode = m_Driver->GetPreprocessingMode(); if(mode != PREPROCESS_NONE) { m_Driver->GetPreprocessingFilterPreviewer(mode)->SetPreviewMode(value); } } bool SnakeWizardModel::GetBlueWhiteSpeedModeValue(bool &value) { PreprocessingMode mode = m_Driver->GetPreprocessingMode(); if(mode != PREPROCESS_NONE) { SpeedImageWrapper *speed = m_Driver->GetSNAPImageData()->GetSpeed(); if(speed->GetColorMap()->GetSystemPreset() == ColorMap::COLORMAP_SPEED && !speed->IsSticky() && speed->GetAlpha() == 1.0) { value = true; } else { value = false; } return true; } return false; } void SnakeWizardModel::SetBlueWhiteSpeedModeValue(bool value) { SpeedImageWrapper *speed = m_Driver->GetSNAPImageData()->GetSpeed(); if(value) { speed->GetColorMap()->SetToSystemPreset(ColorMap::COLORMAP_SPEED); speed->SetSticky(false); speed->SetAlpha(1.0); } } bool SnakeWizardModel::GetRedTransparentSpeedModeValue(bool &value) { PreprocessingMode mode = m_Driver->GetPreprocessingMode(); if(mode != PREPROCESS_NONE) { SpeedImageWrapper *speed = m_Driver->GetSNAPImageData()->GetSpeed(); if(speed->GetColorMap()->GetSystemPreset() == ColorMap::COLORMAP_SPEED_OVERLAY && speed->IsSticky() && speed->GetAlpha() == 0.5) { value = true; } else { value = false; } return true; } return false; } void SnakeWizardModel::SetRedTransparentSpeedModeValue(bool value) { SpeedImageWrapper *speed = m_Driver->GetSNAPImageData()->GetSpeed(); if(value) { speed->GetColorMap()->SetToSystemPreset(ColorMap::COLORMAP_SPEED_OVERLAY); speed->SetSticky(true); speed->SetAlpha(0.5); } } void SnakeWizardModel ::EvaluateThresholdFunction(unsigned int n, float *x, float *y) { assert(m_Driver->IsSnakeModeActive()); ScalarImageWrapperBase *grey = this->GetActiveScalarLayer(PREPROCESS_THRESHOLD); ThresholdSettings *ts = this->GetThresholdSettings(); SpeedImageWrapper *speed = m_Driver->GetSNAPImageData()->GetSpeed(); double imin = grey->GetImageMinAsDouble(); double imax = grey->GetImageMaxAsDouble(); SmoothBinaryThresholdFunctor functor; functor.SetParameters(ts, imin, imax); for(int i = 0; i < n; i++) { float t = i * 1.0 / (n - 1); float x_internal = imin + t * (imax - imin); x[i] = grey->GetNativeIntensityMapping()->MapInternalToNative(x_internal); y[i] = speed->GetNativeIntensityMapping()->MapInternalToNative(functor(x_internal)); // We are actually plotting the threshold function itself, not the speed // function, so we will scale further to the range [0 1] y[i] = 0.5 * (y[i] + 1.0); } } bool SnakeWizardModel ::GetEdgePreprocessingSigmaValueAndRange( double &x, NumericValueRange *range) { if(!AreEdgePreprocessingModelsActive()) return false; EdgePreprocessingSettings *eps = m_Driver->GetEdgePreprocessingSettings(); x = eps->GetGaussianBlurScale(); if(range) range->Set(0.1, 3, 0.01); return true; } void SnakeWizardModel ::SetEdgePreprocessingSigmaValue(double x) { EdgePreprocessingSettings *eps = m_Driver->GetEdgePreprocessingSettings(); eps->SetGaussianBlurScale(x); } bool SnakeWizardModel ::GetEdgePreprocessingKappaValueAndRange( double &x, NumericValueRange *range) { if(!AreEdgePreprocessingModelsActive()) return false; EdgePreprocessingSettings *eps = m_Driver->GetEdgePreprocessingSettings(); x = eps->GetRemappingSteepness(); if(range) range->Set(0.001, 0.2, 0.001); return true; } void SnakeWizardModel ::SetEdgePreprocessingKappaValue(double x) { EdgePreprocessingSettings *eps = m_Driver->GetEdgePreprocessingSettings(); eps->SetRemappingSteepness(x); } bool SnakeWizardModel ::GetEdgePreprocessingExponentValueAndRange( double &x, NumericValueRange *range) { if(!AreEdgePreprocessingModelsActive()) return false; EdgePreprocessingSettings *eps = m_Driver->GetEdgePreprocessingSettings(); x = eps->GetRemappingExponent(); if(range) range->Set(1, 4, 0.01); return true; } void SnakeWizardModel ::SetEdgePreprocessingExponentValue(double x) { EdgePreprocessingSettings *eps = m_Driver->GetEdgePreprocessingSettings(); eps->SetRemappingExponent(x); } void SnakeWizardModel ::EvaluateEdgePreprocessingFunction(unsigned int n, float *x, float *y) { assert(m_Driver->IsSnakeModeActive()); EdgePreprocessingSettings *eps = m_Driver->GetEdgePreprocessingSettings(); ScalarImageWrapperBase *grey = this->GetSelectedScalarLayer(); SpeedImageWrapper *speed = m_Driver->GetSNAPImageData()->GetSpeed(); // Get the range of gradient magnitude in native units double xlim = grey->GetImageGradientMagnitudeUpperLimitNative(); EdgeRemappingFunctor functor; functor.SetParameters(0, xlim, eps->GetRemappingExponent(), eps->GetRemappingSteepness()); for(int i = 0; i < n; i++) { float t = i * 1.0 / (n - 1); float x_internal = t * xlim; x[i] = x_internal; y[i] = speed->GetNativeIntensityMapping()->MapInternalToNative(functor(x_internal)); } } void SnakeWizardModel::ApplyPreprocessing() { // Compute the speed image m_Driver->ApplyCurrentPreprocessingModeToSpeedVolume(m_Parent->GetProgressCommand()); // Invoke an event so we get a screen update InvokeEvent(ModelUpdateEvent()); } bool SnakeWizardModel::GetSnakeTypeValueAndRange( SnakeType &value, GlobalState::SnakeTypeDomain *range) { return m_GlobalState->GetSnakeTypeModel()->GetValueAndDomain(value, range); } void SnakeWizardModel::SetSnakeTypeValue(SnakeType value) { m_Driver->SetSnakeMode(value); } bool SnakeWizardModel::GetPreprocessingModeValueAndRange(PreprocessingMode &value, SnakeWizardModel::PreprocessingModeDomain *range) { PreprocessingMode mode = m_Driver->GetPreprocessingMode(); if(mode == PREPROCESS_NONE) return false; value = mode; if(range) { (*range)[PREPROCESS_THRESHOLD] = "Thresholding"; (*range)[PREPROCESS_EDGE] = "Edge Attraction"; (*range)[PREPROCESS_GMM] = "Clustering"; (*range)[PREPROCESS_RF] = "Classification"; } return true; } void SnakeWizardModel::SetPreprocessingModeValue(PreprocessingMode value) { m_Driver->EnterPreprocessingMode(value); InvokeEvent(ModelUpdateEvent()); InvokeEvent(GMMModifiedEvent()); InvokeEvent(RFClassifierModifiedEvent()); } void SnakeWizardModel::CompletePreprocessing() { // If we are in classification pre-segmentation mode, set the active drawing label // to match the foreground class - otherwise it's confusing to the user if(m_Driver->GetPreprocessingMode() == PREPROCESS_RF) m_Parent->GetGlobalState()->SetDrawingColorLabel( this->GetClassiferFirstForegroundLabel()); // Disconnect preview pipeline m_Driver->EnterPreprocessingMode(PREPROCESS_NONE); InvokeEvent(ModelUpdateEvent()); } bool SnakeWizardModel ::GetActiveBubbleValue(int &value) { // This is irrelevant when the snake is inactive if(!m_Driver->IsSnakeModeActive()) return false; // This may be -1 if no bubbles are selected value = m_GlobalState->GetActiveBubble(); return true; } void SnakeWizardModel ::SetActiveBubbleValue(int value) { m_GlobalState->SetActiveBubble(value); InvokeEvent(ActiveBubbleUpdateEvent()); } void SnakeWizardModel::AddBubbleAtCursor() { // Create a new bubble, using the default radius value Bubble bub; bub.center = to_int(m_Driver->GetCursorPosition()); bub.radius = m_BubbleRadiusDefaultValue; // Add the bubble to the global state m_Driver->GetBubbleArray().push_back(bub); // Set the bubble's position m_GlobalState->SetActiveBubble(m_Driver->GetBubbleArray().size() - 1); // Update the bubble list in the GUI InvokeEvent(ActiveBubbleUpdateEvent()); InvokeEvent(BubbleListUpdateEvent()); InvokeEvent(BubbleDefaultRadiusUpdateEvent()); } void SnakeWizardModel::RemoveBubbleAtCursor() { int ibub = m_GlobalState->GetActiveBubble(); IRISApplication::BubbleArray &ba = m_Driver->GetBubbleArray(); if(ibub >= 0 && ibub < (int) ba.size()) { // Remove the bubble from the global state ba.erase(ba.begin() + ibub); // Update the active bubble if(ibub == (int) ba.size()) m_GlobalState->SetActiveBubble(ibub - 1); // Update the bubble list in the GUI InvokeEvent(ActiveBubbleUpdateEvent()); InvokeEvent(BubbleListUpdateEvent()); InvokeEvent(BubbleDefaultRadiusUpdateEvent()); } else { throw IRISException("Invalid bubble index %d selected for removal.", ibub); } } bool SnakeWizardModel::UpdateBubble(int index, Bubble bubble) { if(m_Driver->GetCurrentImageData()->GetImageRegion().IsInside( to_itkIndex(bubble.center))) { m_Driver->GetBubbleArray()[index] = bubble; InvokeEvent(BubbleDefaultRadiusUpdateEvent()); return true; } return false; } void SnakeWizardModel::OnSnakeModeEnter() { // Initialize the image data m_Driver->InitializeSNAPImageData( m_Driver->GetGlobalState()->GetSegmentationROISettings(), m_Parent->GetProgressCommand()); m_Driver->SetCurrentImageDataToSNAP(); // Some preparatory stuff this->ComputeBubbleRadiusDefaultAndRange(); // Reset the bubbles m_Driver->GetBubbleArray().clear(); m_GlobalState->UnsetActiveBubble(); // We begin in preprocessing mode SetInteractionMode(MODE_PREPROCESSING); // Set the current preprocessing mode. PreprocessingMode lastMode = m_GlobalState->GetLastUsedPreprocessingMode(); m_PreprocessingModeModel->SetValue(lastMode); } void SnakeWizardModel::ComputeBubbleRadiusDefaultAndRange() { // Set bubble radius range according to volume dimensions (world dimensions) Vector3ui size = m_Driver->GetSNAPImageData()->GetVolumeExtents(); Vector3d voxdims = m_Driver->GetSNAPImageData()->GetImageSpacing(); double mindim = vector_multiply_mixed(voxdims, size).min_value(); // The largest value of the bubble radius is mindim / 2 double xBubbleMax = 0.5 * mindim ; // The unit step should be equal or smaller than the smallest voxel edge length // divided by two, and should be a power of 10. Since FLTK accepts rational step // size, we compute it as a ratio two numbers double xMinVoxelEdge = 0.5 * voxdims.min_value(); int xBubbleStepA = 1, xBubbleStepB = 1; int xLogVoxelEdge = (int) floor(log10(xMinVoxelEdge)); if(xLogVoxelEdge > 0) xBubbleStepA = (int)(0.5 + pow(10.0, xLogVoxelEdge)); else if(xLogVoxelEdge < 0) xBubbleStepB = (int)(0.5 + pow(10.0, -xLogVoxelEdge)); // It is likely however that 0.1 is not an appropriate step size when min // voxel size is 0.99, so we try 0.5 and 0.2 as candidates if(xBubbleStepA * 5.0 / xBubbleStepB <= xMinVoxelEdge) xBubbleStepA *= 5; else if(xBubbleStepA * 2.0 / xBubbleStepB <= xMinVoxelEdge) xBubbleStepA *= 2; // Set the bubble min value double xBubbleStep = xBubbleStepA * 1.0 / xBubbleStepB; double xBubbleMin = xBubbleStep; // Set the default value so that it falls on the step boundary m_BubbleRadiusDefaultValue = floor(0.25 * xBubbleMax / xBubbleStep) * xBubbleStep; // Set the domain and value for the radius slider m_BubbleRadiusDomain.Set(xBubbleMin, xBubbleMax, xBubbleStep); // Let the GUI know that the values have changed InvokeEvent(BubbleDefaultRadiusUpdateEvent()); } void SnakeWizardModel::SetInteractionMode(SnakeWizardModel::InteractionMode mode) { m_InteractionMode = mode; InvokeEvent(StateMachineChangeEvent()); } bool SnakeWizardModel ::GetBubbleRadiusValueAndRange( double &value, NumericValueRange *range) { // Bail out if not in snake mode if(!m_Driver->IsSnakeModeActive()) return false; int activeBubble; if(m_ActiveBubbleModel->GetValueAndDomain(activeBubble, NULL) && activeBubble >= 0) { // If a bubble is currently selected, we change the value of the // currently selected bubble value = m_Driver->GetBubbleArray()[activeBubble].radius; } else { // Otherwise, we return the default value computed for this image value = m_BubbleRadiusDefaultValue; } // Set the range as well if(range) *range = m_BubbleRadiusDomain; return true; } void SnakeWizardModel ::SetBubbleRadiusValue(double value) { int activeBubble; if(m_ActiveBubbleModel->GetValueAndDomain(activeBubble, NULL) && activeBubble >= 0) { // There is an active bubble - change its radius m_Driver->GetBubbleArray()[activeBubble].radius = value; InvokeEvent(BubbleListUpdateEvent()); } // Always store as the default value m_BubbleRadiusDefaultValue = value; // Radius has updated InvokeEvent(BubbleDefaultRadiusUpdateEvent()); } void SnakeWizardModel::OnEvolutionPageEnter() { // We are no longer bubble SetInteractionMode(MODE_EVOLUTION); // Initialize the segmentation if(!m_Driver->InitializeActiveContourPipeline()) { throw IRISException("Failed to initialize the active contour. " "Check that the initialization bubbles are " "present and cover the image region."); } } bool SnakeWizardModel::PerformEvolutionStep() { // Do the segmentation step! m_Driver->GetSNAPImageData()->RunSegmentation(m_StepSizeModel->GetValue()); // Fire an event InvokeEvent(EvolutionIterationEvent()); // Return the status. In the future, this should check for convergence, but // the way the parallel sparse filter is behaving in the more recent versions // of ITK, the RMSChange flag is just meaningless. return false; } int SnakeWizardModel::GetEvolutionIterationValue() { if(m_Driver->IsSnakeModeActive() && m_Driver->GetSNAPImageData()->IsSegmentationActive()) { return m_Driver->GetSNAPImageData()->GetElapsedSegmentationIterations(); } else return 0; } ThresholdSettings *SnakeWizardModel::GetThresholdSettings() { // Get the layer currently being thresholded ScalarImageWrapperBase *layer = GetActiveScalarLayer(PREPROCESS_THRESHOLD); // Get the threshold settings from that layer return layer ? dynamic_cast(layer->GetUserData("ThresholdSettings")) : NULL; } void SnakeWizardModel::OnEvolutionPageBack() { // Abort the segmentation (stops segmentation and resets the snake SNAPImageData *sid = m_Driver->GetSNAPImageData(); if(sid->IsSegmentationActive()) { sid->TerminateSegmentation(); sid->ClearSnake(); } SetInteractionMode(MODE_BUBBLES); } void SnakeWizardModel::OnEvolutionPageFinish() { // Stop the segmentation pipeline if(m_Driver->GetSNAPImageData()->IsSegmentationActive()) m_Driver->GetSNAPImageData()->TerminateSegmentation(); // Update IRIS with SNAP images m_Driver->UpdateIRISWithSnapImageData(NULL); // Set an undo point in IRIS, not SNAP! m_Driver->GetIRISImageData()->StoreUndoPoint("Automatic Segmentation"); // Return to IRIS mode m_Driver->SetCurrentImageDataToIRIS(); m_Driver->ReleaseSNAPImageData(); } void SnakeWizardModel::OnCancelSegmentation() { // Stop the segmentation pipeline if(m_Driver->GetSNAPImageData()->IsSegmentationActive()) m_Driver->GetSNAPImageData()->TerminateSegmentation(); // Leave the preprocessing mode m_PreprocessingModeModel->SetValue(PREPROCESS_NONE); // Return to IRIS mode m_Driver->SetCurrentImageDataToIRIS(); m_Driver->ReleaseSNAPImageData(); } void SnakeWizardModel::RewindEvolution() { if(m_Driver->GetSNAPImageData()->IsSegmentationActive()) m_Driver->GetSNAPImageData()->RestartSegmentation(); // Fire an event InvokeEvent(EvolutionIterationEvent()); } bool SnakeWizardModel ::GetNumberOfClustersValueAndRange( int &value, NumericValueRange *range) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); if(uc) { value = uc->GetNumberOfClusters(); if(range) range->Set(2, 20, 1); return true; } return false; } void SnakeWizardModel ::SetNumberOfClustersValue( int value) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); assert(uc); uc->SetNumberOfClusters(value); uc->InitializeClusters(); this->TagGMMPreprocessingFilterModified(); this->InvokeEvent(GMMModifiedEvent()); } bool SnakeWizardModel::GetNumberOfGMMSamplesValueAndRange(int &value, NumericValueRange *range) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); if(uc) { value = uc->GetNumberOfSamples(); if(range) { int nvox = m_Driver->GetCurrentImageData()->GetMain()->GetNumberOfVoxels(); range->Set(std::min(nvox, 5000), nvox, 5000); } return true; } return false; } void SnakeWizardModel::SetNumberOfGMMSamplesValue(int value) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); assert(uc); uc->SetNumberOfSamples(value); uc->InitializeClusters(); this->TagGMMPreprocessingFilterModified(); this->InvokeEvent(GMMModifiedEvent()); } bool SnakeWizardModel::GetForegroundClusterValueAndRange(int &value, NumericValueRange *range) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); if(!uc) return false; // Go through the clusters, and find which cluster is marked as foreground int foreCluster = -1; for(int i = 0; i < uc->GetNumberOfClusters(); i++) { if(uc->GetMixtureModel()->IsForeground(i)) { if(foreCluster >= 0) // Oops! more than one foreground cluster! return false; else foreCluster = i; } } // Set the value value = foreCluster + 1; // Set the range if(range) { range->Set(1, uc->GetNumberOfClusters(), 1); } return true; } void SnakeWizardModel::SetForegroundClusterValue(int value) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); assert(uc); // Go through the clusters, and find which cluster is marked as foreground int foreCluster = value - 1; for(int i = 0; i < uc->GetNumberOfClusters(); i++) { if(i == foreCluster) uc->GetMixtureModel()->SetForeground(i); else uc->GetMixtureModel()->SetBackground(i); } this->TagGMMPreprocessingFilterModified(); InvokeEvent(GMMModifiedEvent()); } void SnakeWizardModel::TagGMMPreprocessingFilterModified() { // TODO: this is not the right way to do this! Make MixtureModel an itkObject // and an inout to the filter, so we don't have to update the filter itself!! // THIS IS HACKY!!! UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); typedef SlicePreviewFilterWrapper GMMPreprocessingPreviewWrapperType; GMMPreprocessingPreviewWrapperType *junk = (GMMPreprocessingPreviewWrapperType *) m_Driver->GetPreprocessingFilterPreviewer(PREPROCESS_GMM); junk->SetParameters(uc->GetMixtureModel()); } void SnakeWizardModel::TagRFPreprocessingFilterModified() { // TODO: this is not the right way to do this! Make RandomForestClassifier an itkObject // and an inout to the filter, so we don't have to update the filter itself!! // THIS IS HACKY!!! RFClassificationEngine *ce = m_Driver->GetClassificationEngine(); typedef SlicePreviewFilterWrapper RFPreprocessingPreviewWrapperType; RFPreprocessingPreviewWrapperType *junk = (RFPreprocessingPreviewWrapperType *) m_Driver->GetPreprocessingFilterPreviewer(PREPROCESS_RF); junk->SetParameters(ce->GetClassifier()); } void SnakeWizardModel::PerformClusteringIteration() { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); assert(uc); uc->Iterate(); this->InvokeEvent(GMMModifiedEvent()); TagGMMPreprocessingFilterModified(); } bool SnakeWizardModel::SetClusterForegroundState(int cluster, bool state) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); assert(uc); GaussianMixtureModel *gmm = uc->GetMixtureModel(); // Currently this implements mutually exclusive behavior if(state && !gmm->IsForeground(cluster)) { for(int i = 0; i < gmm->GetNumberOfGaussians(); i++) { if(cluster == i) gmm->SetForeground(i); else gmm->SetBackground(i); } TagGMMPreprocessingFilterModified(); this->InvokeEvent(GMMModifiedEvent()); return true; } else { return false; } } double SnakeWizardModel::GetClusterWeight(int cluster) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); GaussianMixtureModel *gmm = uc->GetMixtureModel(); return gmm->GetWeight(cluster); } bool SnakeWizardModel::SetClusterWeight(int cluster, double weight) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); assert(uc); GaussianMixtureModel *gmm = uc->GetMixtureModel(); if(weight != gmm->GetWeight(cluster)) { gmm->SetWeightAndRenormalize(cluster, weight); TagGMMPreprocessingFilterModified(); this->InvokeEvent(GMMModifiedEvent()); return true; } else return false; } double SnakeWizardModel::GetClusterNativeMean(int cluster, int component) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); GaussianMixtureModel *gmm = uc->GetMixtureModel(); ImageWrapperBase *aiw = this->GetLayerAndIndexForNthComponent(component).ImageWrapper; const AbstractNativeIntensityMapping *nim = aiw->GetNativeIntensityMapping(); return nim->MapInternalToNative(gmm->GetMean(cluster)[component]); } bool SnakeWizardModel::SetClusterNativeMean(int cluster, int component, double x) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); GaussianMixtureModel *gmm = uc->GetMixtureModel(); ImageWrapperBase *aiw = this->GetLayerAndIndexForNthComponent(component).ImageWrapper; const AbstractNativeIntensityMapping *nim = aiw->GetNativeIntensityMapping(); vnl_vector mean_raw = gmm->GetMean(cluster); double mk = nim->MapNativeToInternal(x); if(mk != mean_raw[component]) { mean_raw[component] = mk; gmm->SetMean(cluster, mean_raw); TagGMMPreprocessingFilterModified(); this->InvokeEvent(GMMModifiedEvent()); return true; } return false; } double SnakeWizardModel::GetClusterNativeCovariance(int cluster, int comp1, int comp2) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); GaussianMixtureModel *gmm = uc->GetMixtureModel(); const AbstractNativeIntensityMapping *nim1 = this->GetLayerAndIndexForNthComponent(comp1).ImageWrapper->GetNativeIntensityMapping(); const AbstractNativeIntensityMapping *nim2 = this->GetLayerAndIndexForNthComponent(comp2).ImageWrapper->GetNativeIntensityMapping(); double cov_raw = gmm->GetGaussian(cluster)->GetCovariance()(comp1, comp2); return cov_raw * nim1->GetScale() * nim2->GetScale(); } double SnakeWizardModel::GetClusterNativeTotalVariance(int cluster) { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); GaussianMixtureModel *gmm = uc->GetMixtureModel(); int ng = gmm->GetNumberOfGaussians(); double var = 0; for(int i = 0; i < gmm->GetNumberOfComponents(); i++) var += this->GetClusterNativeCovariance(cluster, i, i); return var; } void SnakeWizardModel::ReinitializeClustering() { UnsupervisedClustering *uc = m_Driver->GetClusteringEngine(); assert(uc); uc->InitializeClusters(); this->InvokeEvent(GMMModifiedEvent()); TagGMMPreprocessingFilterModified(); } int SnakeWizardModel::GetNumberOfComponentsForSegmentation() { this->Update(); return m_ComponentInfo.size(); } SnakeWizardModel::ComponentInfo SnakeWizardModel::GetLayerAndIndexForNthComponent(int n) { this->Update(); assert(n < m_ComponentInfo.size()); return m_ComponentInfo[n]; } LabelType SnakeWizardModel::GetClassiferFirstForegroundLabel() { ClassifierLabelForegroundMap fbtable = this->GetClassifierLabelForeground(); for(ClassifierLabelForegroundMap::iterator it = fbtable.begin(); it != fbtable.end(); ++it) if(it->second) return it->first; return 0; } void SnakeWizardModel::TrainClassifier() { // Get the classification engine RFClassificationEngine *rfengine = m_Driver->GetClassificationEngine(); // Perform the classification rfengine->TrainClassifier(); // Create a list of utilized labels RandomForestClassifier *rfc = rfengine->GetClassifier(); // Fire the appropriate event InvokeEvent(RFClassifierModifiedEvent()); // TODO: this is a hack! TagRFPreprocessingFilterModified(); } bool SnakeWizardModel::IsClassifierTrained() { // Get the classification engine RFClassificationEngine *rfengine = m_Driver->GetClassificationEngine(); return rfengine && rfengine->GetClassifier()->IsValidClassifier(); } void SnakeWizardModel::ClearSegmentation() { m_Driver->ResetSNAPSegmentationImage(); } itksnap-3.4.0/GUI/Model/SnakeWizardModel.h000066400000000000000000000450231263013355200202330ustar00rootroot00000000000000#ifndef SNAKEWIZARDMODEL_H #define SNAKEWIZARDMODEL_H #include "SNAPCommon.h" #include "AbstractModel.h" #include "PropertyModel.h" #include "ThresholdSettings.h" #include "GlobalState.h" #include "ImageWrapperBase.h" class GlobalUIModel; class IRISApplication; class ImageWrapperBase; class SnakeWizardModel : public AbstractModel { public: irisITKObjectMacro(SnakeWizardModel, AbstractModel) // This is a complex model, so there is some granularization over // the model update events that it fires. itkEventMacro(ThresholdSettingsUpdateEvent, IRISEvent) itkEventMacro(EdgePreprocessingSettingsUpdateEvent, IRISEvent) itkEventMacro(ActiveBubbleUpdateEvent, IRISEvent) itkEventMacro(BubbleListUpdateEvent, IRISEvent) itkEventMacro(BubbleDefaultRadiusUpdateEvent, IRISEvent) itkEventMacro(EvolutionIterationEvent, IRISEvent) itkEventMacro(GMMModifiedEvent, IRISEvent) itkEventMacro(RFClassifierModifiedEvent, IRISEvent) FIRES(ThresholdSettingsUpdateEvent) FIRES(EdgePreprocessingSettingsUpdateEvent) FIRES(ActiveBubbleUpdateEvent) FIRES(BubbleListUpdateEvent) FIRES(BubbleDefaultRadiusUpdateEvent) FIRES(EvolutionIterationEvent) FIRES(GMMModifiedEvent) void SetParentModel(GlobalUIModel *model); irisGetMacro(Parent, GlobalUIModel *) virtual void OnUpdate(); // Interaction modes (correspond to wizard pages) enum InteractionMode { MODE_PREPROCESSING, MODE_BUBBLES, MODE_EVOLUTION, MODE_NONE }; // State machine enums enum UIState { UIF_THESHOLDING_ENABLED, UIF_LOWER_THRESHOLD_ENABLED, UIF_UPPER_THRESHOLD_ENABLED, UIF_EDGEPROCESSING_ENABLED, UIF_CLASSIFIER_TRAINED, UIF_CAN_GENERATE_SPEED, UIF_SPEED_AVAILABLE, // Has speed volume been computed? UIF_PREPROCESSING_ACTIVE, // Is the preprocessing dialog open? UIF_BUBBLE_SELECTED, UIF_INITIALIZATION_VALID, // Do we have data to start snake evol? UIF_BUBBLE_MODE }; // Model for the threshold mode typedef AbstractPropertyModel AbstractThresholdModeModel; // Model for the snake mode typedef AbstractPropertyModel AbstractSnakeTypeModel; // Model describing the current preprocessing mode typedef SimpleItemSetDomain PreprocessingModeDomain; typedef AbstractPropertyModel AbstractPreprocessingModeModel; // Model for layer selection typedef SimpleItemSetDomain LayerSelectionDomain; typedef AbstractPropertyModel AbstractLayerSelectionModel; // Model for scalar representation selection within a layer typedef std::pair LayerScalarRepIndex; typedef SimpleItemSetDomain ScalarRepSelectionDomain; typedef AbstractPropertyModel AbstractScalarRepSelectionModel; // Model for the bubble selection // typedef STLVectorWrapperItemSetDomain BubbleDomain; // typedef AbstractPropertyModel AbstractActiveBubbleProperty; // Model for whether the current pre-processing model is in preview mode or not irisGetMacro(PreviewModel, AbstractSimpleBooleanProperty *) // Get the active layer for single-layer processing modes ScalarImageWrapperBase *GetActiveScalarLayer(PreprocessingMode mode); // Models for the current speed rendering style. These are true if the speed // is displayed using the selected style, false otherwise irisGetMacro(BlueWhiteSpeedModeModel, AbstractSimpleBooleanProperty *) irisGetMacro(RedTransparentSpeedModeModel, AbstractSimpleBooleanProperty *) // Models for the threshold-based preprocessing irisGetMacro(ThresholdLowerModel, AbstractRangedDoubleProperty *) irisGetMacro(ThresholdUpperModel, AbstractRangedDoubleProperty *) irisGetMacro(ThresholdSmoothnessModel, AbstractRangedDoubleProperty *) irisGetMacro(ThresholdModeModel, AbstractThresholdModeModel *) // Model for selecting which image layer is used for thresholding irisGetMacro(ThresholdActiveLayerModel, AbstractLayerSelectionModel *) irisGetMacro(ThresholdActiveScalarRepModel, AbstractScalarRepSelectionModel *) // Models for the edge-based preprocessing irisGetMacro(EdgePreprocessingSigmaModel, AbstractRangedDoubleProperty *) irisGetMacro(EdgePreprocessingKappaModel, AbstractRangedDoubleProperty *) irisGetMacro(EdgePreprocessingExponentModel, AbstractRangedDoubleProperty *) // Called when entering proprocessing mode (i.e., back from button page) void OnBubbleModeBack(); // What happens when we enter bubble mode void OnBubbleModeEnter(); // Model for bubble selection irisGetMacro(ActiveBubbleModel, AbstractSimpleIntProperty *) // Model for bubble radius irisGetMacro(BubbleRadiusModel, AbstractRangedDoubleProperty *) // Get the model for the snake type irisGetMacro(SnakeTypeModel, AbstractSnakeTypeModel *) // Get the model for the current preprocessing mode irisGetMacro(PreprocessingModeModel, AbstractPreprocessingModeModel *) // The models for the evolution page irisGetMacro(StepSizeModel, AbstractRangedIntProperty *) irisGetMacro(EvolutionIterationModel, AbstractSimpleIntProperty *) /** Check the state flags above */ bool CheckState(UIState state); /** A reference to a component used in automatic segmentation */ struct ComponentInfo { ImageWrapperBase *ImageWrapper; ScalarImageWrapperBase *ComponentWrapper; int ComponentIndex; }; /** * This method allows a quick lookup between components involved in * multi-variate segmentation algorithms and corresponding layers. For * example, snake mode may be launched with a six-component main image * and a single-component overlay. Then there are seven components used * in total, and calling this method with 0-5 will return the main image, * and calling it with 6 will return the overlay */ ComponentInfo GetLayerAndIndexForNthComponent(int n); /** * Returns the total number of components available for multi-variate * segmentation methods. \see GetLayerAndIndexForNthComponent(). */ int GetNumberOfComponentsForSegmentation(); /** Evaluate the threshold function so it can be plotted for the user */ void EvaluateThresholdFunction(unsigned int n, float *x, float *y); /** Evaluate the threshold function so it can be plotted for the user */ void EvaluateEdgePreprocessingFunction(unsigned int n, float *x, float *y); /** Perform the preprocessing based on thresholds */ void ApplyPreprocessing(); /** Do some cleanup when the preprocessing dialog closes */ void CompletePreprocessing(); /** Called when first displaying the snake wizard */ void OnSnakeModeEnter(); /** Add bubble at cursor */ void AddBubbleAtCursor(); /** Remove bubble at cursor */ void RemoveBubbleAtCursor(); /** Update a bubble */ bool UpdateBubble(int index, Bubble bubble); /** Called when entering the evolution page */ void OnEvolutionPageEnter(); /** Called when entering the evolution page */ void OnEvolutionPageBack(); /** Called when entering the evolution page */ void OnEvolutionPageFinish(); /** * Perform a single step of snake evolution. Returns true if the evolution * has converged */ bool PerformEvolutionStep(); /** Rewind the evolution */ void RewindEvolution(); /** Cancel segmentation and return to IRIS */ void OnCancelSegmentation(); /* =================================================================== * CLUSTERING SUPPORT (GMM) * =================================================================== */ /** Model controlling the number of clusters in the GMM */ irisRangedPropertyAccessMacro(NumberOfClusters, int) /** Model controlling the number of sampled for GMM optimization */ irisRangedPropertyAccessMacro(NumberOfGMMSamples, int) /** Model controlling the cluster used for the foreground probability */ irisRangedPropertyAccessMacro(ForegroundCluster, int) typedef SimpleItemSetDomain ComponentDomain; typedef ConcretePropertyModel ComponentIndexModel; irisGetMacro(ClusterPlottedComponentModel, ComponentIndexModel *) void ReinitializeClustering(); void PerformClusteringIteration(); // TODO: get rid of this? bool SetClusterForegroundState(int cluster, bool state); // TODO: get rid of this? double GetClusterWeight(int cluster); bool SetClusterWeight(int cluster, double weight); double GetClusterNativeMean(int cluster, int component); bool SetClusterNativeMean(int cluster, int component, double x); double GetClusterNativeCovariance(int cluster, int comp1, int comp2); double GetClusterNativeTotalVariance(int cluster); double EvaluateClusterMarginalUnivariatePDF(int cluster, int component, double x); /* =================================================================== * SUPERVISED CLASSIFICATION SUPPORT (RANDOM FORESTS) * =================================================================== */ /** A data structure representing label foreground/background state */ typedef std::map ClassifierLabelForegroundMap; /** A data structure representing the domain of classification labels */ typedef SimpleNonAtomicItemSetDomain ClassifierLabelForegroundMapDomain; /** A model around this data structure */ typedef AbstractPropertyModel AbstractClassifierLabelForegroundModel; /** Access the model encapsulating the label weights for classification */ irisGenericPropertyAccessMacro(ClassifierLabelForeground, ClassifierLabelForegroundMap, ClassifierLabelForegroundMapDomain) /** Get the first foreground label */ LabelType GetClassiferFirstForegroundLabel(); /** Model for the forest size */ irisRangedPropertyAccessMacro(ForestSize, int) /** Model for the forest size */ irisRangedPropertyAccessMacro(TreeDepth, int) /** Model for whether patch contextual features are used */ irisSimplePropertyAccessMacro(ClassifierUsePatch, bool) /** Model for the patch radius */ irisRangedPropertyAccessMacro(ClassifierPatchRadius, int) /** Bias for the classifier */ irisRangedPropertyAccessMacro(ClassifierBias, double) /** Whether coordinates are used as contextual features */ irisSimplePropertyAccessMacro(ClassifierUseCoordinates, bool) /** Train the random forest classifier when the user hits the 'train' button */ void TrainClassifier(); /** Whether the classifier is trained and available for use */ bool IsClassifierTrained(); /** Clear the classification examples (i.e., clear the classification) */ void ClearSegmentation(); protected: SnakeWizardModel(); virtual ~SnakeWizardModel() {} SmartPtr m_ThresholdUpperModel; bool GetThresholdUpperValueAndRange(double &x, NumericValueRange *range); void SetThresholdUpperValue(double x); SmartPtr m_ThresholdLowerModel; bool GetThresholdLowerValueAndRange(double &x, NumericValueRange *range); void SetThresholdLowerValue(double x); SmartPtr m_ThresholdSmoothnessModel; bool GetThresholdSmoothnessValueAndRange(double &x, NumericValueRange *range); void SetThresholdSmoothnessValue(double x); SmartPtr m_ThresholdModeModel; bool GetThresholdModeValue(ThresholdSettings::ThresholdMode &x); void SetThresholdModeValue(ThresholdSettings::ThresholdMode x); SmartPtr m_ThresholdActiveLayerModel; bool GetThresholdActiveLayerValueAndRange(unsigned long &value, LayerSelectionDomain *range); void SetThresholdActiveLayerValue(unsigned long value); SmartPtr m_ThresholdActiveScalarRepModel; bool GetThresholdActiveScalarRepValueAndRange(LayerScalarRepIndex &value, ScalarRepSelectionDomain *range); void SetThresholdActiveScalarRepValue(LayerScalarRepIndex value); SmartPtr m_PreviewModel; bool GetPreviewValue(bool &value); void SetPreviewValue(bool value); SmartPtr m_BlueWhiteSpeedModeModel; bool GetBlueWhiteSpeedModeValue(bool &value); void SetBlueWhiteSpeedModeValue(bool value); SmartPtr m_RedTransparentSpeedModeModel; bool GetRedTransparentSpeedModeValue(bool &value); void SetRedTransparentSpeedModeValue(bool value); SmartPtr m_EdgePreprocessingSigmaModel; bool GetEdgePreprocessingSigmaValueAndRange(double &x, NumericValueRange *range); void SetEdgePreprocessingSigmaValue(double x); SmartPtr m_EdgePreprocessingExponentModel; bool GetEdgePreprocessingExponentValueAndRange(double &x, NumericValueRange *range); void SetEdgePreprocessingExponentValue(double x); SmartPtr m_EdgePreprocessingKappaModel; bool GetEdgePreprocessingKappaValueAndRange(double &x, NumericValueRange *range); void SetEdgePreprocessingKappaValue(double x); SmartPtr m_SnakeTypeModel; bool GetSnakeTypeValueAndRange(SnakeType &value, GlobalState::SnakeTypeDomain *range); void SetSnakeTypeValue(SnakeType value); SmartPtr m_PreprocessingModeModel; bool GetPreprocessingModeValueAndRange(PreprocessingMode &value, PreprocessingModeDomain *range); void SetPreprocessingModeValue(PreprocessingMode value); SmartPtr m_ActiveBubbleModel; bool GetActiveBubbleValue(int &value); void SetActiveBubbleValue(int value); SmartPtr m_BubbleRadiusModel; bool GetBubbleRadiusValueAndRange(double &value, NumericValueRange *range); void SetBubbleRadiusValue(double value); SmartPtr m_StepSizeModel; SmartPtr m_EvolutionIterationModel; int GetEvolutionIterationValue(); // Get the threshold settings for the active layer ThresholdSettings *GetThresholdSettings(); // Helper function to check if particular set of models is active bool AreThresholdModelsActive(); bool AreEdgePreprocessingModelsActive(); // Helper function to check if we can generate a speed image given the // current pre-segmentation settings. bool CanGenerateSpeedVolume(); // For models that work on only a single scalar image layer, report which // layer is currently selected as the target layer ScalarImageWrapperBase *GetSelectedScalarLayer(); // Compute range and def value of radius based on ROI dimensions void ComputeBubbleRadiusDefaultAndRange(); // Range for the bubble radius variable NumericValueRange m_BubbleRadiusDomain; // Default value for the bubble radius (when there is no selection) double m_BubbleRadiusDefaultValue; // Are we in bubble mode InteractionMode m_InteractionMode; void SetInteractionMode(InteractionMode mode); /* =================================================================== * CLUSTERING SUPPORT (GMM) * =================================================================== */ // Model for the number of clusters SmartPtr m_NumberOfClustersModel; bool GetNumberOfClustersValueAndRange(int &value, NumericValueRange *range); void SetNumberOfClustersValue(int value); // Model for the number of clusters SmartPtr m_NumberOfGMMSamplesModel; bool GetNumberOfGMMSamplesValueAndRange(int &value, NumericValueRange *range); void SetNumberOfGMMSamplesValue(int value); // Model for the active cluster SmartPtr m_ForegroundClusterModel; bool GetForegroundClusterValueAndRange(int &value, NumericValueRange *range); void SetForegroundClusterValue(int value); // Model for the selected component SmartPtr m_ClusterPlottedComponentModel; // TODO: this should be handled through the ITK modified mechanism void TagGMMPreprocessingFilterModified(); // A list of all components available to clustering and other multi-variate // segmentation code. This list is updated in OnUpdate() in response to any // events that change the number of available layers. std::vector m_ComponentInfo; // Update the model for component selection void UpdateClusterPlottedComponentModel(); /* =================================================================== * SUPERVISED CLASSIFICATION SUPPORT (Random Forest) * =================================================================== */ // A list of suggested labels, from which the user can choose one to draw // These labels are the N most recently used labels std::map m_ClassifyQuickLabels; // The size of the suggested label list (static) static unsigned int m_ClassifyQuickLabelsCount; // A list of color labels used in classification std::map m_ClassifyUsedLabels; SmartPtr m_ForestSizeModel; bool GetForestSizeValueAndRange(int &value, NumericValueRange *range); void SetForestSizeValue(int value); SmartPtr m_TreeDepthModel; bool GetTreeDepthValueAndRange(int &value, NumericValueRange *range); void SetTreeDepthValue(int value); SmartPtr m_ClassifierUsePatchModel; SmartPtr m_ClassifierPatchRadiusModel; bool GetClassifierPatchRadiusValueAndRange(int &value, NumericValueRange *range); void SetClassifierPatchRadiusValue(int value); SmartPtr m_ClassifierBiasModel; bool GetClassifierBiasValueAndRange(double &value, NumericValueRange *range); void SetClassifierBiasValue(double value); SmartPtr m_ClassifierUseCoordinatesModel; bool GetClassifierUseCoordinatesValue(bool &value); void SetClassifierUseCoordinatesValue(bool value); SmartPtr m_ClassifierLabelForegroundModel; bool GetClassifierLabelForegroundValueAndRange(ClassifierLabelForegroundMap &value, ClassifierLabelForegroundMapDomain *range); void SetClassifierLabelForegroundValue(ClassifierLabelForegroundMap value); // TODO: this should be handled through the ITK modified mechanism void TagRFPreprocessingFilterModified(); // Parent model GlobalUIModel *m_Parent; IRISApplication *m_Driver; GlobalState *m_GlobalState; }; #endif // SNAKEWIZARDMODEL_H itksnap-3.4.0/GUI/Model/StateManagement.cxx000066400000000000000000000032771263013355200204650ustar00rootroot00000000000000#include "StateManagement.h" #include "SNAPEvents.h" #include "SNAPEventListenerCallbacks.h" void BooleanCondition ::OnStateChange() { InvokeEvent(StateMachineChangeEvent()); } BinaryBooleanCondition ::BinaryBooleanCondition(BooleanCondition *a, BooleanCondition *b) { m_A = a; m_B = b; // Make sure events propagate up AddListener(m_A, StateMachineChangeEvent(), this, &BooleanCondition::OnStateChange); AddListener(m_B, StateMachineChangeEvent(), this, &BinaryBooleanCondition::OnStateChange); } AndCondition::AndCondition(BooleanCondition *a, BooleanCondition *b) : BinaryBooleanCondition(a,b) { } bool AndCondition::operator ()() const { return (*m_A)() && (*m_B)(); } SmartPtr AndCondition::New(BooleanCondition *a, BooleanCondition *b) { SmartPtr p = new AndCondition(a, b); p->UnRegister(); return p; } OrCondition::OrCondition(BooleanCondition *a, BooleanCondition *b) : BinaryBooleanCondition(a,b) { } bool OrCondition::operator ()() const { return (*m_A)() || (*m_B)(); } SmartPtr OrCondition::New(BooleanCondition *a, BooleanCondition *b) { SmartPtr p = new OrCondition(a, b); p->UnRegister(); return p; } NotCondition::NotCondition(BooleanCondition *a) { m_A = a; AddListener(m_A, StateMachineChangeEvent(), this, &BooleanCondition::OnStateChange); } bool NotCondition::operator ()() const { return ! (*m_A)(); } SmartPtr NotCondition::New(BooleanCondition *a) { SmartPtr p = new NotCondition(a); p->UnRegister(); return p; } itksnap-3.4.0/GUI/Model/StateManagement.h000066400000000000000000000057701263013355200201120ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef STATEMANAGEMENT_H #define STATEMANAGEMENT_H #include #include #include /** An abstract condition that returns true or false. Used to create dynamic boolean operators for widget activation and state management. These conditions can be used in the following form SmartPtr p = AndCondition::New( MyCondition::New(BLAH), NotCondition::New(MyCondition::New(FOO))); (*p)() // BLAH && (!FOO) */ class BooleanCondition : public itk::Object { public: FIRES(StateMachineChangeEvent) virtual bool operator () () const = 0; virtual void OnStateChange(); protected: BooleanCondition() {} virtual ~BooleanCondition() {} }; class BinaryBooleanCondition : public BooleanCondition { public: protected: BinaryBooleanCondition(BooleanCondition *a, BooleanCondition *b); SmartPtr m_A, m_B; }; class AndCondition : public BinaryBooleanCondition { public: typedef AndCondition Self; typedef BinaryBooleanCondition Superclass; itkTypeMacro(Self, Superclass) static SmartPtr New(BooleanCondition *a, BooleanCondition *b); bool operator() () const; protected: AndCondition(BooleanCondition *a, BooleanCondition *b); virtual ~AndCondition() {} }; class OrCondition : public BinaryBooleanCondition { public: typedef OrCondition Self; typedef BinaryBooleanCondition Superclass; itkTypeMacro(Self, Superclass) static SmartPtr New(BooleanCondition *a, BooleanCondition *b); bool operator() () const; protected: OrCondition(BooleanCondition *a, BooleanCondition *b); virtual ~OrCondition() {} }; class NotCondition : public BooleanCondition { public: typedef NotCondition Self; typedef BooleanCondition Superclass; itkTypeMacro(Self, Superclass) static SmartPtr New(BooleanCondition *a); bool operator () () const; protected: NotCondition(BooleanCondition *a); virtual ~NotCondition() {} private: SmartPtr m_A; }; #endif // STATEMANAGEMENT_H itksnap-3.4.0/GUI/Model/SynchronizationModel.cxx000066400000000000000000000144401263013355200215640ustar00rootroot00000000000000#include "SynchronizationModel.h" #include "GlobalUIModel.h" #include "IRISApplication.h" #include "SystemInterface.h" #include "GenericSliceModel.h" #include "GenericImageData.h" #include "ImageWrapperBase.h" #include "SliceWindowCoordinator.h" #include "Generic3DModel.h" #include "Generic3DRenderer.h" #include "vtkCamera.h" #include "vtkCommand.h" #include "IPCHandler.h" /** Structure passed on to IPC */ struct IPCMessage { // The cursor position in world coordinates Vector3d cursor; // The zoom factor (screen pixels / mm) double zoom_level[3]; // The position of the viewport center relative to cursor // in all three slice views Vector2f viewPositionRelative[3]; // 3D camera state CameraState camera; // Version of the data structure enum VersionEnum { VERSION = 0x1005 }; }; SynchronizationModel::SynchronizationModel() { // Create the models m_SyncEnabledModel = NewSimpleConcreteProperty(true); m_SyncCursorModel = NewSimpleConcreteProperty(true); m_SyncZoomModel = NewSimpleConcreteProperty(true); m_SyncPanModel = NewSimpleConcreteProperty(true); m_SyncCameraModel = NewSimpleConcreteProperty(true); m_SyncChannelModel = NewRangedConcreteProperty(1, 1, 99, 1); // Create an IPC handler m_IPCHandler = new IPCHandler(); // Broadcast state m_CanBroadcast = false; } SynchronizationModel::~SynchronizationModel() { if(m_IPCHandler->IsAttached()) m_IPCHandler->Close(); delete m_IPCHandler; } void SynchronizationModel::SetParentModel(GlobalUIModel *parent) { // Set the parent m_Parent = parent; m_SystemInterface = m_Parent->GetDriver()->GetSystemInterface(); // Initialize the IPC handler m_IPCHandler->Attach( m_SystemInterface->GetUserPreferencesFileName(), (short) IPCMessage::VERSION, sizeof(IPCMessage)); // TODO: the defaults should be read from global preferences // Listen to the events from the parent that correspond to changes that // need to be broadcast, and send them downstream as model update events // Cursor changes Rebroadcast(m_Parent->GetDriver(), CursorUpdateEvent(), ModelUpdateEvent()); // Viewpoint geometry changes for(int i = 0; i < 3; i++) { GenericSliceModel *gsm = m_Parent->GetSliceModel(i); Rebroadcast(gsm, SliceModelGeometryChangeEvent(), ModelUpdateEvent()); } // Changes to the 3D viewpoint (stored in a vtkCamera object) Rebroadcast(m_Parent->GetModel3D()->GetRenderer(), Generic3DRenderer::CameraUpdateEvent(), ModelUpdateEvent()); } void SynchronizationModel::OnUpdate() { // If there is no synchronization or no image, get out IRISApplication *app = m_Parent->GetDriver(); if(!app->IsMainImageLoaded() || !m_SyncEnabledModel->GetValue() || !m_CanBroadcast) return; // Figure out what to broadcast bool bc_cursor = m_EventBucket->HasEvent(CursorUpdateEvent()) && m_SyncCursorModel->GetValue(); bool bc_zoom = m_EventBucket->HasEvent(SliceModelGeometryChangeEvent()) && m_SyncZoomModel->GetValue(); bool bc_pan = (m_EventBucket->HasEvent(SliceModelGeometryChangeEvent()) || m_EventBucket->HasEvent(CursorUpdateEvent())) && m_SyncPanModel->GetValue(); bool bc_camera = m_EventBucket->HasEvent(Generic3DRenderer::CameraUpdateEvent()) && m_SyncCameraModel->GetValue(); // Read the contents of shared memory into the local message object IPCMessage message; m_IPCHandler->Read(static_cast(&message)); // Cursor change if(bc_cursor) { // Map the cursor to NIFTI coordinates ImageWrapperBase *iw = app->GetCurrentImageData()->GetMain(); message.cursor = iw->TransformVoxelIndexToNIFTICoordinates( to_double(app->GetCursorPosition())); } // Zoom/Pan change for(int i = 0; i < 3; i++) { GenericSliceModel *gsm = m_Parent->GetSliceModel(i); AnatomicalDirection dir = app->GetAnatomicalDirectionForDisplayWindow(i); if(bc_zoom) message.zoom_level[dir] = gsm->GetViewZoom(); if(bc_pan) message.viewPositionRelative[dir] = gsm->GetViewPositionRelativeToCursor(); } // 3D viewpoint if(bc_camera) { // Get the camera state CameraState cs = m_Parent->GetModel3D()->GetRenderer()->GetCameraState(); message.camera = cs; } // Broadcast the new message m_IPCHandler->Broadcast(static_cast(&message)); } void SynchronizationModel::ReadIPCState() { IRISApplication *app = m_Parent->GetDriver(); if(!app->IsMainImageLoaded() || !m_SyncCursorModel->GetValue()) return; // Read the IPC message IPCMessage message; if(m_IPCHandler->ReadIfNew(static_cast(&message))) { if(m_SyncCursorModel->GetValue()) { // Map the cursor position to the image coordinates GenericImageData *id = app->GetCurrentImageData(); Vector3d vox = id->GetMain()->TransformNIFTICoordinatesToVoxelIndex(message.cursor); // Round the cursor to integer value itk::Index<3> pos; Vector3ui vpos; pos[0] = vpos[0] = (unsigned int) (vox[0] + 0.5); pos[1] = vpos[1] = (unsigned int) (vox[1] + 0.5); pos[2] = vpos[2] = (unsigned int) (vox[2] + 0.5); // Check if the voxel position is inside the image region if(vpos != app->GetCursorPosition() && id->GetImageRegion().IsInside(pos)) { app->SetCursorPosition(vpos); } } // Set the zoom/pan levels for(int i = 0; i < 3; i++) { GenericSliceModel *gsm = m_Parent->GetSliceModel(i); AnatomicalDirection dir = app->GetAnatomicalDirectionForDisplayWindow(i); if(m_SyncZoomModel->GetValue() && gsm->IsSliceInitialized() && gsm->GetViewZoom() != message.zoom_level[dir] && static_cast(message.zoom_level[dir]) > 0.0f) { gsm->SetViewZoom(message.zoom_level[dir]); } if(m_SyncPanModel->GetValue() && gsm->IsSliceInitialized() && gsm->GetViewPositionRelativeToCursor() != message.viewPositionRelative[dir]) { gsm->SetViewPositionRelativeToCursor(message.viewPositionRelative[dir]); } } // Set the camera state if(m_SyncCameraModel->GetValue()) { // Get the currently used 3D camera CameraState cs = message.camera; m_Parent->GetModel3D()->GetRenderer()->SetCameraState(message.camera); } } } itksnap-3.4.0/GUI/Model/SynchronizationModel.h000066400000000000000000000031671263013355200212150ustar00rootroot00000000000000#ifndef SYNCHRONIZATIONMODEL_H #define SYNCHRONIZATIONMODEL_H #include "PropertyModel.h" class GlobalUIModel; class SystemInterface; class IPCHandler; /** * @brief Model that interfaces with the GUI controls that handle * synchronization */ class SynchronizationModel : public AbstractModel { public: irisITKObjectMacro(SynchronizationModel, AbstractModel) void SetParentModel(GlobalUIModel *parent); /** Models controlling sync state */ irisSimplePropertyAccessMacro(SyncEnabled, bool) irisRangedPropertyAccessMacro(SyncChannel, int) irisSimplePropertyAccessMacro(SyncCursor, bool) irisSimplePropertyAccessMacro(SyncZoom, bool) irisSimplePropertyAccessMacro(SyncPan, bool) irisSimplePropertyAccessMacro(SyncCamera, bool) /** * Whether the model should be broadcasting. The GUI should toggle this * flag depending on whether the window is active or not */ irisGetSetMacro(CanBroadcast, bool) /** This method should be called by UI at regular intervals to read IPC state */ void ReadIPCState(); protected: SynchronizationModel(); virtual ~SynchronizationModel(); virtual void OnUpdate(); SmartPtr m_SyncEnabledModel; SmartPtr m_SyncCursorModel; SmartPtr m_SyncZoomModel; SmartPtr m_SyncPanModel; SmartPtr m_SyncCameraModel; SmartPtr m_SyncChannelModel; GlobalUIModel *m_Parent; SystemInterface *m_SystemInterface; IPCHandler *m_IPCHandler; bool m_CanBroadcast; }; #endif // SYNCHRONIZATIONMODEL_H itksnap-3.4.0/GUI/Model/UIAction.cxx000066400000000000000000000002011263013355200170430ustar00rootroot00000000000000#include "UIAction.h" #include void UIAbstractAction::Initialize(GlobalUIModel *model) { this->m_Model = model; } itksnap-3.4.0/GUI/Model/UIAction.h000066400000000000000000000015221263013355200164770ustar00rootroot00000000000000#ifndef UIACTION_H #define UIACTION_H #include "itkObject.h" #include "IRISException.h" #include class GlobalUIModel; class IRISException; /** A UI action is an object used to perform complex actions in SNAP. This includes any actions that are undoable. */ class UIAbstractAction : public itk::Object { public: typedef std::list WarningList; typedef std::list::const_iterator WarningIter; virtual void Execute() = 0; virtual void Initialize(GlobalUIModel *model); bool HasWarnings() const { return m_Warnings.size() > 0; } const WarningList &GetWarnings() const { return m_Warnings; } protected: GlobalUIModel *m_Model; std::list m_Warnings; }; class UIUndoableAction : public UIAbstractAction { public: virtual void Undo() = 0; }; #endif // UIACTION_H itksnap-3.4.0/GUI/Model/UIReporterDelegates.cxx000066400000000000000000000001211263013355200212470ustar00rootroot00000000000000#include "UIReporterDelegates.h" UIReporterDelegates::UIReporterDelegates() { } itksnap-3.4.0/GUI/Model/UIReporterDelegates.h000066400000000000000000000045411263013355200207060ustar00rootroot00000000000000#ifndef UIREPORTERDELEGATES_H #define UIREPORTERDELEGATES_H #include "SNAPCommon.h" #include "SNAPEvents.h" #include "itkObject.h" #include "itkObjectFactory.h" namespace itk { template class Image; } class Registry; /** An interface used by models to request the viewport size from the GUI and for the GUI to notify models of changes in the viewport size. */ class ViewportSizeReporter : public itk::Object { public: itkEventMacro(ViewportResizeEvent,IRISEvent) /** Child classes are expected to fire this event when viewport resizes */ FIRES(ViewportResizeEvent) virtual bool CanReportSize() = 0; virtual Vector2ui GetViewportSize() = 0; /** For retina displays, report the ratio of actual GL pixels to logical pixels */ virtual float GetViewportPixelRatio() = 0; /** Get the viewport size in logical units (for retina-like displays) */ virtual Vector2ui GetLogicalViewportSize() = 0; protected: virtual ~ViewportSizeReporter() {} }; /** This is a progress reporter delegate that allows the SNAP model classes to report progress without knowing what GUI toolkit actually implements the progress dialog. */ class ProgressReporterDelegate { public: /** Set the progress value between 0 and 1 */ virtual void SetProgressValue(double) = 0; }; /** This is a delegate that renders text into a bitmap. This is used with OpenGL code that needs to draw text */ class TextRenderingDelegate { public: virtual void RenderTextInOpenGL( const char *text, int x, int y, int w, int h, int font_size, int align_horiz, int align_vert, unsigned char rgba[]) = 0; }; /** This delegate is used to generate an OpenGL texture for icons. Icons are identified by a string. */ class TextureGenerationDelegate { public: virtual int GenerateTexture(const char *imageName); }; /** This delegate is used to obtain system-specific information */ class SystemInfoDelegate { public: virtual std::string GetApplicationDirectory() = 0; virtual std::string GetApplicationFile() = 0; virtual std::string GetApplicationPermanentDataLocation() = 0; typedef itk::Image GrayscaleImage; virtual void LoadResourceAsImage2D(std::string tag, GrayscaleImage *image) = 0; virtual void LoadResourceAsRegistry(std::string tag, Registry ®) = 0; }; #endif // UIREPORTERDELEGATES_H itksnap-3.4.0/GUI/Model/UIState.h000066400000000000000000000014561263013355200163500ustar00rootroot00000000000000#ifndef UISTATE_H #define UISTATE_H /** A list of states that the Global UI may be in. Whenever any of these states changes, the UI issues a StateChangedEvent */ enum UIState { UIF_BASEIMG_LOADED, // i.e., Gray or RGB loaded UIF_OVERLAY_LOADED, // i.e., Baseimg loaded and at least one overlay UIF_IRIS_MODE, // Not in snake mode (or other future such mode) UIF_IRIS_WITH_BASEIMG_LOADED, // i.e., system in main interaction mode UIF_IRIS_WITH_OVERLAY_LOADED, // i.e., system in main interaction mode UIF_ROI_VALID, UIF_LINKED_ZOOM, UIF_UNDO_POSSIBLE, UIF_REDO_POSSIBLE, UIF_UNSAVED_CHANGES, UIF_MESH_SAVEABLE, UIF_SNAKE_MODE, UIF_LEVEL_SET_ACTIVE, UIF_MULTIPLE_BASE_LAYERS // i.e., more than one non-sticky layer }; #endif // UISTATE_H itksnap-3.4.0/GUI/Qt/000077500000000000000000000000001263013355200141775ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/Components/000077500000000000000000000000001263013355200163245ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/Components/.DS_Store000066400000000000000000000140041263013355200200060ustar00rootroot00000000000000Bud1%  @ @ @ @ E%DSDB` @ @ @itksnap-3.4.0/GUI/Qt/Components/AnnotationToolPanel.cxx000066400000000000000000000050141263013355200230000ustar00rootroot00000000000000#include "AnnotationToolPanel.h" #include "ui_AnnotationToolPanel.h" #include "QtAbstractButtonCoupling.h" #include "QtCheckableWidgetGroupCoupling.h" #include "QtSliderCoupling.h" #include "GlobalState.h" #include "GlobalUIModel.h" #include "QFileInfo" #include "IRISApplication.h" AnnotationToolPanel::AnnotationToolPanel(QWidget *parent) : QWidget(parent), ui(new Ui::AnnotationToolPanel) { ui->setupUi(this); } AnnotationToolPanel::~AnnotationToolPanel() { delete ui; } void AnnotationToolPanel::SetModel(GlobalUIModel *model) { m_Model = model; std::map mode_mapping; mode_mapping[ANNOTATION_RULER] = ui->btnRuler; mode_mapping[ANNOTATION_LANDMARK] = ui->btnText; mode_mapping[ANNOTATION_SELECT] = ui->btnSelect; makeCheckableWidgetGroupCoupling(ui->grpMode, mode_mapping, m_Model->GetGlobalState()->GetAnnotationModeModel()); makeCoupling(ui->inColor, m_Model->GetGlobalState()->GetAnnotationColorModel()); makeCoupling(ui->inOpacity, m_Model->GetGlobalState()->GetAnnotationAlphaModel()); makeCoupling((QAbstractButton *) ui->btnVisible, m_Model->GetGlobalState()->GetAnnotationVisibilityModel()); } void AnnotationToolPanel::on_btnOpen_clicked() { // Load annotations QString file = ShowSimpleOpenDialogWithHistory( this, m_Model, "Annotations", "Open Annotation File", "Annotation File", "ITK-SNAP Annotation Files (*.annot)"); if(!file.isNull()) { QString file_abs = QFileInfo(file).absoluteFilePath(); try { m_Model->GetDriver()->LoadAnnotations(to_utf8(file_abs).c_str()); } catch(std::exception &exc) { ReportNonLethalException(this, exc, "Error Opening Annotation File", QString("Failed to open annotation file %1").arg(file_abs)); } } } void AnnotationToolPanel::on_btnSave_clicked() { // Save annotations QString file = ShowSimpleSaveDialogWithHistory( this, m_Model, "Annotations", "Open Annotation File", "Annotation File", "ITK-SNAP Annotation Files (*.annot)", false); if(!file.isNull()) { QString file_abs = QFileInfo(file).absoluteFilePath(); try { m_Model->GetDriver()->SaveAnnotations(to_utf8(file_abs).c_str()); } catch(std::exception &exc) { ReportNonLethalException(this, exc, "Error Saving Annotation File", QString("Failed to save annotation file %1").arg(file_abs)); } } } itksnap-3.4.0/GUI/Qt/Components/AnnotationToolPanel.h000066400000000000000000000007751263013355200224360ustar00rootroot00000000000000#ifndef ANNOTATIONTOOLPANEL_H #define ANNOTATIONTOOLPANEL_H #include class GlobalUIModel; namespace Ui { class AnnotationToolPanel; } class AnnotationToolPanel : public QWidget { Q_OBJECT public: explicit AnnotationToolPanel(QWidget *parent = 0); ~AnnotationToolPanel(); void SetModel(GlobalUIModel *model); private slots: void on_btnOpen_clicked(); void on_btnSave_clicked(); private: Ui::AnnotationToolPanel *ui; GlobalUIModel *m_Model; }; #endif // ANNOTATIONTOOLPANEL_H itksnap-3.4.0/GUI/Qt/Components/AnnotationToolPanel.ui000066400000000000000000000314241263013355200226170ustar00rootroot00000000000000 AnnotationToolPanel 0 0 185 300 Form QLabel { font-size: 12px; } QToolButton { font-size: 10px; } 4 0 0 0 0 Annotation mode: 4 4 0 0 150 0 <html><head/><body><p><span style=" font-weight:600;">Line and Ruler Mode</span></p><p>Use this mode to measure distances between points and angles between line segments. Click with the left mouse button to begin drawing a line. Click again to place the second point of the line. Right-click to complete the line.</p><p>The length of the line and the angle between the line and other lines drawn on the same slice are shown interactively. Hold the Shift key to restrict the angle to a multiple of 5.</p></body></html> Line and Ruler Mode :/root/icons8_ruler_16.png:/root/icons8_ruler_16.png true true true Qt::ToolButtonTextBesideIcon 150 0 <html><head/><body><p><span style=" font-weight:600;">Text Annotation Mode</span></p><p>Use this mode to place text annotations in the 3D image. Annotations are anchored to a single voxel location. Along with line annotations, they can be saved and shared with colleagues.</p></body></html> Text Annotation Mode :/root/icons8_generic_text_16.png:/root/icons8_generic_text_16.png true false true Qt::ToolButtonTextBesideIcon 150 0 <html><head/><body><p><span style=" font-weight:600;">Selection Mode</span></p><p>Use this mode to select previously created annotations. Once selected, annotations can be moved or deleted. Shift-click to select multiple annotations.</p></body></html> Selection Mode :/root/icons8_cursor_16.png:/root/icons8_cursor_16.png true true Qt::ToolButtonTextBesideIcon Qt::Vertical QSizePolicy::Fixed 20 5 Annotation properties: QLabel { font-size:12px; } Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 4 4 4 0 0 Color: 10 10 Qt::Vertical QSizePolicy::Fixed 20 10 Overall annotation opacity: QLabel { font-size:12px; } 4 12 0 4 0 <html><head/><body><p>Adjust the opacity of all annotations</p></body></html> 100 Qt::Horizontal QSlider::NoTicks 25 20 20 Toggle the visibility of all annotations ... :/root/icons8_invisible_12.png :/root/icons8_visible_12.png:/root/icons8_invisible_12.png 12 12 true true true Qt::Vertical 20 40 6 4 4 4 4 Qt::Horizontal 110 20 <html><head/><body><p>Load annotations from file</p></body></html> ... :/root/open_22.png:/root/open_22.png <html><head/><body><p>Save annotations to file</p></body></html> ... :/root/save_22.png:/root/save_22.png QDoubleSlider QSlider
QDoubleSlider.h
QColorButtonWidget QWidget
QColorButtonWidget.h
1
itksnap-3.4.0/GUI/Qt/Components/CollapsableGroupBox.cxx000066400000000000000000000012221263013355200227540ustar00rootroot00000000000000#include "CollapsableGroupBox.h" #include "ui_CollapsableGroupBox.h" #include "QLayout" CollapsableGroupBox::CollapsableGroupBox(QWidget *parent) : QWidget(parent), ui(new Ui::CollapsableGroupBox) { ui->setupUi(this); } CollapsableGroupBox::~CollapsableGroupBox() { delete ui; } QString CollapsableGroupBox::title() const { return ui->pushButton->text(); } void CollapsableGroupBox::setTitle(QString title) { ui->pushButton->setText(title); } void CollapsableGroupBox::addWidget(QWidget *widget) { ui->body->layout()->addWidget(widget); } void CollapsableGroupBox::collapse(bool flag) { ui->body->setMaximumHeight(flag ? 0 : 999999); } itksnap-3.4.0/GUI/Qt/Components/CollapsableGroupBox.h000066400000000000000000000010501263013355200224000ustar00rootroot00000000000000#ifndef COLLAPSABLEGROUPBOX_H #define COLLAPSABLEGROUPBOX_H #include namespace Ui { class CollapsableGroupBox; } class CollapsableGroupBox : public QWidget { Q_OBJECT Q_PROPERTY(QString title READ title WRITE setTitle USER true) public: explicit CollapsableGroupBox(QWidget *parent = 0); ~CollapsableGroupBox(); QString title() const; void setTitle(QString title); void addWidget(QWidget *widget); public slots: void collapse(bool flag); private: Ui::CollapsableGroupBox *ui; }; #endif // COLLAPSABLEGROUPBOX_H itksnap-3.4.0/GUI/Qt/Components/CollapsableGroupBox.ui000066400000000000000000000051541263013355200225770ustar00rootroot00000000000000 CollapsableGroupBox 0 0 214 309 Form QWidget#body { border-left: 1px solid gray; border-right: 1px solid gray; background-color: gray; } 0 0 Qt::NoFocus QPushButton { border-radius:0px; border-top: 1px solid gray; border-bottom: 1px solid gray; background-color: qlineargradient(spread:pad, x1:0.123, y1:0.125, x2:0.108, y2:1, stop:0 rgba(204, 204, 204, 255), stop:1 rgba(255, 255, 255, 255)); margin:0px; padding: 0px; text-align:left; font:12px; } Main Image :/root/triangle_small_down_16.png :/root/triangle_small_right_16.png:/root/triangle_small_down_16.png true false 1 0 pushButton clicked(bool) CollapsableGroupBox collapse(bool) 113 7 119 189 collapse(bool) itksnap-3.4.0/GUI/Qt/Components/ColorLabelQuickListWidget.cxx000066400000000000000000000065731263013355200240760ustar00rootroot00000000000000#include "ColorLabelQuickListWidget.h" #include #include #include #include #include "GlobalUIModel.h" #include "GlobalState.h" #include "IRISApplication.h" #include "ColorLabelQuickListModel.h" #include "ColorLabelTable.h" #include "SNAPQtCommon.h" #include "QtToolbarCoupling.h" ColorLabelQuickListWidget::ColorLabelQuickListWidget(QWidget *parent) : SNAPComponent(parent) { // There is no model m_Model = NULL; // Create and configure the toolbar m_Toolbar = new QToolBar(); m_Toolbar->setIconSize(QSize(16,16)); m_Toolbar->setMovable(false); m_Toolbar->setFloatable(false); m_Toolbar->setStyleSheet("padding: 0px; spacing: 0px;"); m_Toolbar->setContentsMargins(0,0,0,0); // Create and configure the action group m_ActionGroup = new QActionGroup(m_Toolbar); // Add toolbar via layout QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0,0,0,0); layout->addWidget(m_Toolbar); this->setLayout(layout); // RE-emit the signal from the toolbar connect(m_Toolbar, SIGNAL(actionTriggered(QAction*)), this, SIGNAL(actionTriggered(QAction*))); // Define the maximum label count m_MaximumLabelCount = 6; } void ColorLabelQuickListWidget::SetModel(GlobalUIModel *model) { // Grab a copy of the model m_Model = model; // Listen to changes in the label quicklist ColorLabelQuickListModel *qlm = m_Model->GetColorLabelQuickListModel(); connectITK(qlm, ModelUpdateEvent()); // Use the coupling mechanism to handle label selection. makeCoupling(m_Toolbar, qlm->GetActiveComboModel()); } void ColorLabelQuickListWidget::setMaximumLabelCount(int value) { // Change the maximum label count m_MaximumLabelCount = value; // We need to update the toolbar, but only if already there is a model if(m_Model) this->UpdateToolbar(); } void ColorLabelQuickListWidget::onModelUpdate(const EventBucket &bucket) { ColorLabelQuickListModel *qlm = m_Model->GetColorLabelQuickListModel(); // If there is an event from the quick list model, we need to rebuild if(bucket.HasEvent(ModelUpdateEvent(), qlm)) { this->UpdateToolbar(); } } void ColorLabelQuickListWidget::UpdateToolbar() { // Get the color label table ColorLabelTable *clt = m_Model->GetDriver()->GetColorLabelTable(); // Update the source of the label quick-list ColorLabelQuickListModel *qlm = m_Model->GetColorLabelQuickListModel(); qlm->Update(); // Get the updated list of recent labels const ColorLabelQuickListModel::ComboList &qlist = qlm->GetRecentCombos(); // Remove every action from the toolbar foreach(QAction *action, m_ActionGroup->actions()) m_ActionGroup->removeAction(action); m_Toolbar->clear(); // Add an action for each item for(int i = 0; i < qlist.size(); i++) { // Create the action QAction *action = new QAction(m_Toolbar); // Get the foreground/background LabelType fg = qlist[i].first; DrawOverFilter bg = qlist[i].second; // Configure action apperance action->setIcon(CreateLabelComboIcon(16, 16, fg, bg, clt)); action->setToolTip(CreateLabelComboTooltip(fg, bg, clt)); // Set the action's data action->setData(i); // Make action checkable action->setCheckable(true); // Add the action to the action group action->setActionGroup(m_ActionGroup); // Add the action to the toolbar m_Toolbar->addAction(action); } } itksnap-3.4.0/GUI/Qt/Components/ColorLabelQuickListWidget.h000066400000000000000000000024531263013355200235140ustar00rootroot00000000000000#ifndef COLORLABELQUICKLISTWIDGET_H #define COLORLABELQUICKLISTWIDGET_H #include "SNAPCommon.h" #include "SNAPComponent.h" class QToolBar; class GlobalUIModel; class QAction; class QActionGroup; /** * This is a widget holding a toolbar with a list of buttons, each corresponding * to a color label. When the user clicks the buttons, that label is assigned * to be the foreground label. Right clicking the label gives a context menu. */ class ColorLabelQuickListWidget : public SNAPComponent { Q_OBJECT public: explicit ColorLabelQuickListWidget(QWidget *parent = 0); /** Initialize the widget by assigning it a model */ void SetModel(GlobalUIModel *model); /** Set the number of labels to display */ void setMaximumLabelCount(int value); signals: void actionTriggered(QAction *); public slots: protected: // The widget contains a toolbar with a list of actions QToolBar *m_Toolbar; // An action group controlling action behavior QActionGroup *m_ActionGroup; // A pointer to the model GlobalUIModel *m_Model; // The number of labels to display int m_MaximumLabelCount; // Handle an update from the model virtual void onModelUpdate(const EventBucket &bucket); // Populate the toolbar from the model void UpdateToolbar(); }; #endif // COLORLABELQUICKLISTWIDGET_H itksnap-3.4.0/GUI/Qt/Components/ColorMapInspector.cxx000066400000000000000000000126261263013355200224620ustar00rootroot00000000000000#include "ColorMapInspector.h" #include "ui_ColorMapInspector.h" #include "ColorMapModel.h" #include "QtReporterDelegates.h" #include "QtDoubleSpinBoxCoupling.h" #include "QtSpinBoxCoupling.h" #include "QtRadioButtonCoupling.h" #include "QtWidgetActivator.h" #include #include #include "SNAPQtCommon.h" ColorMapInspector::ColorMapInspector(QWidget *parent) : SNAPComponent(parent), ui(new Ui::ColorMapInspector) { ui->setupUi(this); m_PresetsUpdating = false; // Create the viewport reporter m_ColorMapBoxViewportReporter = QtViewportReporter::New(); m_ColorMapBoxViewportReporter->SetClientWidget(ui->boxColorMap); // Connect the interactor on the colormap box to this object ui->boxColorMap->GetDelegate()->SetInspectorWidget(this); } ColorMapInspector::~ColorMapInspector() { delete ui; } void ColorMapInspector::SetModel(ColorMapModel *model) { // Set the model m_Model = model; // Pass the model to the colormap box ui->boxColorMap->SetModel(model); // Connect the viewport reporter model->SetViewportReporter(m_ColorMapBoxViewportReporter); // Listen to model update events connectITK(m_Model, ModelUpdateEvent()); connectITK(m_Model, ColorMapModel::PresetUpdateEvent()); // Connect widgets to the corresponding sub-models makeCoupling(ui->inControlX, m_Model->GetMovingControlPositionModel()); makeCoupling(ui->inControlOpacity, m_Model->GetMovingControlOpacityModel()); makeCoupling(ui->inControlIndex, m_Model->GetMovingControlIndexModel()); // Connect radio button groups to corresponding enums makeRadioGroupCoupling(ui->grpRadioCont, m_Model->GetMovingControlContinuityModel()); makeRadioGroupCoupling(ui->grpRadioSide, m_Model->GetMovingControlSideModel()); // Set up activations activateOnFlag(this, m_Model, ColorMapModel::UIF_LAYER_ACTIVE); activateOnFlag(ui->inControlX, m_Model, ColorMapModel::UIF_CONTROL_SELECTED_IS_NOT_ENDPOINT); activateOnFlag(ui->inControlIndex, m_Model, ColorMapModel::UIF_CONTROL_SELECTED); activateOnFlag(ui->btnControlColor, m_Model, ColorMapModel::UIF_CONTROL_SELECTED); activateOnFlag(ui->btnDeleteControl, m_Model, ColorMapModel::UIF_CONTROL_SELECTED_IS_NOT_ENDPOINT); activateOnFlag(ui->inControlOpacity, m_Model, ColorMapModel::UIF_CONTROL_SELECTED); activateOnFlag(ui->btnCont, m_Model, ColorMapModel::UIF_CONTROL_SELECTED_IS_NOT_ENDPOINT); activateOnFlag(ui->btnDiscont, m_Model, ColorMapModel::UIF_CONTROL_SELECTED_IS_NOT_ENDPOINT); activateOnFlag(ui->btnLeft, m_Model, ColorMapModel::UIF_CONTROL_SELECTED_IS_DISCONTINUOUS); activateOnFlag(ui->btnRight, m_Model, ColorMapModel::UIF_CONTROL_SELECTED_IS_DISCONTINUOUS); activateOnNotFlag(ui->btnAddPreset, m_Model, ColorMapModel::UIF_PRESET_SELECTED); activateOnFlag(ui->btnDelPreset, m_Model, ColorMapModel::UIF_USER_PRESET_SELECTED); // Populate the presets PopulatePresets(); } void ColorMapInspector::onModelUpdate(const EventBucket &b) { // Get the model to update itself m_Model->Update(); if(b.HasEvent(ColorMapModel::PresetUpdateEvent())) { this->PopulatePresets(); } if(b.HasEvent(ModelUpdateEvent())) { // We don't have a coupling for the color button, so we assign the // color to it directly Vector3ui rgb = to_unsigned_int(m_Model->GetSelectedColor() * 255.0); ui->btnControlColor->setIcon(CreateColorBoxIcon(25,25,rgb)); } // Also we don't have a coupling for the current preset, so we will // update it too if(m_Model->GetLayer()) { m_PresetsUpdating = true; std::string sel = m_Model->GetSelectedPreset(); int index = ui->inPreset->findText(sel.c_str()); ui->inPreset->setCurrentIndex(index); m_PresetsUpdating = false; } } void ColorMapInspector::PromptUserForColor() { Vector3d clr = m_Model->GetSelectedColor(); QColor qc; qc.setRgbF(clr(0), clr(1), clr(2)); QColor color = QColorDialog::getColor(qc, this); m_Model->SetSelectedColor(Vector3d(color.redF(), color.greenF(), color.blueF())); } void ColorMapInspector::on_btnControlColor_clicked() { this->PromptUserForColor(); } // TODO: this should be done using a combo box coupling! void ColorMapInspector::PopulatePresets() { // Add the presets to the list m_PresetsUpdating = true; // Populte the combo box (for now, we don't have a coupling for this) PopulateColorMapPresetCombo(ui->inPreset, m_Model); // Done updating m_PresetsUpdating = false; } void ColorMapInspector::on_inPreset_currentIndexChanged(int index) { // Set the preset if(!m_PresetsUpdating) m_Model->SelectPreset(to_utf8(ui->inPreset->itemText(index))); } void ColorMapInspector::on_btnAddPreset_clicked() { // Prompt the user for input bool ok; QString input = QInputDialog::getText( this, tr("Preset Name"), tr("Enter the name for the new preset:"), QLineEdit::Normal, "", &ok); if(ok && !input.isEmpty()) { m_Model->SaveAsPreset(to_utf8(input)); } } void ColorMapInspector::on_btnDelPreset_clicked() { int sel = ui->inPreset->currentIndex(); QString seltext = ui->inPreset->itemText(sel); m_Model->DeletePreset(to_utf8(seltext)); } void ColorMapInspector::on_btnDeleteControl_clicked() { m_Model->DeleteSelectedControl(); } itksnap-3.4.0/GUI/Qt/Components/ColorMapInspector.h000066400000000000000000000021541263013355200221020ustar00rootroot00000000000000#ifndef COLORMAPINSPECTOR_H #define COLORMAPINSPECTOR_H #include "SNAPCommon.h" #include "SNAPComponent.h" #include class QtViewportReporter; class ColorMapModel; namespace Ui { class ColorMapInspector; } class ColorMapInspector : public SNAPComponent { Q_OBJECT public: explicit ColorMapInspector(QWidget *parent = 0); ~ColorMapInspector(); void SetModel(ColorMapModel *model); void PromptUserForColor(); private slots: // Slot for model updates void onModelUpdate(const EventBucket &b); void on_btnControlColor_clicked(); void on_inPreset_currentIndexChanged(int index); void on_btnAddPreset_clicked(); void on_btnDelPreset_clicked(); void on_btnDeleteControl_clicked(); private: void PopulatePresets(); Ui::ColorMapInspector *ui; // Model object ColorMapModel *m_Model; // Viewport reporter for the curve box SmartPtr m_ColorMapBoxViewportReporter; // Icons for the different presets std::map m_PresetMap; // Whether an update is being done to the presets bool m_PresetsUpdating; }; #endif // COLORMAPINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/ColorMapInspector.ui000066400000000000000000000511621263013355200222730ustar00rootroot00000000000000 ColorMapInspector 0 0 485 535 Form QGroupBox { background-origin: content; margin-top: 15px; font-weight: bold; font-size: 12px; font-family: helvetica; color: rgb(30,30,160); background-color: rgb(237,237,237); padding: 5px; border-radius: 4px; border: 1px solid rgb(130,130,130); } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; } 12 6 18 6 6 0 0 0 40 Presets: 6 2 6 Select a colormap: 0 0 0 0 -1 16 Save the current color map as a new preset + Delete the selected preset - Qt::Horizontal QSizePolicy::Fixed 8 20 0 0 Color Map Editor: 0 6 0 0 0 80 Qt::Vertical QSizePolicy::Fixed 20 16 0 0 QGroupBox { background-origin: content; margin-top: 15px; font-weight: normal; font-size: 12px; font-family: helvetica; color: black; background-color: rgb(237,237,237); padding: 5px; border-radius: 0px; border-top: 1px solid rgb(130,130,130); border-left: none; border-right:none; border-bottom:none; } Selected Control Point Properties: 12 0 Side: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 0 0 12 0 0 0 Left 0 0 Right 0 0 0 100 0 Continuous 120 0 Discontinuous Qt::Vertical 20 40 Style: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 9 Index: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Position: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 96 0 9999 16777215 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:13px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">The x-coordinate of the selected control point. The x-coordinate represents input image intensity.</span></p></body></html> QAbstractSpinBox::NoButtons false 0 Opacity: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Color: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 96 0 Choose... Qt::ToolButtonTextBesideIcon 0 0 Qt::Horizontal QSizePolicy::Fixed 40 20 24 24 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:11px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Delete the selected control point</p></body></html> Delete :/root/popup_delete_16.png 0 0 96 0 9999 16777215 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:13px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">The x-coordinate of the selected control point. The x-coordinate represents input image intensity.</span></p></body></html> QAbstractSpinBox::NoButtons false 0 Qt::Horizontal QSizePolicy::Expanding 40 2 Qt::Vertical 20 2 ColorMapBox QWidget
ColorMapBox.h
1
itksnap-3.4.0/GUI/Qt/Components/ContrastInspector.cxx000066400000000000000000000060251263013355200225370ustar00rootroot00000000000000#include "ContrastInspector.h" #include "ui_ContrastInspector.h" #include "QtStyles.h" #include "IntensityCurveModel.h" #include "QtCheckBoxCoupling.h" #include "QtSpinBoxCoupling.h" #include "QtDoubleSpinBoxCoupling.h" #include "QtWidgetArrayCoupling.h" #include "QtReporterDelegates.h" #include "QtWidgetActivator.h" #include "IntensityCurveVTKRenderer.h" #include ContrastInspector::ContrastInspector(QWidget *parent) : SNAPComponent(parent), ui(new Ui::ContrastInspector) { ui->setupUi(this); ApplyCSS(this, ":/root/itksnap.css"); // Create the viewport reporter m_CurveBoxViewportReporter = QtViewportReporter::New(); m_CurveBoxViewportReporter->SetClientWidget(ui->plotWidget); // Set up the renderer m_CurveRenderer = IntensityCurveVTKRenderer::New(); ui->plotWidget->SetRenderer(m_CurveRenderer); m_CurveRenderer->SetBackgroundColor(Vector3d(1.0, 1.0, 1.0)); } ContrastInspector::~ContrastInspector() { delete ui; } void ContrastInspector::SetModel(IntensityCurveModel *model) { // Set the model m_Model = model; // Set the model on the renderer m_CurveRenderer->SetModel(model); // Connect the viewport reporter to the model model->SetViewportReporter(m_CurveBoxViewportReporter); // Listen to model update events connectITK(m_Model, ModelUpdateEvent()); // Set up the couplings. This is all we have to do to make the spin box // play with the model! There are no callbacks to write, no event handlers // to worry about! Yay!!! makeArrayCoupling(ui->inControlX, ui->inControlY, m_Model->GetMovingControlXYModel()); // Set up couplings for window and level makeCoupling(ui->inMin, m_Model->GetIntensityRangeModel(IntensityCurveModel::MINIMUM)); makeCoupling(ui->inMax, m_Model->GetIntensityRangeModel(IntensityCurveModel::MAXIMUM)); makeCoupling(ui->inLevel, m_Model->GetIntensityRangeModel(IntensityCurveModel::LEVEL)); makeCoupling(ui->inWindow, m_Model->GetIntensityRangeModel(IntensityCurveModel::WINDOW)); // Make coupling for the control point id makeCoupling(ui->inControlId, m_Model->GetMovingControlIdModel()); // Histogram bin controls makeCoupling(ui->inBinSize, m_Model->GetHistogramBinSizeModel()); makeCoupling(ui->inCutoff, m_Model->GetHistogramCutoffModel()); makeCoupling(ui->inLogScale, m_Model->GetHistogramScaleModel()); // Couple visibility of the GL widget to the model having a layer makeWidgetVisibilityCoupling(ui->plotWidget, m_Model->GetHasLayerModel()); // Handle activations activateOnFlag(this, m_Model, IntensityCurveModel::UIF_LAYER_ACTIVE); } void ContrastInspector::on_btnRemoveControl_clicked() { m_Model->OnControlPointNumberDecreaseAction(); } void ContrastInspector::on_btnAddControl_clicked() { m_Model->OnControlPointNumberIncreaseAction(); } void ContrastInspector::on_btnReset_clicked() { m_Model->OnResetCurveAction(); } void ContrastInspector::onModelUpdate(const EventBucket &b) { m_Model->Update(); } void ContrastInspector::on_btnAuto_clicked() { m_Model->OnAutoFitWindow(); } itksnap-3.4.0/GUI/Qt/Components/ContrastInspector.h000066400000000000000000000016311263013355200221620ustar00rootroot00000000000000#ifndef CONTRASTINSPECTOR_H #define CONTRASTINSPECTOR_H #include "SNAPComponent.h" #include "SNAPCommon.h" class IntensityCurveModel; class QtViewportReporter; class IntensityCurveVTKRenderer; namespace Ui { class ContrastInspector; } class ContrastInspector : public SNAPComponent { Q_OBJECT public: explicit ContrastInspector(QWidget *parent = 0); ~ContrastInspector(); void SetModel(IntensityCurveModel *model); private slots: void on_btnRemoveControl_clicked(); void on_btnAddControl_clicked(); void on_btnReset_clicked(); // Slot for model updates void onModelUpdate(const EventBucket &b); void on_btnAuto_clicked(); private: IntensityCurveModel *m_Model; Ui::ContrastInspector *ui; // Viewport reporter for the curve box SmartPtr m_CurveBoxViewportReporter; SmartPtr m_CurveRenderer; }; #endif // CONTRASTINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/ContrastInspector.ui000066400000000000000000000575671263013355200223730ustar00rootroot00000000000000 ContrastInspector 0 0 454 477 Form QMainWindow { background-color: rgb(248,248,248); } QGroupBox { background-origin: content; margin-top: 15px; font-weight: bold; font-size: 12px; font-family: helvetica; color: rgb(30,30,160); background-color: rgb(237,237,237); padding: 5px; border-radius: 4px; border: 1px solid rgb(130,130,130); } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; } 12 6 18 6 6 Linear Contrast Adjustment: 0 12 6 6 Window: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 64 0 80 16777215 QAbstractSpinBox::NoButtons false 0 60 0 Auto Minimum: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 60 0 Reset 0 0 64 0 80 16777215 Qt::WheelFocus QAbstractSpinBox::NoButtons false 0 Qt::Horizontal 40 20 0 0 64 0 80 16777215 Qt::WheelFocus QAbstractSpinBox::NoButtons false 0 Maximum: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 64 0 80 16777215 Qt::WheelFocus QAbstractSpinBox::NoButtons false 0 Level: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Horizontal QSizePolicy::Fixed 5 20 Curve-Based Contrast Adjustment: 10 6 0 202 QFrame::StyledPanel QFrame::Raised 0 0 0 background-color:white; 10 4 0 2 0 x: 0 0 80 0 80 16777215 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:13px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">The x-coordinate of the selected control point. The x-coordinate represents input image intensity.</span></p></body></html> QAbstractSpinBox::NoButtons false 0 2 0 y: 0 0 80 0 80 16777215 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:13px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">The y-coordinate of the selected control point. The y-coordinate represents the index into the color map used to display the image. Its values are in the range [0 1].</span></p></body></html> QAbstractSpinBox::NoButtons false 0 20 0 2 0 Selected control point: 2 0 Id: 48 16777215 false 4 0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:12px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Increase the number of control points on the curve</p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:12px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Reduce the number of control points on the curve</p></body></html> - Qt::Horizontal 10 20 Histogram Display Options: 2 6 Bin size: false Qt::Horizontal 5 20 Cutoff: 1 0 80 16777215 % 0 Qt::Horizontal 5 20 Log scale QtVTKRenderWindowBox QWidget
QtVTKRenderWindowBox.h
1
inMin inMax inLevel inWindow btnReset btnAuto inControlId inControlX inControlY btnAddControl btnRemoveControl inBinSize inCutoff inLogScale
itksnap-3.4.0/GUI/Qt/Components/CursorInspector.cxx000066400000000000000000000217001263013355200222140ustar00rootroot00000000000000#ifdef WIN32 #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif //_WIN32_WINNT #define _WIN32_WINNT 0x0501 #endif //WIN32 #include "CursorInspector.h" #include "ui_CursorInspector.h" #include #include #include #include #include #include #include #include #include #include #include "SNAPQtCommon.h" #include #include #include #include #include #include #include /** This class provide the coupling properties for coupling the table of voxel intensities to QTableWidget */ class LayerCurrentVoxelInfoRowDescriptionTraits { public: typedef CurrentVoxelInfoItemSetDomain::ValueType ValueType; static QString GetText(ValueType value, const LayerCurrentVoxelInfo &desc, int col) { if(col == 0) return from_utf8(desc.LayerName); else return from_utf8(desc.IntensityValue); } static QIcon GetIcon(ValueType value, const LayerCurrentVoxelInfo &desc, int col) { return (col == 0) ? QIcon(":/root/icons8_pin_16.png") : CreateColorBoxIcon(12, 12, desc.Color); } static QVariant GetIconSignature(ValueType value, const LayerCurrentVoxelInfo &desc, int col) { // Get the RGB color return (col == 0) ? QVariant(0) : QVariant(QColor(desc.Color[0], desc.Color[1], desc.Color[2])); } }; typedef TextAndIconTableWidgetRowTraits< size_t, LayerCurrentVoxelInfo, LayerCurrentVoxelInfoRowDescriptionTraits> LayerCurrentVoxelInfoTableWidgetRowTraits; /* typedef ItemSetWidgetDomainTraits< CurrentVoxelInfoItemSetDomain, QTableWidget, LayerCurrentVoxelInfoTableWidgetRowTraits> LayerCurrentVoxelInfoDomainTraits; */ CursorInspector::CursorInspector(QWidget *parent) : SNAPComponent(parent), ui(new Ui::CursorInspector) { ui->setupUi(this); // Create an empty standard item model m_CurrentVoxelItemModel = new QStandardItemModel(this); m_CurrentVoxelItemModel->setColumnCount(2); m_CurrentVoxelItemModel->setRowCount(1); // Set the header labels QStringList header; header << "Layer" << "Intensity"; m_CurrentVoxelItemModel->setHorizontalHeaderLabels(header); ui->tableVoxelUnderCursor->setModel(m_CurrentVoxelItemModel); ui->tableVoxelUnderCursor->setAlternatingRowColors(true); ui->tableVoxelUnderCursor->setFixedWidth(160); ui->tableVoxelUnderCursor->setFixedHeight(120); ui->tableVoxelUnderCursor->setContextMenuPolicy(Qt::CustomContextMenu); #if QT_VERSION >= 0x050000 ui->tableVoxelUnderCursor->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive); ui->tableVoxelUnderCursor->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); #else ui->tableVoxelUnderCursor->horizontalHeader()->setResizeMode(0, QHeaderView::Interactive); ui->tableVoxelUnderCursor->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch); #endif ui->tableVoxelUnderCursor->setColumnWidth(0, 92); // ui->tableVoxelUnderCursor->setColumnWidth(1, 68); // Hook up the context menu connect(ui->tableVoxelUnderCursor, SIGNAL(customContextMenuRequested(QPoint)), SLOT(onContextMenuRequested(QPoint))); } CursorInspector::~CursorInspector() { delete ui; } void CursorInspector::SetModel(CursorInspectionModel *model) { m_Model = model; // Activators activateOnFlag(this, m_Model->GetParent(), UIF_BASEIMG_LOADED); // Couple to the model makeCoupling(ui->outLabelId, m_Model->GetLabelUnderTheCursorIdModel()); makeCoupling(ui->outLabelText, m_Model->GetLabelUnderTheCursorTitleModel()); makeArrayCoupling(ui->inCursorX, ui->inCursorY, ui->inCursorZ, m_Model->GetCursorPositionModel()); connectITK(m_Model->GetVoxelAtCursorModel(), DomainChangedEvent()); connectITK(m_Model->GetVoxelAtCursorModel(), DomainDescriptionChangedEvent()); } #include "MainImageWindow.h" #include "LayerInspectorDialog.h" void CursorInspector::onContextMenuRequested(QPoint pos) { m_PopupRow = ui->tableVoxelUnderCursor->rowAt(pos.y()); if(m_PopupRow >= 0) { // Instead of creating a separate context menu here, we use a context menu // from the corresponding row in the LayerInspector. MainImageWindow *winmain = findParentWidget(this); LayerInspectorDialog *inspector = winmain->GetLayerInspector(); // Find the corresponding layer LayerIterator it = m_Model->GetParent()->GetLoadedLayersSelectionModel()->GetNthLayer(m_PopupRow); // Get the menu QMenu *menu = inspector->GetLayerContextMenu(it.GetLayer()); // Show the menu if(menu) menu->popup(QCursor::pos()); } } void CursorInspector::UpdateVoxelTableRow(int i, const LayerCurrentVoxelInfo &vi) { // Get the two items to update QStandardItem *item_layer = m_CurrentVoxelItemModel->item(i, 0); QStandardItem *item_intensity = m_CurrentVoxelItemModel->item(i, 1); item_layer->setText(from_utf8(vi.LayerName)); item_intensity->setText(from_utf8(vi.IntensityValue)); item_intensity->setToolTip(from_utf8(vi.IntensityValue)); // item_layer->setForeground(QBrush(QColor(Qt::darkGray))); // item_intensity->setForeground(QBrush(QColor(Qt::darkGray))); QString tooltip_annot; // By default the color of the items is black item_layer->setForeground(QBrush(QColor(Qt::black))); item_intensity->setForeground(QBrush(QColor(Qt::black))); item_layer->setIcon(QIcon()); if(vi.isSelectedGroundLayer) { // item_layer->setIcon(QIcon(":/root/icons8_star_8.png")); QFont font = item_layer->font(); font.setBold(true); font.setItalic(false); item_layer->setFont(font); item_layer->setToolTip(from_utf8(vi.LayerName)); } else if(vi.isSticky) { // item_layer->setIcon(QIcon(":/root/icons8_pin_10.png")); QFont font = item_layer->font(); font.setBold(false); font.setItalic(true); item_layer->setFont(font); item_layer->setToolTip(QString("

%1

%2

").arg( from_utf8(vi.LayerName)).arg( "This layer is rendered as an overlay on top of other layers.")); } else { QFont font = item_layer->font(); font.setBold(false); font.setItalic(false); item_layer->setFont(font); item_layer->setToolTip(from_utf8(vi.LayerName)); } // Set the tooltip item_layer->setToolTip( QString("

%1

%2

").arg(from_utf8(vi.LayerName)).arg(tooltip_annot)); // Set the color icon QColor stored_color = qvariant_cast(item_intensity->data(Qt::UserRole)); QColor new_color = QColor(vi.Color[0], vi.Color[1], vi.Color[2]); if(new_color != stored_color) { item_intensity->setIcon(CreateColorBoxIcon(12, 12, vi.Color)); item_intensity->setData(new_color, Qt::UserRole); } } void CursorInspector::RebuildVoxelTable() { // Initialize the model m_CurrentVoxelItemModel->removeRows(0, m_CurrentVoxelItemModel->rowCount()); // Get the domain from which we are building this model int dummy; CurrentVoxelInfoItemSetDomain domain; if(m_Model->GetVoxelAtCursorModel()->GetValueAndDomain(dummy, &domain)) { // Add the rows for(LayerIterator it = domain.begin(); it != domain.end(); ++it) { QList items; items.push_back(new QStandardItem()); items.push_back(new QStandardItem()); m_CurrentVoxelItemModel->appendRow(items); this->UpdateVoxelTableRow(m_CurrentVoxelItemModel->rowCount()-1, domain.GetDescription(it)); } } } void CursorInspector::UpdateVoxelTable() { // Get the domain from which we are building this model int dummy; CurrentVoxelInfoItemSetDomain domain; if(m_Model->GetVoxelAtCursorModel()->GetValueAndDomain(dummy, &domain)) { // Update the rows int row = 0; for(LayerIterator it = domain.begin(); it != domain.end(); ++it, ++row) { this->UpdateVoxelTableRow(row, domain.GetDescription(it)); } } } void CursorInspector::onModelUpdate(const EventBucket &bucket) { m_Model->Update(); if(bucket.HasEvent(DomainChangedEvent(), m_Model->GetVoxelAtCursorModel())) { this->RebuildVoxelTable(); } else if(bucket.HasEvent(DomainDescriptionChangedEvent(), m_Model->GetVoxelAtCursorModel())) { this->UpdateVoxelTable(); } } void CursorInspector::on_tableVoxelUnderCursor_clicked(const QModelIndex &index) { // When the user clicks on an item, that item will become the visible one int item_row = index.row(); if(item_row >= 0) { // Find the corresponding layer LayerIterator it = m_Model->GetParent()->GetLoadedLayersSelectionModel()->GetNthLayer(item_row); if(!it.GetLayer()->IsSticky()) m_Model->GetParent()->GetDriver()->GetGlobalState()->SetSelectedLayerId( it.GetLayer()->GetUniqueId()); } } itksnap-3.4.0/GUI/Qt/Components/CursorInspector.h000066400000000000000000000016511263013355200216440ustar00rootroot00000000000000#ifndef CURSORINSPECTOR_H #define CURSORINSPECTOR_H #include "SNAPComponent.h" class CursorInspectionModel; class QTableWidgetItem; class QStandardItemModel; class QModelIndex; struct LayerCurrentVoxelInfo; namespace Ui { class CursorInspector; } class CursorInspector : public SNAPComponent { Q_OBJECT public: explicit CursorInspector(QWidget *parent = 0); void SetModel(CursorInspectionModel *); ~CursorInspector(); public slots: void onContextMenuRequested(QPoint pos); protected: void RebuildVoxelTable(); void UpdateVoxelTable(); void UpdateVoxelTableRow(int i, const LayerCurrentVoxelInfo &vi); private slots: void onModelUpdate(const EventBucket &bucket); void on_tableVoxelUnderCursor_clicked(const QModelIndex &index); private: Ui::CursorInspector *ui; CursorInspectionModel *m_Model; QStandardItemModel *m_CurrentVoxelItemModel; int m_PopupRow; }; #endif // CURSORINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/CursorInspector.ui000066400000000000000000000225171263013355200220360ustar00rootroot00000000000000 CursorInspector 0 0 256 527 Form * { font-size: 12px; } QSpinBox { font-size: 11px; } QLineEdit { font-size: 11px; } QComboBox { font-size: 11px; } 2 0 0 0 0 -1 Cursor position (x,y,z): 4 1 0 64 16777215 <html><head/><body><p><span style=" font-weight:600;">Cursor X coordinate</span></p><p>The X coordinate of the 3D cursor, in the image coordinate system (corresponds to columns of voxels). The first column has index 1 (one-based indexing).</p></body></html> QAbstractSpinBox::NoButtons 1 0 0 0 64 16777215 <html><head/><body><p><span style=" font-weight:600;">Cursor Y coordinate</span></p><p>The Y coordinate of the 3D cursor, in the image coordinate system (corresponds to rows of voxels). The first row has index 1 (one-based indexing).</p></body></html> QAbstractSpinBox::NoButtons 1 0 64 16777215 <html><head/><body><p><span style=" font-weight:600;">Cursor Z coordinate</span></p><p>The Z coordinate of the 3D cursor, in the image coordinate system (corresponds to slices of voxels). The first slice has index 1 (one-based indexing).</p></body></html> QAbstractSpinBox::NoButtons Qt::Vertical QSizePolicy::Fixed 20 6 -1 Intensity under cursor: Qt::Vertical QSizePolicy::Fixed 20 6 QTableView { font-size:10px; } QTableView:!active { selection-background-color: rgb(162, 204, 255); } QAbstractItemView::NoEditTriggers false QAbstractItemView::SingleSelection QAbstractItemView::SelectRows 12 12 Qt::ElideRight false 48 false 16 16 -1 Label under cursor: 4 50 16777215 <html><head/><body><p><span style=" font-weight:600;">Index of label under cursor</span></p><p>This shows the numerical value of the segmentation label under the cursor. When you save the segmentation as an image, these values are stored in the voxels of the saved image.</p></body></html> true QAbstractSpinBox::NoButtons 65536 <html><head/><body><p><span style=" font-weight:600;">Name of label under cursor</span></p><p>This shows the name assigned to the segmentation label under the cursor. Use the label editor to modify label names.</p></body></html> true Qt::Vertical 20 40 inCursorX inCursorY inCursorZ onCursorEdit() itksnap-3.4.0/GUI/Qt/Components/DisplayLayoutInspector.cxx000066400000000000000000000030261263013355200235430ustar00rootroot00000000000000#include "DisplayLayoutInspector.h" #include "ui_DisplayLayoutInspector.h" #include "DisplayLayoutModel.h" #include #include #include "GlobalUIModel.h" DisplayLayoutInspector::DisplayLayoutInspector(QWidget *parent) : QWidget(parent), ui(new Ui::DisplayLayoutInspector) { ui->setupUi(this); } DisplayLayoutInspector::~DisplayLayoutInspector() { delete ui; } void DisplayLayoutInspector::SetModel(DisplayLayoutModel *model) { m_Model = model; // Couple the view panel layout toolbar buttons to the model std::map rmap; rmap[DisplayLayoutModel::VIEW_ALL] = ui->btnFourViews; rmap[DisplayLayoutModel::VIEW_AXIAL] = ui->btnAxial; rmap[DisplayLayoutModel::VIEW_CORONAL] = ui->btnCoronal; rmap[DisplayLayoutModel::VIEW_SAGITTAL] = ui->btnSagittal; rmap[DisplayLayoutModel::VIEW_3D] = ui->btn3D; makeRadioGroupCoupling(ui->grpDisplayLayout, rmap, m_Model->GetViewPanelLayoutModel()); // Couple the radio buttons for layer layout makeRadioGroupCoupling(ui->grpLayerLayout, m_Model->GetSliceViewLayerLayoutModel()); // Couple the thumbnail size control makeCoupling(ui->inThumbSize, m_Model->GetThumbnailRelativeSizeModel()); // Tile/thumb controls deactivated for single image activateOnFlag(ui->grpLayerLayout, m_Model->GetParentModel(), UIF_MULTIPLE_BASE_LAYERS); activateOnFlag(ui->grpThumbnailSize, m_Model->GetParentModel(), UIF_MULTIPLE_BASE_LAYERS); } itksnap-3.4.0/GUI/Qt/Components/DisplayLayoutInspector.h000066400000000000000000000007411263013355200231710ustar00rootroot00000000000000#ifndef DISPLAYLAYOUTINSPECTOR_H #define DISPLAYLAYOUTINSPECTOR_H #include class DisplayLayoutModel; namespace Ui { class DisplayLayoutInspector; } class DisplayLayoutInspector : public QWidget { Q_OBJECT public: explicit DisplayLayoutInspector(QWidget *parent = 0); ~DisplayLayoutInspector(); void SetModel(DisplayLayoutModel *model); private: Ui::DisplayLayoutInspector *ui; DisplayLayoutModel *m_Model; }; #endif // DISPLAYLAYOUTINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/DisplayLayoutInspector.ui000066400000000000000000000266151263013355200233670ustar00rootroot00000000000000 DisplayLayoutInspector 0 0 172 498 Form * { font-size: 12px; } 4 0 0 0 0 -1 Display Layout: 4 QLayout::SetMaximumSize 16 0 0 4 26 26 <html><head/><body><p>Show three orthogonal slice views and the 3D view.</p></body></html> ... :/root/dl_fourviews.png true true 26 26 Only show the axial view. ... :/root/dl_axial.png true true 26 26 Only show the coronal view. ... :/root/dl_coronal.png true true 26 26 Only show the sagittal view. ... :/root/dl_sagittal.png true true 26 26 Only show the 3D view. ... :/root/dl_3d.png true true Qt::Horizontal 1 20 -1 Multiple Image Layers: -1 4 16 0 0 0 140 0 <html><head/><body><p>In <span style=" font-weight:600;">tiled</span> layout, multiple image layers are shown side by side.</p></body></html> Tiled layout :/root/layout_tile_16.png:/root/layout_tile_16.png true true Qt::ToolButtonTextBesideIcon 0 0 140 0 <html><head/><body><p>In <span style=" font-weight:600;">thumbnail</span> layout, one image layer occupies most of the available screen space, and other loaded image layers are shown as thumbnails.</p></body></html> Thumbnail layout :/root/layout_thumb_16.png:/root/layout_thumb_16.png true true Qt::ToolButtonTextBesideIcon -1 Thumbnail Size: 16 0 0 0 <html><head/><body><p>Set the size of the thumbnails relative to window size.</p></body></html> Qt::Horizontal 1 20 Qt::Vertical 20 40 btnFourViews btnAxial btnCoronal btnSagittal itksnap-3.4.0/GUI/Qt/Components/FileChooserPanelWithHistory.cxx000066400000000000000000000413471263013355200244610ustar00rootroot00000000000000#include "FileChooserPanelWithHistory.h" #include "ui_FileChooserPanelWithHistory.h" #include #include #include #include #include #include #include #include #include FileChooserPanelWithHistory::FileChooserPanelWithHistory(QWidget *parent) : QWidget(parent), ui(new Ui::FileChooserPanelWithHistory) { ui->setupUi(this); // INitialize vars m_Model = NULL; m_oracleTarget = NULL; // History menu QMenu *history = new QMenu("History", ui->btnHistory); ui->btnHistory->setMenu(history); // Set up an event filter ui->inFilename->installEventFilter(this); // Connect up the format selector to the filename connect(ui->inFormat, SIGNAL(activated(QString)), this, SLOT(setActiveFormat(QString))); } FileChooserPanelWithHistory::~FileChooserPanelWithHistory() { delete ui; } void FileChooserPanelWithHistory::populateHistory() { // Get the history string lists HistoryManager *hm = m_Model->GetDriver()->GetSystemInterface()->GetHistoryManager(); QStringList local_history = toQStringList(hm->GetLocalHistory(m_historyCategory.toStdString())); QStringList global_history = toQStringList(hm->GetGlobalHistory(m_historyCategory.toStdString())); // Fill out the menu QMenu *menu = ui->btnHistory->menu(); PopulateHistoryMenu(menu, this, SLOT(onHistorySelection()), local_history, global_history); ui->btnHistory->setEnabled(menu->actions().size() > 0); } void FileChooserPanelWithHistory::parseFilters(const QString &activeFormat) { // Clear the filters m_Filter.clear(); ui->inFormat->clear(); m_defaultFormat = activeFormat; // Split the pattern into pieces QStringList pats = m_filePattern.split(";;", QString::SkipEmptyParts); // Split each piece foreach(QString pat, pats) { // Handle patterns in parentheses QRegExp rx("(.*)\\((.*)\\)"); int pos = rx.indexIn(pat); if(pos >= 0) { // Split into title and list of extensions QString title = rx.cap(1).trimmed(); QString extliststr = rx.cap(2).trimmed(); // Store the extension QStringList extlist = extliststr.split(" "); QStringList extlistclean; foreach (QString myext, extlist) { pos = myext.indexOf("."); if(pos >= 0) extlistclean.push_back(myext.mid(pos+1)); } // Make sure every title has somethign! if(extlistclean.size() == 0) extlistclean.push_back(QString()); // Append this info m_Filter[title] = extlistclean; // Add the title to the format dropbox ui->inFormat->addItem(title); // Use this filter if(m_defaultFormat.length() == 0) m_defaultFormat = title; } } // Update the combo box this->setCurrentFormatText(m_defaultFormat); // Show or hide the format panel depending on the number of formats available ui->panelFormat->setVisible(m_Filter.size() > 1); } QString FileChooserPanelWithHistory::fixExtension() const { // This method appends the extension to the currently entered filename if the // currently entered filename does not have an extension already. This is so // that we can type in test and it gets saved as test.png QString filename = ui->inFilename->text(); QString filenameAbs = this->absoluteFilenameKeepExtension(); // Cases when we don't append the extension if(filename.length() == 0 || // No filename entered m_defaultFormat.length() == 0 || // No current format selected m_Filter[m_defaultFormat].size() == 0 || // Current format does not have any extensions QFileInfo(filenameAbs).isDir() || // Selected filename is a directory m_forceExtension == false) // User asked not to do this return filename; // Check if the filename already has one of the extensions for the selected format foreach(QString ext, m_Filter[m_defaultFormat]) { if(ext.length()) { QString eext = QString(".%1").arg(ext); if(filename.endsWith(eext)) return filename; } } // Default extension is the first extension in the accepted list QString defaultExt = m_Filter[m_defaultFormat].front(); // Is the thing the user typed in ending with a dot? Avoid having two dots if(filename.endsWith(".")) return filename + defaultExt; // Otherwise, return the filename with the default extension return filename + "." + defaultExt; } void FileChooserPanelWithHistory::initializeForOpenFile( GlobalUIModel *model, const QString &labelText, const QString &historyCategory, const QString &filePattern, const QString &initialFile, const QString &activeFormat) { // State m_Model = model; m_openMode = true; m_directoryMode = false; m_filePattern = filePattern; m_historyCategory = historyCategory; m_forceExtension = false; // Compute the suffix parseFilters(activeFormat); // Initial UI values ui->label->setText(labelText); // Populate the history this->populateHistory(); // Clear the error fields ui->outError->clear(); ui->outSavePath->clear(); // Get the working directory based on history or main image m_workingDir = GetFileDialogPath(m_Model, m_historyCategory.toStdString().c_str()); // Set the initial file if(initialFile.length()) { this->updateFilename(initialFile); } // Update the display on_inFilename_textChanged(ui->inFilename->text()); } void FileChooserPanelWithHistory::initializeForSaveFile( GlobalUIModel *model, const QString &labelText, const QString &historyCategory, const QString &filePattern, bool force_extension, const QString &initialFile, const QString &activeFormat) { // State m_Model = model; m_openMode = false; m_directoryMode = false; m_filePattern = filePattern; m_historyCategory = historyCategory; m_forceExtension = force_extension; // Compute the suffix parseFilters(activeFormat); // Initial UI values ui->label->setText(labelText); // Populate the history this->populateHistory(); // Clear the error fields ui->outError->clear(); ui->outSavePath->clear(); // Get the working directory based on history or main image m_workingDir = GetFileDialogPath(m_Model, m_historyCategory.toStdString().c_str()); // Set the initial file if(initialFile.length()) { // Update the filename and working directory based on the initial file this->updateFilename(initialFile); } else { // Initialize the dialog with a default filename QString default_basename = "Untitled"; ui->inFilename->setText(QString("%1.%2").arg(default_basename, m_Filter[m_defaultFormat].front())); } // Highlight just the filename highlightFilename(); // Update the display on_inFilename_textChanged(ui->inFilename->text()); } void FileChooserPanelWithHistory::highlightFilename() { // Select the part of the filename minus the extension QString text = ui->inFilename->text(); foreach(QString ext, m_Filter[m_defaultFormat]) { if(text.endsWith(ext, Qt::CaseInsensitive)) { ui->inFilename->setSelection(0, text.length() - (1+ext.length())); return; } } } void FileChooserPanelWithHistory::addButton(QWidget *button) { ui->wButtonPanel->layout()->addWidget(button); } void FileChooserPanelWithHistory::setCustomFormatOracle(QObject *target, const char *slot) { m_oracleTarget = target; m_oracleSlot = slot; } void FileChooserPanelWithHistory::onHistorySelection() { // Get the absolute filename QAction *action = static_cast(this->sender()); this->updateFilename(action->text()); } void FileChooserPanelWithHistory::updateFilename(QString filename) { QFileInfo fi(filename); QString new_file; // If the filename given is relative, define it relative to the working directory if(fi.isRelative()) fi = QFileInfo(m_workingDir, filename); // If the path exists, use it as the new working directory if(fi.absoluteDir().exists()) { m_workingDir = fi.absolutePath(); new_file = fi.fileName(); } else { new_file = fi.absoluteFilePath(); } // Make sure the update code executes even if the text is not changed if(new_file == ui->inFilename->text()) on_inFilename_textChanged(new_file); else ui->inFilename->setText(new_file); } QString FileChooserPanelWithHistory::absoluteFilename() const { QString fix_ext = this->fixExtension(); QFileInfo fi(fix_ext); if(fi.isAbsolute()) return fi.absoluteFilePath(); QFileInfo fi2(QDir(m_workingDir), fix_ext); return fi2.absoluteFilePath(); } QString FileChooserPanelWithHistory::absoluteFilenameKeepExtension() const { QFileInfo fi(ui->inFilename->text()); if(fi.isAbsolute()) return fi.absoluteFilePath(); QFileInfo fi2(QDir(m_workingDir), ui->inFilename->text()); return fi2.absoluteFilePath(); } QString FileChooserPanelWithHistory::activeFormat() const { return m_defaultFormat; } QString FileChooserPanelWithHistory::errorText() const { return ui->outError->text(); } void FileChooserPanelWithHistory::setErrorText(const QString &text) { ui->outError->setText(text); } void FileChooserPanelWithHistory::onFilenameAccept() { QDir myDir = QFileInfo(this->absoluteFilename()).absoluteDir(); if(myDir.exists()) UpdateFileDialogPathForCategory(m_historyCategory.toStdString().c_str(), myDir.absolutePath()); } void FileChooserPanelWithHistory::setActiveFormat(QString format) { if(format == m_defaultFormat) return; // Change the format QString oldFormat = m_defaultFormat; m_defaultFormat = format; // In open mode, we don't tweak the extension if(m_openMode) return; // Get the default new suffix QString newSuffix = m_Filter[format].front(); // If the one of the recognized extensions is currently selected, replace it QString fn = ui->inFilename->text(); foreach (QString ext, m_Filter[oldFormat]) { QString eext = QString(".%1").arg(ext); if(fn.endsWith(eext, Qt::CaseInsensitive)) { fn = fn.mid(0, fn.length() - ext.length()) + newSuffix; break; } } // Modify the extension if it was not overridden if(fn != ui->inFilename->text()) { ui->inFilename->setText(fn); } // Highlight the filename highlightFilename(); } void FileChooserPanelWithHistory::on_btnBrowse_clicked() { // Get the file name QString sel; // Where to open the dialog? QFileInfo bfi(QDir(m_workingDir), ui->inFilename->text()); if(!bfi.absoluteDir().exists()) bfi = QFileInfo(m_workingDir); QString browseDir = bfi.absoluteFilePath(); // Create a file dialog QFileDialog dialog(this, ui->label->text()); if(m_openMode) { dialog.setFileMode(QFileDialog::ExistingFile); dialog.setAcceptMode(QFileDialog::AcceptOpen); } else { dialog.setFileMode(QFileDialog::AnyFile); dialog.setAcceptMode(QFileDialog::AcceptSave); } if(browseDir.length()) { QFileInfo file_info(browseDir); if(file_info.isDir()) { dialog.setDirectory(file_info.absoluteFilePath() + "/"); } else { dialog.setDirectory(file_info.absolutePath() + "/"); dialog.selectFile(file_info.fileName()); } } // Create a single filter that combines all of the extensions for all image types // If any of the extensions is missing, there will be no filter at all QStringList flatExtensionList; QStringList formatList; QString formatEntry; bool have_empty = false; foreach(QString format, m_Filter.keys()) { QStringList formatExtensionList; foreach(QString ext, m_Filter[format]) { #ifdef __APPLE__ // On MacOS, compound extensions are a problem int pos = ext.lastIndexOf("."); if(pos >= 0) { if(m_openMode) ext = ext.replace('.','*'); } #endif if(ext.length()) { QString eext = QString("*.%1").arg(ext); flatExtensionList << eext; formatExtensionList << eext; } else { have_empty = true; formatExtensionList << "*"; } } QString line = QString("%1 (%2)").arg(format).arg(formatExtensionList.join(" ")); formatList << line; if(m_defaultFormat == format) formatEntry = line; } if(m_openMode) { QString allext = flatExtensionList.join(" "); if(!have_empty && allext.length()) { dialog.setNameFilter(allext); } } else { dialog.setNameFilters(formatList); dialog.selectNameFilter(formatEntry); } if(dialog.exec() && dialog.selectedFiles().size()) sel = dialog.selectedFiles().first(); if(sel.length()) { // Update the selection this->updateFilename(sel); } } QString FileChooserPanelWithHistory::guessFormat(const QString &text) { QString newFormat; // Try using the oracle if(m_oracleTarget) { QMetaObject::invokeMethod(m_oracleTarget, m_oracleSlot, Qt::DirectConnection, Q_RETURN_ARG(QString, newFormat), Q_ARG(QString, text)); } // If the oracle failed, try to guess if(newFormat.isNull()) { foreach(QString format, m_Filter.keys()) { foreach(QString ext, m_Filter[format]) { QString eext = QString(".%1").arg(ext); if(ext.length() && text.endsWith(eext)) { newFormat = format; break; } } } } return newFormat; } void FileChooserPanelWithHistory::setCurrentFormatText(const QString &format) { #if QT_VERSION >= 0x050000 ui->inFormat->setCurrentText(format); #else for(int i = 0; i < ui->inFormat->count(); i++) if(ui->inFormat->itemText(i) == format) { ui->inFormat->setCurrentIndex(i); break; } #endif } void FileChooserPanelWithHistory::on_inFilename_textChanged(const QString &text) { // The filename has changed. The first thing we do is to see if the filename has // an extension that matches one of our supported extensions. If it does, then // we change the active format to be that format QString format = guessFormat(absoluteFilenameKeepExtension()); if(format.length()) { m_defaultFormat = format; this->setCurrentFormatText(format); emit activeFormatChanged(format); } else if(m_openMode) { m_defaultFormat = QString(); ui->inFormat->setCurrentIndex(-1); emit activeFormatChanged(format); } // At this point the format might have been changed to match the filename // Get the fileinfo for this file QString file_ext = this->fixExtension(); QFileInfo fi(file_ext), fiwd; if(fi.isRelative()) fiwd = QFileInfo(m_workingDir, file_ext); else fiwd = fi; // Clear the output messages ui->outSavePath->clear(); ui->outError->clear(); // Changes to the text box will be used to populate the error text if(m_openMode) { // Does the file exist? if(text.length() && !fiwd.exists()) ui->outError->setText("The file does not exist"); else if(text.length() && !fiwd.isReadable()) ui->outError->setText("The file is not readable"); else if(ui->inFormat->currentIndex() == -1 && ui->inFilename->text().length()) ui->outError->setText("Unable to recognize file format"); else ui->outError->setText(""); // For relative paths, inform the user where the file will be saved in if(fi.isRelative() && !fi.isDir()) { QString saveDir = fiwd.isDir() ? fiwd.absoluteFilePath() : fiwd.absolutePath(); ui->outSavePath->setText(QString("Path: %1").arg(saveDir)); } } else { if(text.length()) { // Get the extension QString saveFile = this->fixExtension(); QFileInfo fi(saveFile); // If the file is a directory, we don't give any errors, as the user is probably just typing if(fi.isDir()) return; // For relative paths, inform the user where the file will be saved in if(fi.isRelative()) { QString saveDir = fiwd.isDir() ? fiwd.absoluteFilePath() : fiwd.absolutePath(); ui->outSavePath->setText(QString("Path: %1").arg(saveDir)); } // Does the file exist? if(fiwd.exists()) ui->outError->setText("Existing file will be overridden!"); } } emit absoluteFilenameChanged(absoluteFilename()); } bool FileChooserPanelWithHistory::eventFilter(QObject *obj, QEvent *ev) { /* if(obj == ui->inFilename) { if(ev->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(ev); if(keyEvent->key() == Qt::Key_Tab) { if(ui->inFilename->completer()->completionCount()) { ui->inFilename->completer()->complete(); return true; } } } } */ return QObject::eventFilter(obj, ev); } itksnap-3.4.0/GUI/Qt/Components/FileChooserPanelWithHistory.h000066400000000000000000000063471263013355200241070ustar00rootroot00000000000000#ifndef FILECHOOSERPANELWITHHISTORY_H #define FILECHOOSERPANELWITHHISTORY_H #include #include namespace Ui { class FileChooserPanelWithHistory; } class GlobalUIModel; /** * This component is used by different ITK-SNAP dialogs to prompt the * user for a filename, while presenting a history drop-down button and * a browse button. */ class FileChooserPanelWithHistory : public QWidget { Q_OBJECT Q_PROPERTY(QString absoluteFilename READ absoluteFilename NOTIFY absoluteFilenameChanged) Q_PROPERTY(QString activeFormat READ activeFormat NOTIFY activeFormatChanged) Q_PROPERTY(QString errorTest READ errorText WRITE setErrorText) public: explicit FileChooserPanelWithHistory(QWidget *parent = 0); ~FileChooserPanelWithHistory(); // Initialize the panel for opening a file void initializeForOpenFile( GlobalUIModel *model, const QString &labelText, const QString &historyCategory, const QString &filePattern, const QString &initialFile = QString(), const QString &activeFormat = QString()); // Initialize the panel for opening a file void initializeForSaveFile( GlobalUIModel *model, const QString &labelText, const QString &historyCategory, const QString &filePattern, bool force_extension, const QString &initialFile = QString(), const QString &activeFormat = QString()); // Add a button to the button panel (customization) void addButton(QWidget *button); // Provide a custom "oracle" to determine file format from filename void setCustomFormatOracle(QObject *target, const char *slot); // Get the filename selected QString absoluteFilename() const; // Get teh active format QString activeFormat() const; QString errorText() const; void setErrorText(const QString &text); signals: void absoluteFilenameChanged(QString text); void activeFormatChanged(QString text); public slots: // This slot should be envoked when the container dialog // accepts the filename. The path of the filename will be // recorded so the next time we invoke this dialog, the // same path will be used void onFilenameAccept(); void setActiveFormat(QString format); private slots: void on_btnBrowse_clicked(); void onHistorySelection(); void on_inFilename_textChanged(const QString &text); protected: // Populate the history void populateHistory(); // Fix the extension of the file in the dialog QString fixExtension() const; virtual bool eventFilter(QObject *obj, QEvent *ev); // Update filename based on history button or browse void updateFilename(QString filename); private: Ui::FileChooserPanelWithHistory *ui; bool m_openMode; bool m_directoryMode; bool m_forceExtension; QString m_historyCategory; QString m_filePattern; QString m_workingDir; QString m_defaultFormat; QMap m_Filter; GlobalUIModel *m_Model; void parseFilters(const QString &activeFormat = QString()); void highlightFilename(); // Get the filename selected QString absoluteFilenameKeepExtension() const; QObject *m_oracleTarget; const char *m_oracleSlot; QString guessFormat(const QString &text); void setCurrentFormatText(const QString &format); }; #endif // FILECHOOSERPANELWITHHISTORY_H itksnap-3.4.0/GUI/Qt/Components/FileChooserPanelWithHistory.ui000066400000000000000000000135601263013355200242700ustar00rootroot00000000000000 FileChooserPanelWithHistory 0 0 444 155 Form QMenu { font-size:12px; } 4 0 0 0 0 0 0 0 0 TextLabel font-size:10px; color: #7f0000; TextLabel Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 0 0 0 0 0 font-size:10px; color: rgb(91, 91, 91); TextLabel true Qt::Vertical QSizePolicy::Fixed 20 28 0 0 0 0 Qt::Horizontal 40 20 Browse... History 0 0 0 0 0 File Format: 160 0 inFilename btnBrowse btnHistory inFormat itksnap-3.4.0/GUI/Qt/Components/GeneralLayerInspector.cxx000066400000000000000000000052221263013355200233120ustar00rootroot00000000000000#include "GeneralLayerInspector.h" #include "ui_GeneralLayerInspector.h" #include "LayerGeneralPropertiesModel.h" #include "QtCheckBoxCoupling.h" #include "QtComboBoxCoupling.h" #include "QtSpinBoxCoupling.h" #include "QtSliderCoupling.h" #include "QtLineEditCoupling.h" #include "QtAbstractButtonCoupling.h" #include "QtWidgetActivator.h" #include "QtRadioButtonCoupling.h" Q_DECLARE_METATYPE(LayerGeneralPropertiesModel::DisplayMode) GeneralLayerInspector::GeneralLayerInspector(QWidget *parent) : QWidget(parent), ui(new Ui::GeneralLayerInspector) { ui->setupUi(this); } GeneralLayerInspector::~GeneralLayerInspector() { delete ui; } void GeneralLayerInspector::SetModel(LayerGeneralPropertiesModel *model) { m_Model = model; // Couple the widgets makeCoupling(ui->inMode, m_Model->GetDisplayModeModel()); makeCoupling(ui->inComponent, m_Model->GetSelectedComponentModel()); makeCoupling(ui->inComponentSlider, m_Model->GetSelectedComponentModel()); makeCoupling((QAbstractButton *)ui->btnAnimate, m_Model->GetAnimateModel()); // Couple the pin/unpin buttons std::map button_map; button_map[true] = ui->btnPin; button_map[false] = ui->btnUnpin; makeCheckableWidgetGroupCoupling(ui->grpOverlayChecks, button_map, m_Model->GetIsStickyModel()); makeCoupling(ui->inOpacity, m_Model->GetLayerOpacityModel()); makeCoupling(ui->inOpacityValue, m_Model->GetLayerOpacityModel()); makeCoupling((QAbstractButton *)ui->btnVisible, m_Model->GetLayerVisibilityModel()); makeCoupling(ui->outFilename, m_Model->GetFilenameModel()); makeCoupling(ui->inNickname, m_Model->GetNicknameModel()); activateOnFlag(ui->grpMulticomponent, m_Model, LayerGeneralPropertiesModel::UIF_MULTICOMPONENT, QtWidgetActivator::HideInactive); activateOnFlag(ui->grpComponent, m_Model, LayerGeneralPropertiesModel::UIF_CAN_SWITCH_COMPONENTS); activateOnFlag(ui->grpSecondary, m_Model, LayerGeneralPropertiesModel::UIF_IS_STICKINESS_EDITABLE, QtWidgetActivator::HideInactive); activateOnFlag(ui->grpOpacity, m_Model, LayerGeneralPropertiesModel::UIF_IS_OPACITY_EDITABLE); activateOnFlag(ui->lblOpacity, m_Model, LayerGeneralPropertiesModel::UIF_IS_OPACITY_EDITABLE); activateOnFlag(ui->btnUp, m_Model, LayerGeneralPropertiesModel::UIF_MOVABLE_UP); activateOnFlag(ui->btnDown, m_Model, LayerGeneralPropertiesModel::UIF_MOVABLE_DOWN); } void GeneralLayerInspector::on_btnUp_clicked() { m_Model->MoveLayerUp(); } void GeneralLayerInspector::on_btnDown_clicked() { m_Model->MoveLayerDown(); } itksnap-3.4.0/GUI/Qt/Components/GeneralLayerInspector.h000066400000000000000000000011231263013355200227330ustar00rootroot00000000000000#ifndef MULTICOMPONENTINSPECTOR_H #define MULTICOMPONENTINSPECTOR_H #include namespace Ui { class GeneralLayerInspector; } class GlobalUIModel; class LayerGeneralPropertiesModel; class GeneralLayerInspector : public QWidget { Q_OBJECT public: explicit GeneralLayerInspector(QWidget *parent = 0); ~GeneralLayerInspector(); void SetModel(LayerGeneralPropertiesModel *model); private slots: void on_btnUp_clicked(); void on_btnDown_clicked(); private: Ui::GeneralLayerInspector *ui; LayerGeneralPropertiesModel *m_Model; }; #endif // MULTICHANNELINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/GeneralLayerInspector.ui000066400000000000000000000537131263013355200231350ustar00rootroot00000000000000 GeneralLayerInspector 0 0 448 612 Form QGroupBox { background-origin: content; margin-top: 15px; font-weight: bold; font-size: 12px; font-family: helvetica; color: rgb(30,30,160); background-color: rgb(237,237,237); padding: 5px; border-radius: 4px; border: 1px solid rgb(130,130,130); } QGroupBox:!enabled { color: rgb(108,108,108); } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; } 18 6 18 6 6 true QToolButton { font-size:11px; } General Image Layer Properties: 6 4 6 4 6 8 <html><head/><body><p>Edit this field to assign a short nickname to this image. This information can be saved in the workspace. </p></body></html> Filename: <html><head/><body><p>This is the filename from which the image was loaded. This is a read-only field. However, you can save the image to a different file using this dialog.</p></body></html> true Nickname: true QToolButton { font-size:11px; } How to Display this Image Layer: 6 4 6 4 6 8 font-size:12pt; 4 0 0 0 0 80 0 <html><head/><body><p>Adjust the opacity of the image when it is displayed as a semi-transparent overlay.</p></body></html> QAbstractSpinBox::NoButtons % 100 <html><head/><body><p>Adjust the opacity of the image when it is displayed as a semi-transparent overlay.</p></body></html> 100 Qt::Horizontal QSlider::NoTicks 25 20 20 <html><head/><body><p>Toggle the visibility of the image when it is displayed as a semi-transparent overlay.</p></body></html> ... :/root/icons8_invisible_12.png :/root/icons8_visible_12.png:/root/icons8_invisible_12.png 12 12 true true true 6 0 0 0 0 0 0 <html><head/><body><p><span style=" font-size:11pt;">Display this image side by side with the main image.</span></p><p><br/></p><p><span style=" font-size:11pt;">This optoin is best suited for anatomical images, e.g., when displaying T1-weighted and T2-weighted MRI scans of the same subject.</span></p></body></html> Separate image :/root/icons8_unpin_12.png:/root/icons8_unpin_12.png 12 12 true true true Qt::ToolButtonTextBesideIcon 0 0 <html><head/><body><p><span style=" font-size:11pt;">Display this image as a semi-transparent overlay over other images.</span></p><p><br/></p><p><span style=" font-size:11pt;">This option is best for statistical maps. You can control the overall opacity of the overlay in this window, or customize the color map for partial opacity effects. </span></p></body></html> Semi-transparent overlay :/root/icons8_pin_12.png:/root/icons8_pin_12.png 12 12 true true Qt::ToolButtonTextBesideIcon 0 0 0 0 Qt::Horizontal 40 20 <html><head/><body><p><span style=" font-size:11pt;">Place this image layer earlier in the workspace. This affects the order in which the layers are layed out and rendered. The order is most important when more than one semi-transparent overlay is loaded.</span></p></body></html> Move up :/root/icons8_up_12.png:/root/icons8_up_12.png 12 12 Qt::ToolButtonTextBesideIcon <html><head/><body><p><span style=" font-size:11pt;">Place this image layer later in the workspace. This affects the order in which the layers are layed out and rendered. The order is most important when more than one semi-transparent overlay is loaded.</span></p></body></html> Move down :/root/icons8_down_12.png:/root/icons8_down_12.png 12 12 Qt::ToolButtonTextBesideIcon Display as: Qt::Vertical QSizePolicy::Fixed 20 10 Display order: true Overlay opacity: true How to Visualize Multiple Image Components: 6 4 6 4 6 8 Display mode: <html><head/><body><p>Multi-component images have more than one value for each voxel. These values may represent measurements taken at different time points, different chromatic components, etc. ITK-SNAP offers multiple ways to visualize this multi-dimensional information. You can choose to visualize just one component, or a summary function of the components, such as magnitude, average, or maximum. In the special case of three-component images, the components can be rendered as red, green and blue channels (RGB).</p></body></html> 0 20 font-size:12pt; 4 0 0 0 0 80 0 <html><head/><body><p>Select the image component to visualize.</p></body></html> <html><head/><body><p>Select the image component to visualize.</p></body></html> Qt::Horizontal 20 20 <html><head/><body><p>Toggle animation mode.</p><p>When enabled, the display will automatically loop over the image components at one-second intervals.</p></body></html> ... :/root/icons8_film_reel_12.png:/root/icons8_film_reel_12.png 12 12 true true true Component: Qt::Vertical 20 40 itksnap-3.4.0/GUI/Qt/Components/HistoryQListModel.cxx000066400000000000000000000106321263013355200224510ustar00rootroot00000000000000#include "HistoryQListModel.h" #include "HistoryManager.h" #include "SystemInterface.h" #include "IRISApplication.h" #include "GlobalUIModel.h" #include #include #include "SNAPQtCommon.h" #include "LatentITKEventNotifier.h" #include "itkImageFileReader.h" #include #include #include #include #include #include #include #include HistoryQListModel::HistoryQListModel(QObject *parent) : QStandardItemModel(parent) { m_Model = NULL; QPixmap pixmap(128,128); pixmap.fill(QColor(Qt::lightGray)); m_DummyIcon = QIcon(pixmap); } void HistoryQListItem::setItem( GlobalUIModel *model, const QString &history_entry) { // Set the name to the short name QString short_name = QFileInfo(history_entry).fileName(); this->setText(short_name); // Set the filename this->setToolTip(history_entry); this->setData(history_entry, Qt::UserRole); QPixmap dummy(128, 128); dummy.fill(Qt::black); this->setIcon(QIcon(dummy)); // At the moment, these are hard-coded this->setSizeHint(QSize(188,144)); // Deal with the icon later std::string hist_str = to_utf8(history_entry); std::string thumbnail = model->GetDriver()->GetSystemInterface()->GetThumbnailAssociatedWithFile(hist_str.c_str()); m_IconFilename = from_utf8(thumbnail); // TODO: for debugging change 0 to a random number QTimer::singleShot(0, this, SLOT(onTimer())); } void HistoryQListItem::onTimer() { // Construct a string from the filenane and the timestamp QString key = QString("%1::%2") .arg(m_IconFilename) .arg(QFileInfo(m_IconFilename).lastModified().toString()); QPixmap *pixmap = QPixmapCache::find(key); if(pixmap) this->setIcon(QIcon(*pixmap)); else { try { // Load the icon using ITK to avoid this really annoying warning // from the PNG library. The only problem is that QIcon caches typedef itk::RGBAPixel PNGPixelType; typedef itk::Image PNGSliceType; typedef itk::ImageFileReader PNGReaderType; SmartPtr reader = PNGReaderType::New(); reader->SetFileName(to_utf8(m_IconFilename).c_str()); reader->Update(); // Need to load the icon SmartPtr slice = reader->GetOutput(); int w = slice->GetBufferedRegion().GetSize()[0]; int h = slice->GetBufferedRegion().GetSize()[1]; QImage image(w, h, QImage::Format_ARGB32); PNGPixelType *input = slice->GetBufferPointer(); QRgb *output = reinterpret_cast(image.bits()); for(int i = 0; i < slice->GetPixelContainer()->Size(); i++) { *output++ = qRgba(input[i].GetRed(), input[i].GetGreen(), input[i].GetBlue(), input[i].GetAlpha()); } QPixmap load_pixmap = QPixmap::fromImage(image); this->setIcon(QIcon(load_pixmap)); QPixmapCache::insert(key, load_pixmap); } catch(itk::ExceptionObject &exc) { QPixmap dummy(128, 128); dummy.fill(Qt::black); this->setIcon(QIcon(dummy)); QPixmapCache::insert(key, dummy); } } } void HistoryQListModel::rebuildModel() { HistoryManager::AbstractHistoryModel *hmodel = m_Model->GetDriver()->GetHistoryManager()->GetGlobalHistoryModel(m_HistoryName); std::vector history = hmodel->GetValue(); this->setColumnCount(1); this->setRowCount(history.size()); // We need to parse the history in reverse order (but why?) for(int i = 0; i < history.size(); i++) { // Create a standard item to hold this HistoryQListItem *si = new HistoryQListItem(); si->setItem(m_Model, from_utf8(history[history.size() - 1 - i])); this->setItem(i, 0, si); } } void HistoryQListModel::Initialize( GlobalUIModel *model, const std::string &category) { m_Model = model; m_HistoryName = category; // Get the property models for the local and global histories HistoryManager::AbstractHistoryModel *hmodel = m_Model->GetDriver()->GetHistoryManager()->GetGlobalHistoryModel(m_HistoryName); LatentITKEventNotifier::connect( hmodel, ValueChangedEvent(), this, SLOT(onModelUpdate(EventBucket))); // Cache the history this->rebuildModel(); } void HistoryQListModel::onModelUpdate(const EventBucket &bucket) { this->beginResetModel(); this->rebuildModel(); this->endResetModel(); } itksnap-3.4.0/GUI/Qt/Components/HistoryQListModel.h000066400000000000000000000023561263013355200221020ustar00rootroot00000000000000#ifndef HISTORYQLISTMODEL_H #define HISTORYQLISTMODEL_H #include "SNAPCommon.h" #include #include #include #include #include #include #include class EventBucket; class GlobalUIModel; class HistoryQListItem : public QObject, public QStandardItem { Q_OBJECT public: virtual void setItem(GlobalUIModel *model, const QString &history_entry); protected slots: void onTimer(); protected: QString m_IconFilename; }; /** QT model used to display an item from the image history as an entry in the table of recently loaded images */ class HistoryQListModel : public QStandardItemModel { Q_OBJECT public: explicit HistoryQListModel(QObject *parent = 0); void Initialize(GlobalUIModel *, const std::string &category); public slots: void onModelUpdate(const EventBucket &bucket); protected: void rebuildModel(); static void updateIcon(QStandardItem *item); // Need a pointer to the model GlobalUIModel *m_Model; // The name of the history std::string m_HistoryName; // Dummy icon QIcon m_DummyIcon; // List of standard items for concurrent code QList m_ItemList; }; #endif // HISTORYQLISTMODEL_H itksnap-3.4.0/GUI/Qt/Components/ImageInfoInspector.cxx000066400000000000000000000031571263013355200226030ustar00rootroot00000000000000#include "ImageInfoInspector.h" #include "ui_ImageInfoInspector.h" #include "ImageInfoModel.h" #include "QtWidgetArrayCoupling.h" #include "QtLineEditCoupling.h" #include "QtSpinBoxCoupling.h" ImageInfoInspector::ImageInfoInspector(QWidget *parent) : SNAPComponent(parent), ui(new Ui::ImageInfoInspector) { ui->setupUi(this); } ImageInfoInspector::~ImageInfoInspector() { delete ui; } void ImageInfoInspector::SetModel(ImageInfoModel *model) { // Store the model m_Model = model; // Create the traits objects for the various fields. This allows us to // specify the precision used to display these values FixedPrecisionRealToTextFieldWidgetTraits tr_real(4); makeArrayCoupling(ui->outDimX, ui->outDimY, ui->outDimZ, m_Model->GetImageDimensionsModel()); makeArrayCoupling(ui->outSpacingX, ui->outSpacingY, ui->outSpacingZ, m_Model->GetImageSpacingModel(), tr_real); makeArrayCoupling(ui->outOriginX, ui->outOriginY, ui->outOriginZ, m_Model->GetImageOriginModel(), tr_real); makeArrayCoupling(ui->outItkX, ui->outItkY, ui->outItkZ, m_Model->GetImageItkCoordinatesModel(), tr_real); makeArrayCoupling(ui->outNiftiX, ui->outNiftiY, ui->outNiftiZ, m_Model->GetImageNiftiCoordinatesModel(), tr_real); makeArrayCoupling(ui->inVoxX, ui->inVoxY, ui->inVoxZ, m_Model->GetImageVoxelCoordinatesModel()); makeArrayCoupling(ui->outMin, ui->outMax, m_Model->GetImageMinMaxModel(), tr_real); makeCoupling(ui->outRAI, m_Model->GetImageOrientationModel()); } itksnap-3.4.0/GUI/Qt/Components/ImageInfoInspector.h000066400000000000000000000006761263013355200222330ustar00rootroot00000000000000#ifndef IMAGEINFOINSPECTOR_H #define IMAGEINFOINSPECTOR_H #include class ImageInfoModel; namespace Ui { class ImageInfoInspector; } class ImageInfoInspector : public SNAPComponent { Q_OBJECT public: explicit ImageInfoInspector(QWidget *parent = 0); ~ImageInfoInspector(); void SetModel(ImageInfoModel *model); private: ImageInfoModel *m_Model; Ui::ImageInfoInspector *ui; }; #endif // IMAGEINFOINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/ImageInfoInspector.ui000066400000000000000000000637361263013355200224270ustar00rootroot00000000000000 ImageInfoInspector 0 0 450 523 Form QGroupBox { background-origin: content; margin-top: 15px; font-weight: normal; font-size: 12px; color: black; padding: 5px; border-radius: 0px; border-top: 1px solid rgb(130,130,130); border-left: none; border-right:none; border-bottom:none; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; } QWidget:read-only { background-color:rgb(240,240,240); } QDoubleSpinBox:read-only { background-color:rgb(240,240,240); } 6 6 18 6 6 Dimensions 6 Qt::Horizontal 40 20 20 0 x: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 16777215 true 20 0 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 16777215 true 20 0 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 16777215 true Voxel Spacing 6 Qt::Horizontal 40 20 20 0 x: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 16777215 true 20 0 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 16777215 true 20 0 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 16777215 true Origin and Orientation 6 Qt::Horizontal 40 20 20 0 x: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 16777215 true 20 0 Orientation (RAI) code: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 80 16777215 true 80 16777215 true 80 16777215 true Reorient... 3D Cursor Position 6 20 0 x: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 x: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 z: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 y: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 20 0 x: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 64 0 false QAbstractSpinBox::UpDownArrows 64 0 false QAbstractSpinBox::UpDownArrows 64 0 false QAbstractSpinBox::UpDownArrows 0 0 80 0 80 16777215 true 0 0 80 0 80 16777215 true 0 0 80 0 80 16777215 true 80 16777215 true 80 16777215 true 0 0 0 0 80 16777215 true 9 Voxel coordinates Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true 1 0 9 World (ITK) coordinates Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true 1 0 9 World (NIFTI) coordinates Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter true Intensity Range 6 Qt::Horizontal 40 20 20 0 min: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 96 0 64 16777215 true 48 0 max: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 96 0 64 16777215 true Qt::Vertical 20 40 itksnap-3.4.0/GUI/Qt/Components/LabelInspector.cxx000066400000000000000000000027021263013355200217570ustar00rootroot00000000000000#include "LabelInspector.h" #include "ui_LabelInspector.h" #include #include "GlobalUIModel.h" #include "IRISException.h" #include "IRISApplication.h" #include "QtComboBoxCoupling.h" #include "QtCheckBoxCoupling.h" #include "QtSliderCoupling.h" #include "QtSpinBoxCoupling.h" LabelInspector::LabelInspector(QWidget *parent) : SNAPComponent(parent), ui(new Ui::LabelInspector) { ui->setupUi(this); ui->inForeLabel->setIconSize(QSize(16,16)); ui->inBackLabel->setIconSize(QSize(16,16)); // Connect to the action in the menubar // ui->btnEdit->setAction("actionLabel_Editor"); } LabelInspector::~LabelInspector() { delete ui; } void LabelInspector ::SetModel(GlobalUIModel *model) { // Get the model m_Model = model; // Attach to quick list // ui->quickList->SetModel(model); // Use couplings where we can makeCoupling(ui->inOpacity, m_Model->GetSegmentationOpacityModel()); makeCoupling(ui->inOpacityValue, m_Model->GetSegmentationOpacityModel()); // makeCoupling(ui->chkVisible, m_Model->GetSegmentationVisibilityModel()); // Couple the color label combo box. The actual logic for how the labels are // mapped to color labels is handled in QtComboBoxCoupling.h makeCoupling(ui->inForeLabel, m_Model->GetGlobalState()->GetDrawingColorLabelModel()); // Couple the draw over label combo box. makeCoupling(ui->inBackLabel, m_Model->GetGlobalState()->GetDrawOverFilterModel()); } itksnap-3.4.0/GUI/Qt/Components/LabelInspector.h000066400000000000000000000006701263013355200214060ustar00rootroot00000000000000#ifndef LABELINSPECTOR_H #define LABELINSPECTOR_H #include "SNAPComponent.h" namespace Ui { class LabelInspector; } class LabelInspector : public SNAPComponent { Q_OBJECT public: explicit LabelInspector(QWidget *parent = 0); ~LabelInspector(); // Set the model void SetModel(GlobalUIModel *model); public slots: private slots: private: Ui::LabelInspector *ui; GlobalUIModel *m_Model; }; #endif // LABELINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/LabelInspector.ui000066400000000000000000000277461263013355200216110ustar00rootroot00000000000000 LabelInspector 0 0 164 136 0 0 16777215 16777215 Form 0 0 0 0 0 QLabel { font-size: 12px; } QComboBox { font-size:12px; padding: 0px 0px 0px 5px; } QSpinBox { font-size: 11px; } 0 0 0 0 0 -1 Active label: <html><head/><body><p><span style=" font-weight:600;">Active Segmentation Label (&lt;,&gt;)</span></p><p>Select the label used for segmentation operations, including manual and automatic tools. When segmentations are generated, they will be assigned the active segmentation label. </p></body></html> QAbstractItemView::item { spacing:10; } false QComboBox::InsertAtBottom Hello Qt::Vertical QSizePolicy::Fixed 20 6 -1 Paint over: <html><head/><body><p><span style=" font-weight:600;">&quot;Paint Over&quot; Mask</span></p><p>Determines how segmentation operations such as drawing polygons affect voxels in the segmentation image.<br/></p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td><p><span style=" text-decoration: underline;">Paint over mask</span></p></td><td><p><span style=" text-decoration: underline;">Which voxels are affected:</span></p></td></tr><tr><td><p>All labels</p></td><td><p>All voxels</p></td></tr><tr><td><p>All visible labels &nbsp;&nbsp; </p></td><td><p>All voxels except those labeled with a hidden label</p></td></tr><tr><td><p>Clear label</p></td><td><p>Voxels that have not been assigned a label already</p></td></tr><tr><td><p>Specific label</p></td><td><p>Voxels that have the specific label </p></td></tr></table></body></html> 0 0 0 0 0 Qt::Vertical QSizePolicy::Fixed 20 6 -1 Overall label opacity: 4 0 0 0 0 <html><head/><body><p><span style=" font-weight:600;">Label Opacity (A,S,D keys)</span></p><p>Change the overall opacity of segmentation labels.<br></p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td style=" padding-left:5; padding-right:5; padding-top:0; padding-bottom:0;"><p>A,D</p></td><td style=" padding-left:5; padding-right:5; padding-top:0; padding-bottom:0;"><p>Make labels less/more opaque</p></td></tr><tr><td style=" padding-left:5; padding-right:5; padding-top:0; padding-bottom:0;"><p>S</p></td><td style=" padding-left:5; padding-right:5; padding-top:0; padding-bottom:0;"><p>Toggle label visibility on and off</p></td></tr></table></body></html> true QAbstractSpinBox::NoButtons <html><head/><body><p><span style=" font-weight:600;">Label Opacity (A,S,D keys)</span></p><p>Change the overall opacity of segmentation labels.<br></p><table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"><tr><td style=" padding-left:5; padding-right:5; padding-top:0; padding-bottom:0;"><p>A,D</p></td><td style=" padding-left:5; padding-right:5; padding-top:0; padding-bottom:0;"><p>Make labels less/more opaque</p></td></tr><tr><td style=" padding-left:5; padding-right:5; padding-top:0; padding-bottom:0;"><p>S</p></td><td style=" padding-left:5; padding-right:5; padding-top:0; padding-bottom:0;"><p>Toggle label visibility on and off</p></td></tr></table></body></html> 0 100 1 10 50 Qt::Horizontal QSlider::NoTicks 25 0 0 0 0 0 itksnap-3.4.0/GUI/Qt/Components/LabelMiniInspector.cxx000066400000000000000000000027761263013355200226070ustar00rootroot00000000000000#include "LabelMiniInspector.h" #include "ui_LabelMiniInspector.h" #include #include "GlobalUIModel.h" #include "IRISException.h" #include "IRISApplication.h" #include "QtComboBoxCoupling.h" #include "QtCheckBoxCoupling.h" #include "QtSliderCoupling.h" #include "QtSpinBoxCoupling.h" #include "QtAbstractButtonCoupling.h" LabelMiniInspector::LabelMiniInspector(QWidget *parent) : SNAPComponent(parent), ui(new Ui::LabelMiniInspector) { ui->setupUi(this); ui->inForeLabel->setIconSize(QSize(16,16)); ui->inBackLabel->setIconSize(QSize(16,16)); } LabelMiniInspector::~LabelMiniInspector() { delete ui; } void LabelMiniInspector ::SetModel(GlobalUIModel *model) { // Get the model m_Model = model; // Use couplings where we can makeCoupling(ui->inOpacity, m_Model->GetSegmentationOpacityModel()); makeCoupling(ui->inOpacityValue, m_Model->GetSegmentationOpacityModel()); makeCoupling((QAbstractButton *)ui->toolButton, m_Model->GetSegmentationVisibilityModel()); // Couple the color label combo box. The actual logic for how the labels are // mapped to color labels is handled in QtComboBoxCoupling.h makeCoupling(ui->inForeLabel, m_Model->GetGlobalState()->GetDrawingColorLabelModel()); // Couple the draw over label combo box. makeCoupling(ui->inBackLabel, m_Model->GetGlobalState()->GetDrawOverFilterModel()); // Couple the inversion checkbox makeCoupling(ui->cbInvert, m_Model->GetGlobalState()->GetPolygonInvertModel()); } itksnap-3.4.0/GUI/Qt/Components/LabelMiniInspector.h000066400000000000000000000007301263013355200222200ustar00rootroot00000000000000#ifndef LABELMINIINSPECTOR_H #define LABELMINIINSPECTOR_H #include "SNAPComponent.h" namespace Ui { class LabelMiniInspector; } class LabelMiniInspector : public SNAPComponent { Q_OBJECT public: explicit LabelMiniInspector(QWidget *parent = 0); ~LabelMiniInspector(); // Set the model void SetModel(GlobalUIModel *model); public slots: private slots: private: Ui::LabelMiniInspector *ui; GlobalUIModel *m_Model; }; #endif // LABELMINIINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/LabelMiniInspector.ui000066400000000000000000000217131263013355200224120ustar00rootroot00000000000000 LabelMiniInspector 0 0 169 171 0 0 16777215 16777215 Form * { font-size: 12px; } QAbstractItemView::item { padding:4px; } 2 0 -1 50 false Foreground label: QAbstractItemView::item { spacing:10; } false QComboBox::InsertAtBottom Qt::Vertical QSizePolicy::Fixed 20 4 -1 Background label: 0 0 Qt::Horizontal QSizePolicy::Fixed 40 20 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Helvetica'; font-size:12px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Inverts some segmentation operations, such as polygon drawing, by applying the segmentation to the outside of the drawn region, rather than inside.</p></body></html> Draw inverted Qt::Vertical QSizePolicy::Fixed 20 4 0 0 -1 Overall label opacity: 4 0 QAbstractSpinBox::NoButtons % <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Helvetica'; font-size:12px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Set the overall opacity of segmentation labels.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">[A]: Make more transparent</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">[D]: Make more opaque</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">[S]: Toggle on/off</p></body></html> 0 100 1 10 50 Qt::Horizontal QSlider::TicksBelow 25 ... :/root/popup_delete_16.png :/root/popup_ok_16.png:/root/popup_delete_16.png true true itksnap-3.4.0/GUI/Qt/Components/LabelSelectionButton.cxx000066400000000000000000000217111263013355200231330ustar00rootroot00000000000000#include "LabelSelectionButton.h" #include "GlobalUIModel.h" #include "AbstractModel.h" #include "LatentITKEventNotifier.h" #include "GlobalState.h" #include "IRISApplication.h" #include "ColorLabelTable.h" #include #include "SNAPQtCommon.h" #include #include #include #include LabelSelectionButton::LabelSelectionButton(QWidget *parent) : QToolButton(parent) { LabelSelectionButtonPopupMenu *menu = new LabelSelectionButtonPopupMenu(this); this->setMenu(menu); this->setPopupMode(QToolButton::InstantPopup); this->setIconSize(QSize(22,22)); } void LabelSelectionButton::SetModel(GlobalUIModel *model) { m_Model = model; LatentITKEventNotifier::connect( m_Model->GetGlobalState()->GetDrawingColorLabelModel(), IRISEvent(), this, SLOT(onModelUpdate(const EventBucket&))); LatentITKEventNotifier::connect( m_Model->GetGlobalState()->GetDrawOverFilterModel(), IRISEvent(), this, SLOT(onModelUpdate(const EventBucket&))); LabelSelectionButtonPopupMenu *menu = static_cast(this->menu()); menu->SetModel(model); this->UpdateAppearance(); } void LabelSelectionButton::onModelUpdate(const EventBucket &bucket) { this->UpdateAppearance(); } #if QT_VERSION < 0x050000 #define QRegularExpression QRegExp #endif void LabelSelectionButton::UpdateAppearance() { ColorLabelTable *clt = m_Model->GetDriver()->GetColorLabelTable(); LabelType fg = m_Model->GetGlobalState()->GetDrawingColorLabel(); DrawOverFilter bg = m_Model->GetGlobalState()->GetDrawOverFilter(); // Draw a split button this->setIcon(CreateLabelComboIcon(20, 20, fg, bg, clt)); // Update the tooltip QString tooltip = this->toolTip(); tooltip.replace(QRegularExpression(".*"), QString("%1").arg(GetTitleForColorLabel(fg, clt))); tooltip.replace(QRegularExpression(".*"), QString("%1").arg(GetTitleForDrawOverFilter(bg, clt))); this->setToolTip(tooltip); } /* void LabelSelectionButton::UpdateAppearanceOld() { int w = 16; QPixmap pm(w, w); QPainter qp(&pm); QPolygon poly_fg, poly_bg; poly_fg << QPoint(0, 0) << QPoint(w-1,0) << QPoint(0,w-1) << QPoint(0,0); poly_bg << QPoint(0, 0) << QPoint(w-1,0) << QPoint(w-1,w-1) << QPoint(0,w-1) << QPoint(0,0); LabelType fg = m_Model->GetGlobalState()->GetDrawingColorLabel(); DrawOverFilter bg = m_Model->GetGlobalState()->GetDrawOverFilter(); ColorLabel lfg = m_Model->GetDriver()->GetColorLabelTable()->GetColorLabel(fg); ColorLabel lbg = m_Model->GetDriver()->GetColorLabelTable()->GetColorLabel(bg.DrawOverLabel); QBrush brush_fg(QColor(lfg.GetRGB(0), lfg.GetRGB(1), lfg.GetRGB(2))); QBrush brush_bg(QColor(lbg.GetRGB(0), lbg.GetRGB(1), lbg.GetRGB(2))); qp.setPen(Qt::black); qp.setBrush(brush_bg); qp.drawPolygon(poly_bg); qp.setBrush(brush_fg); qp.drawPolygon(poly_fg); // Draw a split button this->setIcon(QIcon(pm)); } */ #include "ColorLabelQuickListWidget.h" #include #include #include class ColorLabelQuickListWidgetAction : public QWidgetAction { public: ColorLabelQuickListWidgetAction(QWidget *parent) : QWidgetAction(parent) { QWidget *topWidget = new QWidget(parent); QVBoxLayout *lo = new QVBoxLayout(topWidget); lo->setContentsMargins(4,4,4,4); lo->setSpacing(2); m_Widget = new ColorLabelQuickListWidget(parent); lo->addWidget(new QLabel("Quick palette:"),0,Qt::AlignLeft); lo->addWidget(m_Widget, 0, Qt::AlignCenter); this->setDefaultWidget(topWidget); } irisGetMacro(Widget, ColorLabelQuickListWidget *) protected: ColorLabelQuickListWidget *m_Widget; }; LabelSelectionButtonPopupMenu::LabelSelectionButtonPopupMenu(QWidget *parent) : QMenu(parent) { // Add the foreground and background label selectors m_SubForeground = this->addMenu("Active label:"); m_SubBackground = this->addMenu("Paint over:"); this->addSeparator(); // Create a QAction wrapped around the recent labels menu ColorLabelQuickListWidgetAction *action = new ColorLabelQuickListWidgetAction(this); this->m_Recent = action->GetWidget(); this->addAction(action); this->setStyleSheet("font-size: 12px;"); connect(m_SubForeground, SIGNAL(triggered(QAction*)), SLOT(onForegroundAction(QAction*))); connect(m_SubBackground, SIGNAL(triggered(QAction*)), SLOT(onBackgroundAction(QAction*))); connect(m_Recent, SIGNAL(actionTriggered(QAction*)), this, SLOT(close())); } void LabelSelectionButtonPopupMenu::SetModel(GlobalUIModel *model) { m_Model = model; // Listen to changes in active label, active label set LatentITKEventNotifier::connect( model->GetGlobalState()->GetDrawingColorLabelModel(), IRISEvent(), this, SLOT(onModelUpdate(const EventBucket &))); LatentITKEventNotifier::connect( model->GetGlobalState()->GetDrawOverFilterModel(), IRISEvent(), this, SLOT(onModelUpdate(const EventBucket &))); // Configure the recents menu m_Recent->SetModel(model); this->UpdateMenu(); // Update the timestamp m_LastUpdateTime.Modified(); } void LabelSelectionButtonPopupMenu::UpdateMenu() { m_SubForeground->clear(); m_SubBackground->clear(); ColorLabelTable *clt = m_Model->GetDriver()->GetColorLabelTable(); DrawOverFilter dof[] = { DrawOverFilter(PAINT_OVER_ALL, 0), DrawOverFilter(PAINT_OVER_VISIBLE, 0) }; for(int i = 0; i < 2; i++) { QIcon icon = CreateColorBoxIcon(16, 16, GetBrushForDrawOverFilter(dof[i], clt)); QString name = GetTitleForDrawOverFilter(dof[i], clt); QAction *action = m_SubBackground->addAction(icon, name); action->setData(QVariant::fromValue(dof[i])); action->setCheckable(true); } m_SubBackground->addSeparator(); typedef ColorLabelTable::ValidLabelMap ValidMap; const ColorLabelTable::ValidLabelMap &vmap = clt->GetValidLabels(); for(ValidMap::const_iterator it = vmap.begin(); it != vmap.end(); ++it) { const ColorLabel &cl = it->second; QIcon icon = CreateColorBoxIcon(16, 16, GetBrushForColorLabel(cl)); QString name = GetTitleForColorLabel(cl); QAction *action_fg = m_SubForeground->addAction(icon, name); action_fg->setData(it->first); action_fg->setCheckable(true); QAction *action_bg = m_SubBackground->addAction(icon, name); action_bg->setData( QVariant::fromValue(DrawOverFilter(PAINT_OVER_ONE, it->first))); action_bg->setCheckable(true); } } void LabelSelectionButtonPopupMenu::UpdateCurrents() { ColorLabelTable *clt = m_Model->GetDriver()->GetColorLabelTable(); LabelType fg = m_Model->GetGlobalState()->GetDrawingColorLabel(); DrawOverFilter bg = m_Model->GetGlobalState()->GetDrawOverFilter(); // Go through and check/uncheck all actions QList afg = m_SubForeground->actions(); for(int i = 0; i < afg.size(); i++) { QAction *action = afg.at(i); int label = action->data().toInt(); const ColorLabel &cl = clt->GetColorLabel(label); // Check if the color label has updated if(cl.GetTimeStamp() > m_LastUpdateTime) { action->setIcon(CreateColorBoxIcon(16, 16, GetBrushForColorLabel(cl))); action->setText(GetTitleForColorLabel(cl)); } if(label == fg) { action->setChecked(true); m_SubForeground->setIcon(action->icon()); } else { action->setChecked(false); } } // Go through and check/uncheck all actions QList abg = m_SubBackground->actions(); for(int i = 0; i < abg.size(); i++) { QAction *action = abg.at(i); if(action->isSeparator()) continue; DrawOverFilter dfi = qvariant_cast(action->data()); if(dfi.CoverageMode == PAINT_OVER_ONE) { const ColorLabel &cl = clt->GetColorLabel(dfi.DrawOverLabel); // Check if the color label has updated if(cl.GetTimeStamp() > m_LastUpdateTime) { action->setIcon(CreateColorBoxIcon(16, 16, GetBrushForColorLabel(cl))); action->setText(GetTitleForColorLabel(cl)); } } if(dfi == bg) { action->setChecked(true); m_SubBackground->setIcon(action->icon()); } else { action->setChecked(false); } } m_LastUpdateTime.Modified(); } void LabelSelectionButtonPopupMenu::onModelUpdate(const EventBucket &bucket) { if(bucket.HasEvent(DomainChangedEvent())) { this->UpdateMenu(); this->UpdateCurrents(); } else if(bucket.HasEvent(ValueChangedEvent()) || bucket.HasEvent(DomainDescriptionChangedEvent())) { this->UpdateCurrents(); } } void LabelSelectionButtonPopupMenu::onForegroundAction(QAction *action) { int label = action->data().toInt(); m_Model->GetGlobalState()->SetDrawingColorLabel(label); } void LabelSelectionButtonPopupMenu::onBackgroundAction(QAction *action) { DrawOverFilter dof = qvariant_cast(action->data()); m_Model->GetGlobalState()->SetDrawOverFilter(dof); } itksnap-3.4.0/GUI/Qt/Components/LabelSelectionButton.h000066400000000000000000000026621263013355200225640ustar00rootroot00000000000000#ifndef LABELSELECTIONBUTTON_H #define LABELSELECTIONBUTTON_H #include #include #include #include "itkTimeStamp.h" class GlobalUIModel; class EventBucket; class ColorLabelQuickListWidget; class LabelSelectionButtonRecentPanel : public QWidgetAction { }; class LabelSelectionButtonPopupMenu : public QMenu { Q_OBJECT public: LabelSelectionButtonPopupMenu(QWidget *parent); void SetModel(GlobalUIModel *model); public slots: void onModelUpdate(const EventBucket &bucket); void onForegroundAction(QAction *action); void onBackgroundAction(QAction *action); protected: void UpdateMenu(); void UpdateCurrents(); QMenu *m_SubForeground, *m_SubBackground; ColorLabelQuickListWidget *m_Recent; GlobalUIModel *m_Model; itk::TimeStamp m_LastUpdateTime; }; /** * @brief The LabelSelectionButton class * This is an attempt to create a nice looking button with a menu that can * be used to select labels. The button, when pressed, shows a menu where * the user can quickly choose among available labels, including recently * used labels. */ class LabelSelectionButton : public QToolButton { Q_OBJECT public: explicit LabelSelectionButton(QWidget *parent = 0); void SetModel(GlobalUIModel *model); signals: public slots: void onModelUpdate(const EventBucket &bucket); protected: GlobalUIModel *m_Model; void UpdateAppearance(); }; #endif // LABELSELECTIONBUTTON_H itksnap-3.4.0/GUI/Qt/Components/LatentITKEventNotifier.cxx000066400000000000000000000116121263013355200233520ustar00rootroot00000000000000#include "LatentITKEventNotifier.h" #include #include #include LatentITKEventNotifierCleanup ::LatentITKEventNotifierCleanup(QObject *parent) : QObject(parent) { m_Source = NULL; } LatentITKEventNotifierCleanup ::~LatentITKEventNotifierCleanup() { if(m_Source) { m_Source->RemoveObserver(m_Tag); m_Source->RemoveObserver(m_DeleteTag); } } void LatentITKEventNotifierCleanup ::SetSource(itk::Object *source, unsigned long tag) { // Store the source m_Source = source; m_Tag = tag; // Listen for delete events on the source m_DeleteTag = AddListenerConst( source, itk::DeleteEvent(), this, &LatentITKEventNotifierCleanup::DeleteCallback); } void LatentITKEventNotifierCleanup ::DeleteCallback(const itk::Object *object, const itk::EventObject &evt) { #ifdef SNAP_DEBUG_EVENTS if(flag_snap_debug_events) { std::cout << "DELETE CALLBACK from " << object->GetNameOfClass() << " [" << typeid(*object).name() << "]" << " event " << evt.GetEventName() << std::endl << std::flush; } #endif // Forget the source. m_Source = NULL; } LatentITKEventNotifierHelper ::LatentITKEventNotifierHelper(QObject *parent) : QObject(parent) { // Emitting itkEvent will result in onQueuedEvent being called when // control returns to the main Qt loop QObject::connect(this, SIGNAL(itkEvent()), this, SLOT(onQueuedEvent()), Qt::QueuedConnection); } void LatentITKEventNotifierHelper ::Callback(itk::Object *object, const itk::EventObject &evt) { #ifdef SNAP_DEBUG_EVENTS if(flag_snap_debug_events) { std::cout << "QUEUE Event " << evt.GetEventName() << " from " << object->GetNameOfClass() << " [" << object << "] " << " for " << parent()->metaObject()->className() << " named '" << qPrintable(parent()->objectName()) << "'" << std::endl << std::flush; } #endif // Register this event m_Bucket.PutEvent(evt, object); // Emit signal emit itkEvent(); // Call parent's update // QApplication::postEvent(this, new QEvent(QEvent::User), 1000); } void LatentITKEventNotifierHelper ::onQueuedEvent() { static int invocation = 0; if(!m_Bucket.IsEmpty()) { #ifdef SNAP_DEBUG_EVENTS std::string class_name = parent()->metaObject()->className(); std::string object_name = qPrintable(parent()->objectName()); if(flag_snap_debug_events) { std::cout << "SEND " << m_Bucket << " to " << class_name << " named '" << object_name << "'" << std::endl << std::flush; } #endif ++invocation; // Send the event to the target object - immediate emit dispatchEvent(m_Bucket); // Empty the bucket, so the rest of the events are ignored m_Bucket.Clear(); } } /* bool LatentITKEventNotifierHelper ::event(QEvent *event) { if(event->type() == QEvent::User) { if(!m_Bucket.IsEmpty()) { // Send the event to the target object - immediate emit dispatchEvent(m_Bucket); // Empty the bucket, so the rest of the events are ignored m_Bucket.Clear(); } return true; } else return false; } */ void LatentITKEventNotifier ::connect(itk::Object *source, const itk::EventObject &evt, QObject *target, const char *slot) { // Call common implementation LatentITKEventNotifierHelper *c = doConnect(evt, target, slot); // Listen to events from the source unsigned long tag = AddListener(source, evt, c, &LatentITKEventNotifierHelper::Callback); // Create an cleaner as a child of the helper LatentITKEventNotifierCleanup *clean = new LatentITKEventNotifierCleanup(c); clean->SetSource(source, tag); } LatentITKEventNotifierHelper* LatentITKEventNotifier ::doConnect(const itk::EventObject &evt, QObject *target, const char *slot) { // It is possible that there is already a helper attached to the target // object. In that case, we can economize by reusing that helper LatentITKEventNotifierHelper *c = target->findChild(); if(!c) { // Here the helper becomes property of QObject target, so that if the // target is deleted, the helper will also go away c = new LatentITKEventNotifierHelper(target); } // Connect to the target qobject. // VERY IMPORTANT: this uses Qt::UniqueConnection, otherwise the slot will get // called every time that an event is hooked up to the slot, making the slot be // called sometimes as many as six times per event. This may have been a factor in // the laggy GUI performance before QObject::connect(c, SIGNAL(dispatchEvent(const EventBucket &)), target, slot, Qt::UniqueConnection); return c; } void LatentITKEventNotifier ::disconnect(itk::Object *source, unsigned long tag) { source->RemoveObserver(tag); } itksnap-3.4.0/GUI/Qt/Components/LatentITKEventNotifier.h000066400000000000000000000045731263013355200230070ustar00rootroot00000000000000#ifndef LATENTITKEVENTNOTIFIER_H #define LATENTITKEVENTNOTIFIER_H #include #include "EventBucket.h" #include class LatentITKEventNotifierHelper : public QObject { Q_OBJECT public: explicit LatentITKEventNotifierHelper(QObject *parent = 0); void Callback(itk::Object *object, const itk::EventObject &evt); // bool event(QEvent *event); public slots: void onQueuedEvent(); signals: void itkEvent(); void dispatchEvent(const EventBucket &bucket); protected: EventBucket m_Bucket; }; /** This object is in charge of cleaning up when the parent QObject is deleted. This makes sure that the itk::Object being observed does not try to send commands to a non-existing observer. It also makes sure that if the source itk::Object is deleted, it will not be accessed */ class LatentITKEventNotifierCleanup : public QObject { Q_OBJECT public: explicit LatentITKEventNotifierCleanup(QObject *parent = 0); ~LatentITKEventNotifierCleanup(); void SetSource(itk::Object *source, unsigned long tag); void DeleteCallback(const itk::Object *object, const itk::EventObject &evt); protected: itk::Object *m_Source; unsigned long m_Tag, m_DeleteTag; }; /** This class is used to hook up Qt widgets to the itk event system used by the upstream objects. */ class LatentITKEventNotifier { public: /** Map itk events originating from object source to a slot in the object target. The signature of slot is void mySlot(const EventBucket &b). The slot will be called after the control has returned to the Qt main loop, and until then, events fired by the source object are pooled into a bucket. This bucket will be passed to the slot. Connection is handled by a helper object that becomes the child of the target qWidget. This helper object will automatically disconnect from the target object if the target qWidget is destroyed. The helper object will also automatically disappear if the source itk::Object is deleted. */ static void connect(itk::Object *source, const itk::EventObject &evt, QObject *target, const char *slot); static void disconnect(itk::Object *source, unsigned long tag); private: static LatentITKEventNotifierHelper *doConnect( const itk::EventObject &evt, QObject *target, const char *slot); }; #endif // LATENTITKEVENTNOTIFIER_H itksnap-3.4.0/GUI/Qt/Components/LayerInspectorRowDelegate.cxx000066400000000000000000000445211263013355200241440ustar00rootroot00000000000000#include "LayerInspectorRowDelegate.h" #include "ui_LayerInspectorRowDelegate.h" #include "ImageWrapperBase.h" #include "LayerTableRowModel.h" #include "SNAPQtCommon.h" #include "QtAbstractButtonCoupling.h" #include "QtLabelCoupling.h" #include "QtSliderCoupling.h" #include "QtActionGroupCoupling.h" #include "QtActionCoupling.h" #include "QtSliderCoupling.h" #include #include #include #include #include #include #include "QtWidgetActivator.h" #include "QtCursorOverride.h" #include "GlobalUIModel.h" #include "ImageIODelegates.h" #include "ImageIOWizard.h" #include "MainImageWindow.h" #include "SaveModifiedLayersDialog.h" #include "DisplayMappingPolicy.h" #include "ColorMap.h" #include "ColorMapModel.h" class QAction; OpacitySliderAction::OpacitySliderAction(QWidget *parent) : QWidgetAction(parent) { } QWidget *OpacitySliderAction::createWidget(QWidget *parent) { m_Container = new QWidget(parent); m_Slider = new QSlider(); m_Slider->setOrientation(Qt::Horizontal); QHBoxLayout *lo = new QHBoxLayout(); lo->setContentsMargins(27, 4, 4, 4); lo->setSpacing(10); lo->addWidget(new QLabel("Opacity:")); lo->addWidget(m_Slider); m_Container->setLayout(lo); return m_Container; } QString LayerInspectorRowDelegate::m_SliderStyleSheetTemplate; LayerInspectorRowDelegate::LayerInspectorRowDelegate(QWidget *parent) : SNAPComponent(parent), ui(new Ui::LayerInspectorRowDelegate) { ui->setupUi(this); // Confirugre the popup menu m_PopupMenu = new QMenu(this); m_PopupMenu->setStyleSheet("font-size:11px;"); // Add the save/close actions m_PopupMenu->addAction(ui->actionSave); m_PopupMenu->addAction(ui->actionClose); m_PopupMenu->addSeparator(); m_PopupMenu->addAction(ui->actionAutoContrast); m_PopupMenu->addAction(ui->actionContrast_Inspector); m_PopupMenu->addSeparator(); // Add the color map menu m_ColorMapMenu = m_PopupMenu->addMenu("Color Map"); m_SystemPresetActionGroup = NULL; // Add the component selection menu m_DisplayModeMenu = m_PopupMenu->addMenu("Multi-Component Display"); m_DisplayModeActionGroup = NULL; m_PopupMenu->addSeparator(); m_PopupMenu->addAction(ui->actionPin_layer); m_PopupMenu->addAction(ui->actionUnpin_layer); // Create a slider for the layer opacity in the context menu m_OverlayOpacitySlider = new QSlider(m_PopupMenu); m_OverlayOpacitySlider->setOrientation(Qt::Horizontal); m_OverlayOpacitySliderAction = new WidgetWithLabelAction(this); m_OverlayOpacitySliderAction->setWidget(m_OverlayOpacitySlider); m_OverlayOpacitySliderAction->setLabelText("Opacity: "); m_PopupMenu->addAction(m_OverlayOpacitySliderAction); // Create a menu listing the loaded overlays m_OverlaysMenu = m_PopupMenu->addMenu("Overlays"); // Placeholder for image processing commands m_PopupMenu->addSeparator(); QMenu *processMenu = m_PopupMenu->addMenu("Image Processing"); processMenu->addAction(ui->actionTextureFeatures); // set up an event filter ui->inLayerOpacity->installEventFilter(this); // Load the style sheet template if(!m_SliderStyleSheetTemplate.length()) { QFile qf(":/root/fancyslider.css"); if(qf.open(QFile::ReadOnly)) { m_SliderStyleSheetTemplate = QString(qf.readAll()); } } // Initialize the state m_Selected = false; m_Hover = false; UpdateBackgroundPalette(); } LayerInspectorRowDelegate::~LayerInspectorRowDelegate() { delete ui; } void LayerInspectorRowDelegate::SetModel(LayerTableRowModel *model) { m_Model = model; makeCoupling(ui->inLayerOpacity, model->GetLayerOpacityModel()); makeCoupling(ui->outLayerNickname, model->GetNicknameModel()); makeCoupling(ui->outComponent, model->GetComponentNameModel()); makeCoupling(m_OverlayOpacitySlider, model->GetLayerOpacityModel()); makeCoupling((QAbstractButton *) ui->btnVisible, model->GetVisibilityToggleModel()); makeCoupling((QAbstractButton *) ui->btnSticky, model->GetStickyModel()); const QtWidgetActivator::Options opt_hide = QtWidgetActivator::HideInactive; activateOnFlag(ui->actionUnpin_layer, model, LayerTableRowModel::UIF_UNPINNABLE, opt_hide); activateOnFlag(ui->actionPin_layer, model, LayerTableRowModel::UIF_PINNABLE, opt_hide); activateOnAnyFlags(ui->btnSticky, model, LayerTableRowModel::UIF_UNPINNABLE, LayerTableRowModel::UIF_PINNABLE, opt_hide); activateOnFlag(m_OverlayOpacitySliderAction, model, LayerTableRowModel::UIF_OPACITY_EDITABLE, opt_hide); activateOnFlag(m_ColorMapMenu, model, LayerTableRowModel::UIF_COLORMAP_ADJUSTABLE, opt_hide); activateOnFlag(m_DisplayModeMenu, model, LayerTableRowModel::UIF_MULTICOMPONENT, opt_hide); activateOnFlag(ui->outComponent, model, LayerTableRowModel::UIF_MULTICOMPONENT, opt_hide); // makeActionVisibilityCoupling(ui->actionUnpin_layer, model->GetStickyModel()); // makeActionVisibilityCoupling(ui->actionPin_layer, model->GetStickyModel(), true); // makeActionVisibilityCoupling(m_OverlayOpacitySliderAction, model->GetStickyModel()); // Hook up some activations activateOnFlag(ui->btnVisible, model, LayerTableRowModel::UIF_OPACITY_EDITABLE, opt_hide); activateOnFlag(ui->inLayerOpacity, model, LayerTableRowModel::UIF_OPACITY_EDITABLE, opt_hide); // activateOnFlag(ui->btnMoveUp, model, LayerTableRowModel::UIF_MOVABLE_UP); // activateOnFlag(ui->btnMoveDown, model, LayerTableRowModel::UIF_MOVABLE_DOWN); activateOnFlag(ui->actionClose, model, LayerTableRowModel::UIF_CLOSABLE); activateOnFlag(ui->actionAutoContrast, model, LayerTableRowModel::UIF_CONTRAST_ADJUSTABLE); // Hook up the colormap and the slider's style sheet connectITK(m_Model->GetLayer(), WrapperChangeEvent()); OnNicknameUpdate(); ApplyColorMap(); // Listen to changes in all layer organization and metadata, as this affects the list // of overlays shown in the context menu connectITK(m_Model->GetParentModel()->GetDriver(), LayerChangeEvent()); connectITK(m_Model->GetParentModel()->GetDriver(), WrapperMetadataChangeEvent()); // Listen to preset changes from the color map model connectITK(m_Model->GetParentModel()->GetColorMapModel(), ColorMapModel::PresetUpdateEvent()); // Listen to changes in the currently selected layer in GlobalState connectITK(m_Model->GetParentModel()->GetGlobalState()->GetSelectedLayerIdModel(), ValueChangedEvent()); // Update the color map menu UpdateColorMapMenu(); // Update the component menu UpdateComponentMenu(); // Update the overlays this->UpdateOverlaysMenu(); } ImageWrapperBase *LayerInspectorRowDelegate::GetLayer() const { // No model? No layer. if(!m_Model) return NULL; // Must update the model, because the layer might have been deleted // and we must make sure that we return a clean layer m_Model->Update(); return m_Model->GetLayer(); } void LayerInspectorRowDelegate::UpdateBackgroundPalette() { // Set up a pallete for the background QPalette palette; QLinearGradient linearGradient(QPointF(0, 0), QPointF(0, this->height())); if(m_Selected && m_Hover) { linearGradient.setColorAt(0, QColor(180,180,215)); linearGradient.setColorAt(1, QColor(200,200,235)); } else if(m_Selected) { linearGradient.setColorAt(0, QColor(190,190,225)); linearGradient.setColorAt(1, QColor(210,210,245)); } else if(m_Hover) { linearGradient.setColorAt(0, QColor(225,225,235)); linearGradient.setColorAt(1, QColor(245,245,255)); } else { linearGradient.setColorAt(0, QColor(235,235,235)); linearGradient.setColorAt(1, QColor(255,255,255)); } QBrush brush(linearGradient); palette.setBrush(QPalette::Window, brush); ui->frame->setPalette(palette); // Also set the font for the label if(ui->outLayerNickname->font().bold() != m_Selected) { QFont font = ui->outLayerNickname->font(); font.setBold(m_Selected); ui->outLayerNickname->setFont(font); } } void LayerInspectorRowDelegate::setSelected(bool value) { if(m_Selected != value) { m_Selected = value; emit selectionChanged(value); // Update selection in the model m_Model->SetSelected(value); // Update the look and feel this->UpdateBackgroundPalette(); // Update! this->update(); } } QAction *LayerInspectorRowDelegate::saveAction() const { return ui->actionSave; } QAction *LayerInspectorRowDelegate::closeAction() const { return ui->actionClose; } QMenu *LayerInspectorRowDelegate::contextMenu() const { return this->m_PopupMenu; } void LayerInspectorRowDelegate::enterEvent(QEvent *) { m_Hover = true; this->UpdateBackgroundPalette(); } void LayerInspectorRowDelegate::leaveEvent(QEvent *) { m_Hover = false; this->UpdateBackgroundPalette(); } void LayerInspectorRowDelegate::mousePressEvent(QMouseEvent *) { this->setSelected(true); } void LayerInspectorRowDelegate::mouseReleaseEvent(QMouseEvent *) { this->setSelected(true); } void LayerInspectorRowDelegate::contextMenuEvent(QContextMenuEvent *evt) { m_PopupMenu->popup(evt->globalPos()); } bool LayerInspectorRowDelegate::eventFilter(QObject *, QEvent *evt) { if(evt->type() == QEvent::FocusIn) { if(!this->selected()) { this->setSelected(true); return true; } } if(evt->type() == QEvent::MouseButtonPress) { if(!this->selected()) this->setSelected(true); return false; } return false; } void LayerInspectorRowDelegate::UpdateColorMapMenu() { // The presets are available from the color map model. We can use them // regardless of the row that is currently selected ColorMapModel *cmm = m_Model->GetParentModel()->GetColorMapModel(); ColorMapPresetManager *pm = cmm->GetPresetManager(); // Get the system and user presets ColorMapModel::PresetList pSystem, pUser; cmm->GetPresets(pSystem, pUser); // Remove all of the existing actions m_ColorMapMenu->clear(); if(m_SystemPresetActionGroup) delete m_SystemPresetActionGroup; // Create a map from preset names to actions std::map actionMap; // Add all of the system presets m_SystemPresetActionGroup = new QActionGroup(this); // Create the actions for the system presets for(unsigned int i = 0; i < pSystem.size(); i++) { QIcon icon = CreateColorMapIcon(16, 16, pm->GetPreset(pSystem[i])); QAction *action = m_SystemPresetActionGroup->addAction(icon, from_utf8(pSystem[i])); action->setCheckable(true); actionMap[pSystem[i]] = action; } // Add a separator to the action group m_SystemPresetActionGroup->addAction("")->setSeparator(true); // Add the user presets to the action group for(unsigned int i = 0; i < pUser.size(); i++) { QIcon icon = CreateColorMapIcon(16, 16, pm->GetPreset(pUser[i])); QAction *action = m_SystemPresetActionGroup->addAction(icon, from_utf8(pUser[i])); action->setCheckable(true); actionMap[pUser[i]] = action; } // Connect the action group to the model makeActionGroupCoupling(m_SystemPresetActionGroup, actionMap, m_Model->GetColorMapPresetModel()); // Add the action group to the menu m_ColorMapMenu->addActions(m_SystemPresetActionGroup->actions()); // Add the link to color map editor m_ColorMapMenu->addSeparator(); m_ColorMapMenu->addAction(ui->actionColor_Map_Editor); } void LayerInspectorRowDelegate::UpdateComponentMenu() { m_DisplayModeMenu->clear(); if(m_DisplayModeActionGroup) delete m_DisplayModeActionGroup; // Create a map from display modes to actions std::map actionMap; // Add all of the system presets m_DisplayModeActionGroup = new QActionGroup(this); // Get the list of all available display modes from the model const LayerTableRowModel::DisplayModeList &modes = m_Model->GetAvailableDisplayModes(); // Create the actions for the display modes LayerTableRowModel::DisplayModeList::const_iterator it; for(it = modes.begin(); it != modes.end(); it++) { MultiChannelDisplayMode mode = *it; // Insert some separators into the menu if(mode.SelectedScalarRep == SCALAR_REP_MAGNITUDE || mode.UseRGB) m_DisplayModeMenu->addSeparator(); // Create an action QAction *action = m_DisplayModeMenu->addAction( from_utf8(m_Model->GetDisplayModeString(mode))); action->setCheckable(true); m_DisplayModeActionGroup->addAction(action); actionMap[mode] = action; } // Hook up with the ctions makeActionGroupCoupling(m_DisplayModeActionGroup, actionMap, m_Model->GetDisplayModeModel()); } #include "GenericImageData.h" #include "LayerInspectorDialog.h" void LayerInspectorRowDelegate::UpdateOverlaysMenu() { int k = 0; // Clear the overlays menu m_OverlaysMenu->clear(); // If the current layer is sticky, disable the menu if(m_Model->GetLayer() && !m_Model->GetLayer()->IsSticky()) { // Get the current image data GenericImageData *gid = m_Model->GetParentModel()->GetDriver()->GetCurrentImageData(); // Find the number of sticky layers for(LayerIterator it = gid->GetLayers(OVERLAY_ROLE | SNAP_ROLE); !it.IsAtEnd(); ++it) { if(it.GetLayer()->IsSticky()) { // Find the context menu for that layer LayerInspectorDialog *insp = findParentWidget(this); QMenu *menu = insp->GetLayerContextMenu(it.GetLayer()); if(menu) { m_OverlaysMenu->addAction(menu->menuAction()); k++; } } } } // Enable overlays menu if it's not empty m_OverlaysMenu->menuAction()->setVisible(k > 0); } void LayerInspectorRowDelegate::OnNicknameUpdate() { // Update things that depend on the nickname QString name = from_utf8(m_Model->GetNickname()); ui->actionSave->setText(QString("Save image \"%1\" ...").arg(name)); ui->actionSave->setToolTip(ui->actionSave->text()); ui->actionClose->setText(QString("Close image \"%1\"").arg(name)); ui->actionClose->setToolTip(ui->actionClose->text()); ui->outLayerNickname->setToolTip(name); m_PopupMenu->setTitle(name); } void LayerInspectorRowDelegate::onModelUpdate(const EventBucket &bucket) { if(bucket.HasEvent(WrapperDisplayMappingChangeEvent())) { this->ApplyColorMap(); } if(bucket.HasEvent(WrapperMetadataChangeEvent(), m_Model->GetLayer())) { this->OnNicknameUpdate(); } if(bucket.HasEvent(ColorMapModel::PresetUpdateEvent())) { this->UpdateColorMapMenu(); } if(bucket.HasEvent(LayerChangeEvent(), m_Model->GetParentModel()->GetDriver()) || bucket.HasEvent(WrapperChangeEvent(), m_Model->GetParentModel()->GetDriver())) { this->UpdateOverlaysMenu(); } if(bucket.HasEvent(ValueChangedEvent(), m_Model->GetParentModel()->GetGlobalState()->GetSelectedLayerIdModel())) { unsigned long sid = m_Model->GetParentModel()->GetGlobalState()->GetSelectedLayerId(); this->setSelected(m_Model->GetLayer() && sid == m_Model->GetLayer()->GetUniqueId()); } } void LayerInspectorRowDelegate::mouseMoveEvent(QMouseEvent *) { this->setSelected(true); } void LayerInspectorRowDelegate::ApplyColorMap() { ColorMap *cm = m_Model->GetLayer()->GetDisplayMapping()->GetColorMap(); if(cm) { QStringList stops; for(int i = 0; i < cm->GetNumberOfCMPoints(); i++) { ColorMap::CMPoint cmp = cm->GetCMPoint(i); for(int side = 0; side < 2; side++) { if((i == 0 && side == 0) || (i == cm->GetNumberOfCMPoints()-1 && side == 1) || (cmp.m_Type == ColorMap::CONTINUOUS && side == 1)) continue; QString cmstr = QString("stop: %1 rgba(%2, %3, %4, %5)") .arg(cmp.m_Index) .arg(cmp.m_RGBA[side][0]).arg(cmp.m_RGBA[side][1]) .arg(cmp.m_RGBA[side][2]).arg(cmp.m_RGBA[side][3]); stops << cmstr; } } QString gradient = QString("qlineargradient(x1:0, y1:0, x2:1, y2:0, %1);") .arg(stops.join(",")); QString stylesheet = m_SliderStyleSheetTemplate; stylesheet.replace("#gradient#", gradient); ui->inLayerOpacity->setStyleSheet(stylesheet); } } void LayerInspectorRowDelegate::on_btnMenu_pressed() { m_PopupMenu->popup(QCursor::pos()); ui->btnMenu->setDown(false); } /* void LayerInspectorRowDelegate::on_btnMoveUp_clicked() { m_Model->MoveLayerUp(); } void LayerInspectorRowDelegate::on_btnMoveDown_pressed() { m_Model->MoveLayerDown(); } */ void LayerInspectorRowDelegate::on_actionSave_triggered() { // Create a model for IO SmartPtr model = m_Model->CreateIOWizardModelForSave(); // Interactive ImageIOWizard wiz(this); wiz.SetModel(model); wiz.exec(); } void LayerInspectorRowDelegate::on_actionClose_triggered() { // Should we prompt for a single layer or all layers? ImageWrapperBase *prompted_layer = m_Model->IsMainLayer() ? NULL : m_Model->GetLayer(); // Prompt for changes if(SaveModifiedLayersDialog::PromptForUnsavedChanges(m_Model->GetParentModel(), prompted_layer)) { m_Model->CloseLayer(); } } void LayerInspectorRowDelegate::onColorMapPresetSelected() { } void LayerInspectorRowDelegate::on_actionAutoContrast_triggered() { m_Model->AutoAdjustContrast(); } void LayerInspectorRowDelegate::on_actionTextureFeatures_triggered() { QtCursorOverride c(Qt::WaitCursor); m_Model->GenerateTextureFeatures(); } void LayerInspectorRowDelegate::on_actionPin_layer_triggered() { m_Model->SetSticky(true); } void LayerInspectorRowDelegate::on_actionUnpin_layer_triggered() { m_Model->SetSticky(false); } void LayerInspectorRowDelegate::on_actionContrast_Inspector_triggered() { emit contrastInspectorRequested(); } WidgetWithLabelAction::WidgetWithLabelAction(QWidget *parent) : QWidgetAction(parent) { m_Container = new QWidget(parent); m_Label = new QLabel(); QHBoxLayout *lo = new QHBoxLayout(); lo->setContentsMargins(27, 4, 4, 4); lo->setSpacing(10); lo->addWidget(m_Label); m_Container->setLayout(lo); this->setDefaultWidget(m_Container); connect(this, SIGNAL(changed()), this, SLOT(onChanged())); } void WidgetWithLabelAction::setWidget(QWidget *widget) { m_Container->layout()->addWidget(widget); } void WidgetWithLabelAction::setLabelText(const QString &text) { m_Label->setText(text); } void WidgetWithLabelAction::onChanged() { m_Container->setVisible(this->isVisible()); m_Container->setEnabled(this->isEnabled()); } void LayerInspectorRowDelegate::on_actionColor_Map_Editor_triggered() { emit colorMapInspectorRequested(); } itksnap-3.4.0/GUI/Qt/Components/LayerInspectorRowDelegate.h000066400000000000000000000074321263013355200235710ustar00rootroot00000000000000#ifndef LAYERINSPECTORROWDELEGATE_H #define LAYERINSPECTORROWDELEGATE_H #include #include #include "SNAPCommon.h" #include class LayerTableRowModel; class ImageWrapperBase; class QMenu; class QActionGroup; class QContextMenuEvent; class QSlider; class QLabel; class OpacitySliderAction : public QWidgetAction { Q_OBJECT public: OpacitySliderAction(QWidget *parent = 0); QSlider *GetSlider() { return m_Slider; } QWidget *GetContainer() { return m_Container; } protected: virtual QWidget *createWidget(QWidget *parent); QSlider *m_Slider; QWidget *m_Container; }; class WidgetWithLabelAction : public QWidgetAction { Q_OBJECT public: WidgetWithLabelAction(QWidget *parent = 0); void setWidget(QWidget *widget); virtual void setLabelText(const QString &text); protected slots: void onChanged(); protected: QWidget *m_Container; QLabel *m_Label; }; namespace Ui { class LayerInspectorRowDelegate; } /** * @brief The LayerInspectorRowDelegate class * This compound widget is used to display a row in the table of layers in * the layer inspector. The name LayerInspectorRowDelegate is not quite right * as we are not actually using a Qt list widget to display layers. Instead * these widgets are organized in a QScrollArea. */ class LayerInspectorRowDelegate : public SNAPComponent { Q_OBJECT Q_PROPERTY(bool selected READ selected WRITE setSelected USER true NOTIFY selectionChanged) public: explicit LayerInspectorRowDelegate(QWidget *parent = 0); ~LayerInspectorRowDelegate(); void SetModel(LayerTableRowModel *model); ImageWrapperBase *GetLayer() const; bool selected() const { return m_Selected; } // Access the actions for this item QAction * saveAction() const; QAction * closeAction() const; // Get the context menu for this item QMenu *contextMenu() const; void enterEvent(QEvent *); void leaveEvent(QEvent *); void mousePressEvent(QMouseEvent *); void mouseMoveEvent(QMouseEvent *); void mouseReleaseEvent(QMouseEvent *); void contextMenuEvent(QContextMenuEvent *evt); bool eventFilter(QObject *, QEvent *); public slots: void setSelected(bool value); virtual void onModelUpdate(const EventBucket &bucket); signals: void selectionChanged(bool); void contrastInspectorRequested(); void colorMapInspectorRequested(); private slots: void on_btnMenu_pressed(); /* void on_btnMoveUp_clicked(); void on_btnMoveDown_pressed(); */ void on_actionSave_triggered(); void on_actionClose_triggered(); void onColorMapPresetSelected(); void on_actionAutoContrast_triggered(); void on_actionTextureFeatures_triggered(); void on_actionPin_layer_triggered(); void on_actionUnpin_layer_triggered(); void on_actionContrast_Inspector_triggered(); void on_actionColor_Map_Editor_triggered(); private: Ui::LayerInspectorRowDelegate *ui; // It is very important here that we keep a smart pointer to the model, // rather than a regular pointer. That's because the layer itself may // be deleted, in which case, there will be noone kept holding the model. SmartPtr m_Model; bool m_Selected; bool m_Hover; static QString m_SliderStyleSheetTemplate; // A popup menu QMenu *m_PopupMenu; // A submenu for the color maps QMenu *m_ColorMapMenu, *m_DisplayModeMenu, *m_OverlaysMenu; // Slider for opacity that lives in the menu QSlider *m_OverlayOpacitySlider; WidgetWithLabelAction *m_OverlayOpacitySliderAction; // An action group for the system presets QActionGroup* m_SystemPresetActionGroup, *m_DisplayModeActionGroup; void ApplyColorMap(); void UpdateBackgroundPalette(); void UpdateColorMapMenu(); void UpdateComponentMenu(); void UpdateOverlaysMenu(); void OnNicknameUpdate(); }; #endif // LAYERINSPECTORROWDELEGATE_H itksnap-3.4.0/GUI/Qt/Components/LayerInspectorRowDelegate.ui000066400000000000000000000351271263013355200237610ustar00rootroot00000000000000 LayerInspectorRowDelegate 0 0 200 57 200 16777215 Form QToolButton::checked { border: none; background-color: rgba(0,0,0,0); }; QToolButton::hovered { border: 4px solid gray; }; QMenu::item { font-size:9px; } 0 0 0 0 0 true QFrame::NoFrame QFrame::Raised 0 0 0 0 0 4 0 158 16777215 -1 false font-size:12px; TextLabel 2 0 0 0 20 20 <html><head/><body><p>&quot;Pin&quot; or &quot;unpin&quot; the image layer. When an image layer is pinned, it is rendered as an overlay on top of other images. </p></body></html> false ... :/root/icons8_unpin_12.png :/root/icons8_pin_12.png:/root/icons8_unpin_12.png 12 12 true false true Qt::Horizontal QSizePolicy::Fixed 6 20 20 20 <html><head/><body><p>Toggle between making the image layer visible or invisible.</p></body></html> false ... :/root/icons8_invisible_12.png :/root/icons8_visible_12.png:/root/icons8_invisible_12.png 12 12 true true true 64 0 64 20 <html><head/><body><p>Change the opacity of the image layer.</p></body></html> false QSlider::groove:horizontal { border: 1px solid #bbb; background: white; height: 6px; border-radius: 3px; } QSlider::sub-page:horizontal { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #66e, stop: 1 #bbf); background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, stop: 0 #bbf, stop: 1 #55f); border: 1px solid #777; height: 6px; border-radius: 3px; } QSlider::add-page:horizontal { background: #fff; border: 1px solid #777; height: 6px; border-radius: 3px; } QSlider::handle:horizontal { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #eee, stop:1 #ccc); border: 1px solid #777; width: 13px; margin-top: -2px; margin-bottom: -2px; border-radius: 4px; } QSlider::handle:horizontal:hover { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #fff, stop:1 #ddd); border: 1px solid #444; border-radius: 3px; } QSlider::sub-page:horizontal:disabled { background: #bbb; border-color: #999; } QSlider::add-page:horizontal:disabled { background: #eee; border-color: #999; } QSlider::handle:horizontal:disabled { background: #eee; border: 1px solid #aaa; border-radius: 4px; } 55 Qt::Horizontal QSlider::NoTicks Qt::Horizontal 40 20 16777215 20 <html><head/><body><p>Indicates what aspect of a multi-component image layer is displayed (e.g., a particular component, magnitude of components, etc.).</p></body></html> font-size:11px; color: rgb(120, 120, 120) TextLabel 0 0 16777215 20 <html><head/><body><p>Show a context menu of commands for this image layer.</p></body></html> false ... :/root/context_gray_10.png:/root/context_gray_10.png 10 10 false false true :/root/save_22.png:/root/save_22.png Save... Save Image to File :/root/icons8_close_16.png:/root/icons8_close_16.png Close Close (unload) the selected image layer :/root/icons8_fantasy_16.png:/root/icons8_fantasy_16.png Auto Adjust Contrast Adjust contrast of the image layer automatically for optimal visualization Generate texture features Create texture features derived from this image :/root/icons8_pin_16.png :/root/icons8_pin_16.png:/root/icons8_pin_16.png Display as ovelay Display the layer as a semi-transparent overlay on top of other image layers :/root/icons8_unpin_16.png:/root/icons8_unpin_16.png Display as base layer Display the layer side by side with other layers Contrast Inspector ... Open the contrast inspector to adjust image contrast for this layer Color Map Inspector ... Open the color map editor for this layer itksnap-3.4.0/GUI/Qt/Components/MetadataInspector.cxx000066400000000000000000000035201263013355200224570ustar00rootroot00000000000000#include "MetadataInspector.h" #include "ui_MetadataInspector.h" #include #include #include "ImageInfoModel.h" void MetadataTableQtModel::SetParentModel(ImageInfoModel *model) { m_ParentModel = model; LatentITKEventNotifier::connect( m_ParentModel,ImageInfoModel::MetadataChangeEvent(), this, SLOT(onModelUpdate())); } int MetadataTableQtModel::rowCount(const QModelIndex &parent) const { return m_ParentModel->GetMetadataRows(); } int MetadataTableQtModel::columnCount(const QModelIndex &parent) const { return 2; } QVariant MetadataTableQtModel::headerData( int section, Qt::Orientation orientation, int role) const { const char *header[] = {"Key", "Value"}; if(role == Qt::DisplayRole) { return header[section]; } else return QVariant(); } QVariant MetadataTableQtModel::data( const QModelIndex &index, int role) const { // Ignore bad requests if(index.isValid() && (role == Qt::DisplayRole || role == Qt::ToolTipRole)) return m_ParentModel->GetMetadataCell(index.row(), index.column()).c_str(); else return QVariant(); } void MetadataTableQtModel::onModelUpdate() { m_ParentModel->Update(); emit layoutChanged(); } MetadataInspector::MetadataInspector(QWidget *parent) : SNAPComponent(parent), ui(new Ui::MetadataInspector) { ui->setupUi(this); m_TableModel = new MetadataTableQtModel(this); } MetadataInspector::~MetadataInspector() { delete ui; } void MetadataInspector::SetModel(ImageInfoModel *model) { m_Model = model; m_TableModel->SetParentModel(model); // Hook the table model to the table widget ui->tblMetaData->setModel(m_TableModel); // Connect the filter input to the model makeCoupling(ui->inFilter, m_Model->GetMetadataFilterModel()); } void MetadataInspector::onModelUpdate(const EventBucket &bucket) { } itksnap-3.4.0/GUI/Qt/Components/MetadataInspector.h000066400000000000000000000023231263013355200221040ustar00rootroot00000000000000#ifndef METADATAINSPECTOR_H #define METADATAINSPECTOR_H #include #include #include class ImageInfoModel; class MetadataTableQtModel; namespace Ui { class MetadataInspector; } class MetadataTableQtModel : public QAbstractTableModel { Q_OBJECT public: MetadataTableQtModel(QWidget *parent) : QAbstractTableModel(parent) {} virtual ~MetadataTableQtModel() {} void SetParentModel(ImageInfoModel *model); int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant data(const QModelIndex &index, int role) const; public slots: void onModelUpdate(); protected: ImageInfoModel *m_ParentModel; }; class MetadataInspector : public SNAPComponent { Q_OBJECT public: explicit MetadataInspector(QWidget *parent = 0); ~MetadataInspector(); // Set the model void SetModel(ImageInfoModel *model); // Respond to model updates virtual void onModelUpdate(const EventBucket &bucket); private: Ui::MetadataInspector *ui; MetadataTableQtModel *m_TableModel; ImageInfoModel *m_Model; }; #endif // METADATAINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/MetadataInspector.ui000066400000000000000000000050161263013355200222740ustar00rootroot00000000000000 MetadataInspector 0 0 410 444 Form 12 6 18 6 6 75 true Image Metadata: true false 180 true false 24 0 Qt::Horizontal 40 20 Filter: itksnap-3.4.0/GUI/Qt/Components/PaintbrushToolPanel.cxx000066400000000000000000000060631263013355200230120ustar00rootroot00000000000000#include "PaintbrushToolPanel.h" #include "ui_PaintbrushToolPanel.h" #include "PaintbrushSettingsModel.h" #include "QtRadioButtonCoupling.h" #include "QtCheckBoxCoupling.h" #include "QtDoubleSpinBoxCoupling.h" #include "QtSpinBoxCoupling.h" #include "QtSliderCoupling.h" PaintbrushToolPanel::PaintbrushToolPanel(QWidget *parent) : QWidget(parent), ui(new Ui::PaintbrushToolPanel) { ui->setupUi(this); // Adjust the shortcuts for increase/decrease behavior ui->actionBrushIncrease->setShortcuts( ui->actionBrushIncrease->shortcuts() << QKeySequence('=')); ui->actionBrushDecrease->setShortcuts( ui->actionBrushDecrease->shortcuts() << QKeySequence('_')); ui->actionGranularityIncrease->setShortcuts( ui->actionGranularityIncrease->shortcuts() << QKeySequence(Qt::META + Qt::Key_Equal) << QKeySequence(Qt::META + Qt::Key_Plus)); ui->actionGranularityDecrease->setShortcuts( ui->actionGranularityDecrease->shortcuts() << QKeySequence(Qt::META + Qt::Key_Underscore) << QKeySequence(Qt::META + Qt::Key_Minus)); ui->actionSmoothnessIncrease->setShortcuts( ui->actionSmoothnessIncrease->shortcuts() << QKeySequence(Qt::ALT + Qt::Key_Equal) << QKeySequence(Qt::ALT + Qt::Key_Plus)); ui->actionSmoothnessDecrease->setShortcuts( ui->actionSmoothnessDecrease->shortcuts() << QKeySequence(Qt::ALT + Qt::Key_Underscore) << QKeySequence(Qt::ALT + Qt::Key_Minus)); addAction(ui->actionBrushIncrease); addAction(ui->actionBrushDecrease); addAction(ui->actionGranularityDecrease); addAction(ui->actionGranularityIncrease); addAction(ui->actionSmoothnessDecrease); addAction(ui->actionSmoothnessIncrease); addAction(ui->actionBrushStyle); } PaintbrushToolPanel::~PaintbrushToolPanel() { delete ui; } void PaintbrushToolPanel::SetModel(PaintbrushSettingsModel *model) { m_Model = model; // Couple the radio buttons std::map rmap; rmap[PAINTBRUSH_RECTANGULAR] = ui->btnSquare; rmap[PAINTBRUSH_ROUND] = ui->btnRound; rmap[PAINTBRUSH_WATERSHED] = ui->btnWatershed; makeRadioGroupCoupling(ui->grpBrushStyle, rmap, m_Model->GetPaintbrushModeModel()); // Couple the other controls makeCoupling(ui->chkVolumetric, model->GetVolumetricBrushModel()); makeCoupling(ui->chkIsotropic, model->GetIsotropicBrushModel()); makeCoupling(ui->chkChase, model->GetChaseCursorModel()); makeCoupling(ui->inBrushSizeSlider, model->GetBrushSizeModel()); makeCoupling(ui->inBrushSizeSpinbox, model->GetBrushSizeModel()); // Couple the visibility of the adaptive widget makeWidgetVisibilityCoupling(ui->grpAdaptive, model->GetAdaptiveModeModel()); makeCoupling(ui->inGranularity, model->GetThresholdLevelModel()); makeCoupling(ui->inSmoothness, model->GetSmoothingIterationsModel()); } void PaintbrushToolPanel::on_actionBrushStyle_triggered() { if(ui->btnSquare->isChecked()) ui->btnRound->setChecked(true); else if(ui->btnRound->isChecked()) ui->btnWatershed->setChecked(true); else if(ui->btnWatershed->isChecked()) ui->btnSquare->setChecked(true); } itksnap-3.4.0/GUI/Qt/Components/PaintbrushToolPanel.h000066400000000000000000000010171263013355200224310ustar00rootroot00000000000000#ifndef PAINTBRUSHTOOLPANEL_H #define PAINTBRUSHTOOLPANEL_H #include class PaintbrushSettingsModel; namespace Ui { class PaintbrushToolPanel; } class PaintbrushToolPanel : public QWidget { Q_OBJECT public: explicit PaintbrushToolPanel(QWidget *parent = 0); ~PaintbrushToolPanel(); void SetModel(PaintbrushSettingsModel *model); private slots: void on_actionBrushStyle_triggered(); private: Ui::PaintbrushToolPanel *ui; PaintbrushSettingsModel *m_Model; }; #endif // PAINTBRUSHTOOLPANEL_H itksnap-3.4.0/GUI/Qt/Components/PaintbrushToolPanel.ui000066400000000000000000000436161263013355200226320ustar00rootroot00000000000000 PaintbrushToolPanel 0 0 179 442 Form * { font-size:12px; } QSpinBox { font-size:11px; } 4 0 0 0 0 Brush Style: 6 12 0 0 0 26 26 <html><head/><body><p><span style=" font-weight:600;">Square brush shape (⌘B to cycle)</span></p> square :/root/brush_shape_square.png:/root/brush_shape_square.png Ctrl+S true true 26 26 <html><head/><body><p><span style=" font-weight:600;">Round brush shape (⌘B to cycle)</span></p></body></html> round :/root/brush_shape_round.png:/root/brush_shape_round.png true true 26 26 <html><head/><body><p><span style=" font-weight:600;">Adaptive brush (⌘B to cycle)</span></p><p>The brush adjusts itself to follow image boundaries.</p></body></html> adaptive :/root/brush_shape_adaptive.png:/root/brush_shape_adaptive.png true true Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Fixed 4 4 Brush Size: 6 0 12 0 48 0 <html><head/><body><p>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Brush Size (+, - keys)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Adjust the radius of the paintbrush.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Brush Size (+, - keys)</span></p><p>Adjust the radius of the paintbrush.</p></body></html> Qt::Horizontal Qt::Vertical QSizePolicy::Fixed 4 4 Brush Options: QCheckBox { padding:0px; } 0 0 0 0 8 <html><head/><body><p><span style=" font-weight:600;">Toggle 3D brush mode (⇧⌘B, V)</span></p><p>When checked, the brush is applied in all three dimensions: a rectangular brush creates a cube in the segmentation, and a round brush creates a sphere.</p></body></html> 3D Ctrl+Shift+B, V <html><head/><body><p><span style=" font-weight:600;">Toggle isotropic brush mode (⇧⌘B, I)</span></p><p>This option is relevant for images with anisotropic voxels (voxels with different x,y and z dimensions). When checked, the physical dimensions of the brush are kept as close to equal as possible. When not checked, the brush size is K x K voxels, regardless of the dimensions of the voxel.</p></body></html> Isotropic Ctrl+Shift+B, I <html><head/><body><p><span style=" font-weight:600;">Toggle cursor chasing mode (⇧⌘B, C)</span></p><p>When checked, at the end of each paintbrush stroke, the position of the 3D image cursor is reset to the endpoint of the stroke.</p></body></html> Cursor chases brush Ctrl+Shift+B, C Qt::Vertical QSizePolicy::Fixed 4 4 4 0 0 0 0 Qt::Horizontal Adaptive Algorithm: QFormLayout::FieldsStayAtSizeHint 6 4 0 0 0 Granularity: inGranularity <html><head/><body><p><span style=" font-weight:600;">Adaptive brush granularity (^+,^-)</span></p><p>Lower values of this parameter lead to oversegmentation, while higher values lead to undersegmentation.</p></body></html> Smoothness: inSmoothness <html><head/><body><p><span style=" font-weight:600;">Adaptive brush smoothness (⌥+,⌥-)</span></p><p>Larger values of this parameter produce smoother segments.</p></body></html> Qt::Vertical 20 40 Increase Brush Size + Increase Brush Size - GranularityIncrease GranularityDecrease GranularityDecrease SmoothnessIncrease Alt+Tab SmoothnessDecrease BrushStyle Ctrl+B actionBrushIncrease triggered() inBrushSizeSpinbox stepUp() -1 -1 40 160 actionBrushDecrease triggered() inBrushSizeSpinbox stepDown() -1 -1 40 160 actionGranularityIncrease triggered() inGranularity stepUp() -1 -1 138 214 actionGranularityDecrease triggered() inGranularity stepDown() -1 -1 138 214 actionSmoothnessIncrease triggered() inSmoothness stepUp() -1 -1 138 241 actionSmoothnessDecrease triggered() inSmoothness stepDown() -1 -1 138 241 itksnap-3.4.0/GUI/Qt/Components/PolygonToolPanel.cxx000066400000000000000000000027441263013355200223240ustar00rootroot00000000000000#include "PolygonToolPanel.h" #include "ui_PolygonToolPanel.h" #include "GlobalUIModel.h" #include "QtCheckBoxCoupling.h" #include "QtSpinBoxCoupling.h" #include "QtSliderCoupling.h" #include "QtRadioButtonCoupling.h" #include "GlobalState.h" #include "PolygonSettingsModel.h" #include "QtWidgetActivator.h" PolygonToolPanel::PolygonToolPanel(QWidget *parent) : QWidget(parent), ui(new Ui::PolygonToolPanel) { ui->setupUi(this); } PolygonToolPanel::~PolygonToolPanel() { delete ui; } void PolygonToolPanel::SetModel(GlobalUIModel *model) { m_Model = model; // Couple the inversion checkbox makeCoupling(ui->chkInvertPolygon, m_Model->GetGlobalState()->GetPolygonInvertModel()); // Couple the freehand drawing mode makeCoupling(ui->inSegmentLength, m_Model->GetPolygonSettingsModel()->GetFreehandSegmentLengthModel()); makeCoupling(ui->inSegmentLengthSlider, m_Model->GetPolygonSettingsModel()->GetFreehandSegmentLengthModel()); // Couple the radio buttons std::map radioMap; radioMap[false] = ui->btnSmooth; radioMap[true] = ui->btnPiecewiseLinear; makeRadioGroupCoupling(ui->grpCurveStyle, radioMap, m_Model->GetPolygonSettingsModel()->GetFreehandIsPiecewiseModel()); // Toggle the appearance of the piecewise block makeWidgetVisibilityCoupling(ui->grpSegmentLength, m_Model->GetPolygonSettingsModel()->GetFreehandIsPiecewiseModel()); } itksnap-3.4.0/GUI/Qt/Components/PolygonToolPanel.h000066400000000000000000000006351263013355200217460ustar00rootroot00000000000000#ifndef POLYGONTOOLPANEL_H #define POLYGONTOOLPANEL_H #include namespace Ui { class PolygonToolPanel; } class GlobalUIModel; class PolygonToolPanel : public QWidget { Q_OBJECT public: explicit PolygonToolPanel(QWidget *parent = 0); ~PolygonToolPanel(); void SetModel(GlobalUIModel *model); private: Ui::PolygonToolPanel *ui; GlobalUIModel *m_Model; }; #endif // POLYGONTOOLPANEL_H itksnap-3.4.0/GUI/Qt/Components/PolygonToolPanel.ui000066400000000000000000000147531263013355200221420ustar00rootroot00000000000000 PolygonToolPanel 0 0 174 300 Form QWidget { font-size:12px; } 4 0 0 0 0 Freehand drawing style: QRadioButton { padding: 0px; } 4 20 0 0 0 Smooth curve true Polygon 0 40 0 0 0 Segment length: 6 0 QAbstractSpinBox::NoButtons <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Helvetica'; font-size:12px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Set the overall opacity of segmentation labels.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">[A]: Make more transparent</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">[D]: Make more opaque</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">[S]: Toggle on/off</p></body></html> 0 100 1 10 50 Qt::Horizontal QSlider::NoTicks 25 Qt::Horizontal <html><head/><body><p>When enabled, polygon drawing operations are inverted, i.e., the outside of the polygon is filled with the active label.</p></body></html> Invert polygon Qt::Vertical 20 40 itksnap-3.4.0/GUI/Qt/Components/QActionButton.cxx000066400000000000000000000020111263013355200215740ustar00rootroot00000000000000#include "QActionButton.h" #include "SNAPQtCommon.h" #include #include "QToolButton" QActionButton::QActionButton(QWidget *parent) : QPushButton(parent) { m_action = NULL; } void QActionButton::setAction(QString actionName) { // Remove old connections if(m_action && m_action->objectName() != actionName) { disconnect(m_action, SIGNAL(changed()), this, SLOT(updateFromAction())); disconnect(this, SIGNAL(clicked()), m_action, SLOT(trigger())); } // Assign the action m_action = FindUpstreamAction(this, actionName); if(m_action) { // Update ourselves updateFromAction(); // Create connections connect(m_action, SIGNAL(changed()), this, SLOT(updateFromAction())); connect(this, SIGNAL(clicked()), m_action, SLOT(trigger())); } } QString QActionButton::action() const { return m_action->objectName(); } void QActionButton::updateFromAction() { setToolTip(m_action->toolTip()); setEnabled(m_action->isEnabled()); setStatusTip(m_action->statusTip()); } itksnap-3.4.0/GUI/Qt/Components/QActionButton.h000066400000000000000000000007521263013355200212330ustar00rootroot00000000000000#ifndef QACTIONBUTTON_H #define QACTIONBUTTON_H #include class QAction; class QActionButton : public QPushButton { Q_OBJECT Q_PROPERTY(QString action READ action WRITE setAction NOTIFY actionChanged) public: explicit QActionButton(QWidget *parent = 0); void setAction(QString action); QString action() const; signals: void actionChanged(QString); public slots: void updateFromAction(); private: QAction *m_action; }; #endif // QACTIONBUTTON_H itksnap-3.4.0/GUI/Qt/Components/QColorButtonWidget.cxx000066400000000000000000000016261263013355200226140ustar00rootroot00000000000000#include "QColorButtonWidget.h" #include #include #include #include QColorButtonWidget::QColorButtonWidget(QWidget *parent) : QWidget(parent) { m_Button = new QToolButton(this); m_Button->setText("Choose ..."); m_Button->setIcon(CreateColorBoxIcon(16,16,m_value)); m_Button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); m_Button->setIconSize(QSize(16,16)); QHBoxLayout *lo = new QHBoxLayout(); lo->setContentsMargins(0,0,0,0); lo->addWidget(m_Button); this->setLayout(lo); connect(m_Button, SIGNAL(clicked()), SLOT(onButtonPress())); } void QColorButtonWidget::setValue(QColor value) { m_value = value; m_Button->setIcon(CreateColorBoxIcon(16,16,value)); emit valueChanged(); } void QColorButtonWidget::onButtonPress() { QColor color = QColorDialog::getColor(m_value, this); if(color.isValid()) setValue(color); } itksnap-3.4.0/GUI/Qt/Components/QColorButtonWidget.h000066400000000000000000000010101263013355200222240ustar00rootroot00000000000000#ifndef QCOLORBUTTONWIDGET_H #define QCOLORBUTTONWIDGET_H #include class QToolButton; class QColorButtonWidget : public QWidget { Q_OBJECT public: explicit QColorButtonWidget(QWidget *parent = 0); Q_PROPERTY(QColor value READ value WRITE setValue NOTIFY valueChanged) void setValue(QColor value); QColor value() { return m_value; } signals: void valueChanged(); public slots: void onButtonPress(); private: QToolButton *m_Button; QColor m_value; }; #endif // QCOLORBUTTONWIDGET_H itksnap-3.4.0/GUI/Qt/Components/QDoubleSlider.cxx000066400000000000000000000017701263013355200215530ustar00rootroot00000000000000#include "QDoubleSlider.h" #include QDoubleSlider::QDoubleSlider(QWidget *parent) : QSlider(parent) { m_DoubleMin = 0.0; m_DoubleMax = 1.0; m_DoubleStep = 0.01; updateRange(); } void QDoubleSlider::updateRange() { int mymax = ceil((m_DoubleMax - m_DoubleMin) / m_DoubleStep); this->setMinimum(0); this->setMaximum(mymax); this->setSingleStep(1); this->setDoubleValue(m_DoubleValue); } void QDoubleSlider::setDoubleValue(double x) { m_DoubleValue = x; double t = (m_DoubleValue - m_DoubleMin) / (m_DoubleMax - m_DoubleMin); t = std::max(0.0, std::min(1.0, t)); int p = (int)(0.5 + this->maximum() * t); if(this->value() != p) this->setValue(p); m_CorrespondingIntValue = p; } double QDoubleSlider::doubleValue() { if(this->value() != m_CorrespondingIntValue) { double t = this->value() * 1.0 / this->maximum(); m_DoubleValue = m_DoubleMin + t * (m_DoubleMax - m_DoubleMin); m_CorrespondingIntValue = this->value(); } return m_DoubleValue; } itksnap-3.4.0/GUI/Qt/Components/QDoubleSlider.h000066400000000000000000000017661263013355200212050ustar00rootroot00000000000000#ifndef QDOUBLESLIDER_H #define QDOUBLESLIDER_H #include class QDoubleSlider : public QSlider { Q_OBJECT public: explicit QDoubleSlider(QWidget *parent = 0); double doubleMinimum() { return m_DoubleMin; } double doubleMaximum() { return m_DoubleMax; } double doubleSingleStep() { return m_DoubleStep; } void setDoubleMinimum(double value) { m_DoubleMin = value; updateRange(); } void setDoubleMaximum(double value) { m_DoubleMax = value; updateRange(); } void setDoubleSingleStep(double value) { m_DoubleStep = value; updateRange(); } double doubleValue(); void setDoubleValue(double x); signals: public slots: private: double m_DoubleMin; double m_DoubleMax; double m_DoubleStep; double m_DoubleValue; int m_CorrespondingIntValue; void updateRange(); void updateStep() { QSlider::setSingleStep((int)(1000 * m_DoubleStep / (m_DoubleMax - m_DoubleMin))); } }; #endif // QDOUBLESLIDER_H itksnap-3.4.0/GUI/Qt/Components/QDoubleSliderWithEditor.cxx000066400000000000000000000073721263013355200235620ustar00rootroot00000000000000#include "QDoubleSliderWithEditor.h" #include "ui_QDoubleSliderWithEditor.h" #include QDoubleSliderWithEditor::QDoubleSliderWithEditor(QWidget *parent) : QWidget(parent), ui(new Ui::QDoubleSliderWithEditor) { ui->setupUi(this); ui->slider->setMinimum(0); ui->slider->setMaximum(1000000); ui->slider->setSingleStep(0); // When the value in the slider changes, we want to update the spinbox connect(ui->slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); connect(ui->spinbox, SIGNAL(valueChanged(double)), this, SLOT(spinnerValueChanged(double))); m_IgnoreSliderEvent = false; m_IgnoreSpinnerEvent = false; m_ForceDiscreteSteps = true; } QDoubleSliderWithEditor::~QDoubleSliderWithEditor() { delete ui; } void QDoubleSliderWithEditor::setValue(double newval) { // Set the value in the spinbox if(newval != ui->spinbox->value()) { m_IgnoreSpinnerEvent = true; ui->spinbox->setValue(newval); ui->spinbox->setSpecialValueText(""); m_IgnoreSpinnerEvent = false; this->updateSliderFromSpinner(); } } double QDoubleSliderWithEditor::value() { return ui->spinbox->value(); } void QDoubleSliderWithEditor::updateSliderFromSpinner() { // Set the value of the slider proportionally to the range of the spinner double a = ui->spinbox->minimum(); double b = ui->spinbox->maximum(); double v = ui->spinbox->value(); double r = (v - a) / (b - a); int vs = (int) (ui->slider->maximum() * r); m_IgnoreSliderEvent = true; if(vs != ui->slider->value()) ui->slider->setValue(vs); m_IgnoreSliderEvent = false; } double QDoubleSliderWithEditor::minimum() { return ui->spinbox->minimum(); } double QDoubleSliderWithEditor::maximum() { return ui->spinbox->maximum(); } double QDoubleSliderWithEditor::singleStep() { return ui->spinbox->singleStep(); } void QDoubleSliderWithEditor::setMinimum(double x) { ui->spinbox->setMinimum(x); this->updateSliderFromSpinner(); } void QDoubleSliderWithEditor::setMaximum(double x) { ui->spinbox->setMaximum(x); this->updateSliderFromSpinner(); } void QDoubleSliderWithEditor::setSingleStep(double x) { ui->spinbox->setSingleStep(x); } void QDoubleSliderWithEditor::setForceDiscreteSteps(bool useDiscreteSteps) { m_ForceDiscreteSteps = useDiscreteSteps; } void QDoubleSliderWithEditor::sliderValueChanged(int valslider) { if(!m_IgnoreSliderEvent) { // We need to map the integer value into the closest acceptable by the spinbox double r = valslider * 1.0 / ui->slider->maximum(); double a = ui->spinbox->minimum(); double b = ui->spinbox->maximum(); double v; // If necessary, round the value using singleStep if(m_ForceDiscreteSteps) { double step = ui->spinbox->singleStep(); double t = 0.5 * step + (b - a) * r; v = a + (t - std::fmod(t, step)); } else { v = a + (b - a) * r; } // Set the value in the spinner and slider this->setValue(v); // Invoke the event emit valueChanged(ui->spinbox->value()); } } void QDoubleSliderWithEditor::spinnerValueChanged(double value) { // This is very simple, we just stick the value into the slider if(!m_IgnoreSpinnerEvent) { this->updateSliderFromSpinner(); emit valueChanged(value); } } void QDoubleSliderWithEditor::stepUp() { ui->spinbox->stepUp(); } void QDoubleSliderWithEditor::stepDown() { ui->spinbox->stepDown(); } void QDoubleSliderWithEditor::setValueToNull() { // First, set the value to minimum m_IgnoreSliderEvent = true; m_IgnoreSpinnerEvent = true; ui->slider->setValue(ui->slider->minimum()); ui->spinbox->setValue(ui->spinbox->minimum()); ui->spinbox->setSpecialValueText(" "); m_IgnoreSliderEvent = false; m_IgnoreSpinnerEvent = false; } itksnap-3.4.0/GUI/Qt/Components/QDoubleSliderWithEditor.h000066400000000000000000000025541263013355200232040ustar00rootroot00000000000000#ifndef QDOUBLESLIDERWITHEDITOR_H #define QDOUBLESLIDERWITHEDITOR_H #include namespace Ui { class QDoubleSliderWithEditor; } class QDoubleSliderWithEditor : public QWidget { Q_OBJECT public: Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged) Q_PROPERTY(double minimum READ minimum WRITE setMinimum) Q_PROPERTY(double maximum READ maximum WRITE setMaximum) Q_PROPERTY(double singleStep READ singleStep WRITE setSingleStep) explicit QDoubleSliderWithEditor(QWidget *parent = 0); ~QDoubleSliderWithEditor(); double value(); void setValue(double newval); void setValueToNull(); double minimum(); double maximum(); double singleStep(); void setMinimum(double); void setMaximum(double); void setSingleStep(double); void setOrientation(Qt::Orientation) {} /** Whether the slider uses discrete steps (in units of SingleStep) or * a continuous range of values */ void setForceDiscreteSteps(bool useDiscreteSteps); public slots: void sliderValueChanged(int); void spinnerValueChanged(double); void stepUp(); void stepDown(); signals: void valueChanged(double); private: void updateSliderFromSpinner(); Ui::QDoubleSliderWithEditor *ui; bool m_IgnoreSliderEvent, m_IgnoreSpinnerEvent; bool m_ForceDiscreteSteps; }; #endif // QDOUBLESLIDERWITHEDITOR_H itksnap-3.4.0/GUI/Qt/Components/QDoubleSliderWithEditor.ui000066400000000000000000000020511263013355200233620ustar00rootroot00000000000000 QDoubleSliderWithEditor 0 0 210 32 Form 0 QAbstractSpinBox::NoButtons Qt::Horizontal false QSlider::NoTicks itksnap-3.4.0/GUI/Qt/Components/QtCursorOverride.h000066400000000000000000000013321263013355200217560ustar00rootroot00000000000000#ifndef QCURSOROVERRIDE_H #define QCURSOROVERRIDE_H #include #include /** A small object that can be allocated on the stack to change the appearance of the Qt cursor. It is conventient to use inside of try/catch blocks try { CusrorOverride co; // do stuff } catch(...) { // cursor restored at this point } */ class QtCursorOverride { public: QtCursorOverride(const QCursor &cursor) { QApplication::setOverrideCursor(cursor); } QtCursorOverride(Qt::CursorShape shape = Qt::WaitCursor) { QApplication::setOverrideCursor(QCursor(shape)); } ~QtCursorOverride() { QApplication::restoreOverrideCursor(); } }; #endif // QCURSOROVERRIDE_H itksnap-3.4.0/GUI/Qt/Components/QtHideOnDeactivateContainer.cxx000066400000000000000000000006501263013355200243610ustar00rootroot00000000000000#include "QtHideOnDeactivateContainer.h" #include QtHideOnDeactivateContainer::QtHideOnDeactivateContainer(QWidget *parent) : QWidget(parent) { } QtHideOnDeactivateContainer::~QtHideOnDeactivateContainer() { } void QtHideOnDeactivateContainer::changeEvent(QEvent *event) { QWidget::changeEvent(event); if(event->type() == QEvent::EnabledChange) { this->setVisible(this->isEnabled()); } } itksnap-3.4.0/GUI/Qt/Components/QtHideOnDeactivateContainer.h000066400000000000000000000005231263013355200240050ustar00rootroot00000000000000#ifndef QTHIDEONDEACTIVATECONTAINER_H #define QTHIDEONDEACTIVATECONTAINER_H #include class QtHideOnDeactivateContainer : public QWidget { Q_OBJECT public: QtHideOnDeactivateContainer(QWidget *parent); ~QtHideOnDeactivateContainer(); virtual void changeEvent(QEvent *event); }; #endif // QTHIDEONDEACTIVATECONTAINER_H itksnap-3.4.0/GUI/Qt/Components/QtIPCManager.cxx000066400000000000000000000012111263013355200212560ustar00rootroot00000000000000#include "QtIPCManager.h" #include "SNAPEvents.h" #include "SynchronizationModel.h" QtIPCManager::QtIPCManager(QWidget *parent) : SNAPComponent(parent) { // Start the IPC timer at 30ms intervals startTimer(30); } void QtIPCManager::SetModel(SynchronizationModel *model) { m_Model = model; // Listen to update events from the model connectITK(m_Model, ModelUpdateEvent()); } void QtIPCManager::onModelUpdate(const EventBucket &bucket) { // Update the model - this is where all the broadcasting takes place m_Model->Update(); } void QtIPCManager::timerEvent(QTimerEvent *) { if(!m_Model) return; m_Model->ReadIPCState(); } itksnap-3.4.0/GUI/Qt/Components/QtIPCManager.h000066400000000000000000000013441263013355200207120ustar00rootroot00000000000000#ifndef QTIPCMANAGER_H #define QTIPCMANAGER_H #include #include class SynchronizationModel; /** * @brief This class manages IPC communications between SNAP sessions on the * GUI level. It uses Qt's timers to schedule checks for IPC updates, and it * listens to the events from the model layer in order to send IPC messages * out. */ class QtIPCManager : public SNAPComponent { Q_OBJECT public: explicit QtIPCManager(QWidget *parent = 0); void SetModel(SynchronizationModel *model); signals: public slots: virtual void onModelUpdate(const EventBucket &bucket); protected: virtual void timerEvent(QTimerEvent *); private: SynchronizationModel *m_Model; }; #endif // QTIPCMANAGER_H itksnap-3.4.0/GUI/Qt/Components/QtRendererPlatformSupport.cxx000066400000000000000000000115561263013355200242350ustar00rootroot00000000000000#include "QtRendererPlatformSupport.h" #include #include #include #include #include "SNAPQtCommon.h" #include #include QRect QtRendererPlatformSupport::WorldRectangleToPixelRectangle(double wx, double wy, double ww, double wh) { // Viewport and such vnl_vector viewport(4); vnl_matrix model_view(4,4), projection(4,4); // Map the rectangle to viewport coordinates glGetIntegerv(GL_VIEWPORT, viewport.data_block()); glGetDoublev(GL_PROJECTION_MATRIX, projection.data_block()); glGetDoublev(GL_MODELVIEW_MATRIX, model_view.data_block()); vnl_vector xw1(4), xs1, xw2(4), xs2; xw1[0] = wx; xw1[1] = wy; xw1[2] = 0.0; xw1[3] = 1.0; xs1 = projection.transpose() * (model_view.transpose() * xw1); xw2[0] = wx + ww; xw2[1] = wy + wh; xw2[2] = 0.0; xw2[3] = 1.0; xs2 = projection.transpose() * (model_view.transpose() * xw2); double x = std::min(xs1[0] / xs1[3], xs2[0] / xs2[3]); double y = std::min(xs1[1] / xs1[3], xs2[1] / xs2[3]); double w = fabs(xs1[0] / xs1[3] - xs2[0] / xs2[3]); double h = fabs(xs1[1] / xs1[3] - xs2[1] / xs2[3]); x = (x + 1) / 2; y = (y + 1) / 2; w = w / 2; h = h / 2; return QRect( QPoint((int)(x * viewport[2] + viewport[0]), (int)(y * viewport[3] + viewport[1])), QSize((int)(w * viewport[2]),(int)(h * viewport[3]))); } void QtRendererPlatformSupport ::RenderTextInOpenGL(const char *text, double x, double y, double w, double h, FontInfo font, int align_horiz, int align_vert, const Vector3d &rgbf, double alpha) { // Map the coordinates to screen coordinates QRect rScreen = this->WorldRectangleToPixelRectangle(x, y, w, h); // Create a pixmap to render the text QImage canvas(rScreen.width(), rScreen.height(), QImage::Format_ARGB32); canvas.fill(QColor(0,0,0,0)); // Paint the text onto the canvas QPainter painter(&canvas); QColor pen_color; pen_color.setRgbF(rgbf[0], rgbf[1], rgbf[2], 1.0); pen_color.setAlphaF(alpha); painter.setPen(pen_color); int ah = Qt::AlignHCenter, av = Qt::AlignVCenter; switch(align_horiz) { case AbstractRendererPlatformSupport::LEFT : ah = Qt::AlignLeft; break; case AbstractRendererPlatformSupport::HCENTER : ah = Qt::AlignHCenter; break; case AbstractRendererPlatformSupport::RIGHT : ah = Qt::AlignRight; break; } switch(align_vert) { case AbstractRendererPlatformSupport::BOTTOM : av = Qt::AlignBottom; break; case AbstractRendererPlatformSupport::VCENTER : av = Qt::AlignVCenter; break; case AbstractRendererPlatformSupport::TOP : av = Qt::AlignTop; break; } QFont qfont; switch(font.type) { case AbstractRendererPlatformSupport::SERIF: qfont.setFamily("Times"); break; case AbstractRendererPlatformSupport::SANS: qfont.setFamily("Helvetica"); break; case AbstractRendererPlatformSupport::TYPEWRITER: qfont.setFamily("Courier"); break; } qfont.setPixelSize(font.pixel_size); qfont.setBold(font.bold); painter.setFont(qfont); // painter.fillRect(QRectF(0,0,rScreen.width(),rScreen.height()), QColor(64,64,64,64)); painter.drawText(QRectF(0,0,rScreen.width(),rScreen.height()), ah | av, QString::fromUtf8(text)); QImage gl = QGLWidget::convertToGLFormat(canvas); glPushAttrib(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); if(alpha <= 0.9) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); else glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glRasterPos2i(x,y); glDrawPixels(rScreen.width(),rScreen.height(), GL_RGBA, GL_UNSIGNED_BYTE, gl.bits()); glPopAttrib(); } int QtRendererPlatformSupport ::MeasureTextWidth(const char *text, AbstractRendererPlatformSupport::FontInfo font) { QFont qfont; switch(font.type) { case AbstractRendererPlatformSupport::SERIF: qfont.setFamily("Times"); break; case AbstractRendererPlatformSupport::SANS: qfont.setFamily("Helvetica"); break; case AbstractRendererPlatformSupport::TYPEWRITER: qfont.setFamily("Courier"); break; } qfont.setPixelSize(font.pixel_size); qfont.setBold(font.bold); QFontMetrics fm(qfont); return fm.width(QString::fromUtf8(text)); } #if QT_VERSION >= 0x050000 #include void QtRendererPlatformSupport::LoadTexture(const char *url, GLuint &texture_id, Vector2ui &tex_size) { // Get the icon corresponding to the URL QImage myimage(QString(":/root/%1.png").arg(url)); // Create an opengl texture object QOpenGLTexture *texture = new QOpenGLTexture(myimage); texture_id = texture->textureId(); // Set the texture size tex_size[0] = texture->width(); tex_size[1] = texture->height(); } #else void QtRendererPlatformSupport::LoadTexture(const char *url, GLuint &texture_id, Vector2ui &tex_size) { // Seems like this is not even used anywhere } #endif itksnap-3.4.0/GUI/Qt/Components/QtRendererPlatformSupport.h000066400000000000000000000013051263013355200236510ustar00rootroot00000000000000#ifndef QTRENDERERPLATFORMSUPPORT_H #define QTRENDERERPLATFORMSUPPORT_H #include "AbstractRenderer.h" #include class QtRendererPlatformSupport : public AbstractRendererPlatformSupport { public: virtual void RenderTextInOpenGL( const char *text, double x, double y, double w, double h, FontInfo font, int align_horiz, int align_vert, const Vector3d &rgbf, double alpha = 1.0); virtual int MeasureTextWidth(const char *text, FontInfo font); virtual void LoadTexture(const char *url, GLuint &texture_id, Vector2ui &tex_size); protected: QRect WorldRectangleToPixelRectangle(double wx, double wy, double ww, double wh); }; #endif // QTRENDERERPLATFORMSUPPORT_H itksnap-3.4.0/GUI/Qt/Components/QtReporterDelegates.cxx000066400000000000000000000103301263013355200227720ustar00rootroot00000000000000#include "QtReporterDelegates.h" #include "UIReporterDelegates.h" #include #include #include #include #include #include #include "itkImage.h" #include "itkImageRegionIteratorWithIndex.h" #include "Registry.h" #include #include "SNAPQtCommon.h" QtViewportReporter::QtViewportReporter() { m_ClientWidget = NULL; m_Filter = new EventFilter(); m_Filter->m_Owner = this; } QtViewportReporter::~QtViewportReporter() { if(m_ClientWidget) m_ClientWidget->removeEventFilter(m_Filter); delete m_Filter; } void QtViewportReporter::SetClientWidget(QWidget *widget) { // In case we are changing widgets, make sure the filter is cleared if(m_ClientWidget) m_ClientWidget->removeEventFilter(m_Filter); // Store the widget m_ClientWidget = widget; // Capture events from the widget m_ClientWidget->installEventFilter(m_Filter); } bool QtViewportReporter::CanReportSize() { return m_ClientWidget != NULL; } #if QT_VERSION >= 0x050000 Vector2ui QtViewportReporter::GetViewportSize() { // For retina displays, this method reports size in actual pixels, not abstract pixels return Vector2ui(m_ClientWidget->width() * m_ClientWidget->devicePixelRatio(), m_ClientWidget->height() * m_ClientWidget->devicePixelRatio()); } float QtViewportReporter::GetViewportPixelRatio() { return m_ClientWidget->devicePixelRatio(); } #include std::string QtSystemInfoDelegate::GetApplicationPermanentDataLocation() { return to_utf8(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); } #else Vector2ui QtViewportReporter::GetViewportSize() { // For retina displays, this method reports size in actual pixels, not abstract pixels return Vector2ui(m_ClientWidget->width(), m_ClientWidget->height()); } float QtViewportReporter::GetViewportPixelRatio() { return 1.0f; } #include std::string QtSystemInfoDelegate::GetApplicationPermanentDataLocation() { return to_utf8(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); } #endif Vector2ui QtViewportReporter::GetLogicalViewportSize() { return Vector2ui(m_ClientWidget->width(), m_ClientWidget->height()); } bool QtViewportReporter::EventFilter ::eventFilter(QObject *object, QEvent *event) { if(object == m_Owner->m_ClientWidget && event->type() == QEvent::Resize) { m_Owner->InvokeEvent(ViewportResizeEvent()); } return QObject::eventFilter(object, event); } QtProgressReporterDelegate::QtProgressReporterDelegate() { m_Dialog = NULL; } void QtProgressReporterDelegate::SetProgressDialog(QProgressDialog *dialog) { m_Dialog = dialog; m_Dialog->setMinimum(0); m_Dialog->setMaximum(1000); m_Dialog->setWindowModality(Qt::WindowModal); m_Dialog->setLabelText("ITK-SNAP progress"); } #include #include void QtProgressReporterDelegate::SetProgressValue(double value) { m_Dialog->setValue((int) (1000 * value)); // qDebug() << "Progress: " << value; // QCoreApplication::processEvents(); } std::string QtSystemInfoDelegate::GetApplicationDirectory() { return to_utf8(QCoreApplication::applicationDirPath()); } std::string QtSystemInfoDelegate::GetApplicationFile() { return to_utf8(QCoreApplication::applicationFilePath()); } void QtSystemInfoDelegate ::LoadResourceAsImage2D(std::string tag, GrayscaleImage *image) { // Load the image using Qt QImage iq(QString(":/snapres/snapres/%1").arg(from_utf8(tag))); // Initialize the itk image itk::ImageRegion<2> region; region.SetSize(0, iq.width()); region.SetSize(1, iq.height()); image->SetRegions(region); image->Allocate(); // Fill the image buffer for(itk::ImageRegionIteratorWithIndex it(image, region); !it.IsAtEnd(); ++it) { it.Set(qGray(iq.pixel(it.GetIndex()[0],it.GetIndex()[1]))); } } void QtSystemInfoDelegate::LoadResourceAsRegistry(std::string tag, Registry ®) { // Read the file into a byte array QFile file(QString(":/snapres/snapres/%1").arg(from_utf8(tag))); if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { QByteArray ba = file.readAll(); // Read the registry contents std::stringstream ss(ba.data()); reg.ReadFromStream(ss); } } itksnap-3.4.0/GUI/Qt/Components/QtReporterDelegates.h000066400000000000000000000036151263013355200224270ustar00rootroot00000000000000#ifndef QTREQUESTDELEGATES_H #define QTREQUESTDELEGATES_H #include #include "UIReporterDelegates.h" class QProgressDialog; class QGLWidget; /** An implementation of a viewport reporter for Qt. CAVEAT: the reported must be destroyed before the client widget */ class QtViewportReporter : public ViewportSizeReporter { public: irisITKObjectMacro(QtViewportReporter, ViewportSizeReporter) /** Set the widget that we report on */ void SetClientWidget(QWidget *widget); bool CanReportSize(); Vector2ui GetViewportSize(); float GetViewportPixelRatio(); Vector2ui GetLogicalViewportSize(); protected: QtViewportReporter(); virtual ~QtViewportReporter(); QWidget *m_ClientWidget; /** This helper class intercepts resize events from the widget */ class EventFilter : public QObject { public: bool eventFilter(QObject *object, QEvent *event); QtViewportReporter *m_Owner; }; EventFilter *m_Filter; }; class QtProgressReporterDelegate : public ProgressReporterDelegate { public: QtProgressReporterDelegate(); void SetProgressDialog(QProgressDialog *dialog); void SetProgressValue(double); private: QProgressDialog *m_Dialog; }; class QtTextRenderingDelegate : public TextRenderingDelegate { public: virtual void RenderTextInOpenGL( const char *text, int x, int y, int w, int h, int font_size, int align_horiz, int align_vert, unsigned char rgba[]); protected: }; class QtSystemInfoDelegate : public SystemInfoDelegate { public: virtual std::string GetApplicationDirectory(); virtual std::string GetApplicationFile(); virtual std::string GetApplicationPermanentDataLocation(); typedef itk::Image GrayscaleImage; virtual void LoadResourceAsImage2D(std::string tag, GrayscaleImage *image); virtual void LoadResourceAsRegistry(std::string tag, Registry ®); }; #endif // QTREQUESTDELEGATES_H itksnap-3.4.0/GUI/Qt/Components/QtWarningDialog.cxx000066400000000000000000000021741263013355200221060ustar00rootroot00000000000000#include "QtWarningDialog.h" #include "ui_QtWarningDialog.h" #include #include #include #include #include #include void QtWarningDialog ::show(const std::vector &wl) { const QString htmlTemplate = "" "%2"; if(wl.size()) { QString html; for(std::vector::const_iterator it = wl.begin(); it != wl.end(); it++) { QString text = it->what(); QString head = text.section(".",0,0); QString tail = text.section(".", 1); html += QString(htmlTemplate).arg( "dlg_warning_32", QString("%1. \n%2").arg(head, tail)); } html = QString("%1
").arg(html); QtWarningDialog msg; msg.ui->label->setText(html); msg.exec(); } } QtWarningDialog::QtWarningDialog(QWidget *parent) : QDialog(parent), ui(new Ui::QtWarningDialog) { ui->setupUi(this); } QtWarningDialog::~QtWarningDialog() { delete ui; } void QtWarningDialog::on_pushButton_clicked() { this->close(); } itksnap-3.4.0/GUI/Qt/Components/QtWarningDialog.h000066400000000000000000000011371263013355200215310ustar00rootroot00000000000000#ifndef QTWARNINGDIALOG_H #define QTWARNINGDIALOG_H namespace Ui { class QtWarningDialog; } #include "IRISException.h" #include #include class QLabel; class QCheckBox; class QPushButton; class QtWarningDialog : public QDialog { Q_OBJECT public: explicit QtWarningDialog(QWidget *parent = 0); ~ QtWarningDialog(); static void show(const std::vector &warnings); public slots: private slots: void on_pushButton_clicked(); private: QLabel *m_Label; QPushButton *m_Button; QCheckBox *m_Checkbox; Ui::QtWarningDialog *ui; }; #endif // QTWARNINGDIALOG_H itksnap-3.4.0/GUI/Qt/Components/QtWarningDialog.ui000066400000000000000000000051001263013355200217110ustar00rootroot00000000000000 QtWarningDialog 0 0 384 179 0 0 ITK-SNAP - Warnings Generated 15 1 1 360 80 400 16777215 Warning: blah blah blah blah! true false Do not show this warning again Qt::Horizontal 40 20 Close Qt::Horizontal 40 20 itksnap-3.4.0/GUI/Qt/Components/QtWidgetActivator.cxx000066400000000000000000000050541263013355200224610ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #include "QtWidgetActivator.h" #include "LatentITKEventNotifier.h" #include #include #include #include "GlobalUIModel.h" #include "SNAPUIFlag.h" QtWidgetActivator ::QtWidgetActivator(QObject *parent, BooleanCondition *cond, Options options) : QObject(parent) { // Register to listen to the state change events m_TargetWidget = dynamic_cast(parent); m_TargetAction = dynamic_cast(parent); m_Condition = cond; m_Options = options; // Give it a name setObjectName(QString("Activator:%1").arg(parent->objectName())); // React to events after control returns to the main UI loop LatentITKEventNotifier::connect( cond, StateMachineChangeEvent(), this, SLOT(OnStateChange(const EventBucket &))); // Update the state of the widget EventBucket dummy; this->OnStateChange(dummy); } QtWidgetActivator::~QtWidgetActivator() { } void QtWidgetActivator::OnStateChange(const EventBucket &) { // Update the state of the widget based on the condition bool active = (*m_Condition)(); if(m_TargetWidget) { bool status = m_TargetWidget->isEnabledTo(m_TargetWidget->parentWidget()); if(status != active) { m_TargetWidget->setEnabled(active); if(m_Options & HideInactive) m_TargetWidget->setVisible(active); } } else if(m_TargetAction) { bool status = m_TargetAction->isEnabled(); if(status != active) { m_TargetAction->setEnabled(active); if(m_Options & HideInactive) m_TargetAction->setVisible(active); } } } itksnap-3.4.0/GUI/Qt/Components/QtWidgetActivator.h000066400000000000000000000072671263013355200221160ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTWIDGETACTIVATOR_H #define QTWIDGETACTIVATOR_H #include #include #include class BooleanCondition; class QAction; class EventBucket; class QtWidgetActivator : public QObject { Q_OBJECT public: enum Option { NoOptions = 0x0, HideInactive = 0x1 }; Q_DECLARE_FLAGS(Options, Option) /** Creates an activator with widget parent and boolean condition. The condition will be deleted when this object is destroyed. The parent widget enabled property will mirror the BoolenaCondition The widget can be a QWidget or a QAction. Otherwise, this will do nothing */ explicit QtWidgetActivator(QObject *parent, BooleanCondition *cond, Options options = NoOptions); ~QtWidgetActivator(); public slots: void OnStateChange(const EventBucket &); private: QWidget *m_TargetWidget; QAction *m_TargetAction; SmartPtr m_Condition; Options m_Options; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QtWidgetActivator::Options) template void activateOnFlag(QObject *w, TModel *m, TStateEnum flag, QtWidgetActivator::Options options = QtWidgetActivator::NoOptions) { typedef SNAPUIFlag FlagType; SmartPtr f = FlagType::New(m, flag); new QtWidgetActivator(w, f, options); } template void activateOnNotFlag(QObject *w, TModel *m, TStateEnum flag, QtWidgetActivator::Options options = QtWidgetActivator::NoOptions) { typedef SNAPUIFlag FlagType; SmartPtr f = FlagType::New(m, flag); SmartPtr nf = NotCondition::New(f); new QtWidgetActivator(w, nf, options); } template void activateOnAllFlags(QObject *w, TModel *m, TStateEnum flag1, TStateEnum flag2, QtWidgetActivator::Options options = QtWidgetActivator::NoOptions) { typedef SNAPUIFlag FlagType; SmartPtr f1 = FlagType::New(m, flag1); SmartPtr f2 = FlagType::New(m, flag2); SmartPtr f = AndCondition::New(f1, f2); new QtWidgetActivator(w, f, options); } template void activateOnAnyFlags(QObject *w, TModel *m, TStateEnum flag1, TStateEnum flag2, QtWidgetActivator::Options options = QtWidgetActivator::NoOptions) { typedef SNAPUIFlag FlagType; SmartPtr f1 = FlagType::New(m, flag1); SmartPtr f2 = FlagType::New(m, flag2); SmartPtr f = OrCondition::New(f1, f2); new QtWidgetActivator(w, f, options); } #endif // QTWIDGETACTIVATOR_H itksnap-3.4.0/GUI/Qt/Components/RecentHistoryItemsView.cxx000066400000000000000000000053751263013355200235210ustar00rootroot00000000000000#include "RecentHistoryItemsView.h" #include "ui_RecentHistoryItemsView.h" #include "HistoryManager.h" #include #include #include #include #include #include #include #include #include #include class HistoryListItemDelegate : public QItemDelegate { public: HistoryListItemDelegate(QWidget *parent) : QItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if(!(option.state & QStyle::State_MouseOver)) { painter->setOpacity(0.8); } else { painter->setBackground(QBrush(Qt::black)); } QItemDelegate::paint(painter, option, index); } }; RecentHistoryItemsView::RecentHistoryItemsView(QWidget *parent) : QWidget(parent), ui(new Ui::RecentHistoryItemsView) { ui->setupUi(this); // Delegate for history HistoryListItemDelegate *del = new HistoryListItemDelegate(ui->listRecent); ui->listRecent->setItemDelegate(del); ui->listRecent->setUniformItemSizes(true); // Set up the popup for the recent list m_RecentListPopup = new QMenu(this); m_RecentListPopupAction = m_RecentListPopup->addAction( "Remove from recent image list", this, SLOT(OnRemoveRecentImageListItem())); } RecentHistoryItemsView::~RecentHistoryItemsView() { delete ui; } void RecentHistoryItemsView::Initialize(GlobalUIModel *model, std::string history) { m_Model = model; m_History = history; // Create a model for the table of recent images and connect to the widget m_HistoryModel = new HistoryQListModel(ui->listRecent); m_HistoryModel->Initialize(model, history); ui->listRecent->setModel(m_HistoryModel); } void RecentHistoryItemsView::on_listRecent_clicked(const QModelIndex &index) { // Load the appropriate image if(index.isValid()) { QString filename = ui->listRecent->model()->data(index, Qt::UserRole).toString(); emit RecentItemSelected(filename); } } void RecentHistoryItemsView::on_listRecent_customContextMenuRequested(const QPoint &pos) { QModelIndex idx = ui->listRecent->indexAt(pos); if(idx.isValid()) { QVariant data = ui->listRecent->model()->data(idx, Qt::UserRole); if(!data.isNull()) { m_RecentListPopupAction->setData(data); m_RecentListPopup->popup(QCursor::pos()); } } } void RecentHistoryItemsView::OnRemoveRecentImageListItem() { QAction *action = qobject_cast(this->sender()); QString filename = action->data().toString(); HistoryManager *hm = m_Model->GetDriver()->GetSystemInterface()->GetHistoryManager(); hm->DeleteHistoryItem(m_History, to_utf8(filename)); } itksnap-3.4.0/GUI/Qt/Components/RecentHistoryItemsView.h000066400000000000000000000017371263013355200231440ustar00rootroot00000000000000#ifndef RECENTHISTORYITEMSVIEW_H #define RECENTHISTORYITEMSVIEW_H #include class GlobalUIModel; class QMenu; class QAction; class HistoryQListModel; class QModelIndex; namespace Ui { class RecentHistoryItemsView; } class RecentHistoryItemsView : public QWidget { Q_OBJECT public: /** Set the model and the history name managed by this widget */ void Initialize(GlobalUIModel *model, std::string history); explicit RecentHistoryItemsView(QWidget *parent = 0); ~RecentHistoryItemsView(); signals: void RecentItemSelected(QString filename); private slots: void OnRemoveRecentImageListItem(); void on_listRecent_clicked(const QModelIndex &index); void on_listRecent_customContextMenuRequested(const QPoint &pos); private: Ui::RecentHistoryItemsView *ui; GlobalUIModel *m_Model; QMenu *m_RecentListPopup; QAction *m_RecentListPopupAction; HistoryQListModel *m_HistoryModel; std::string m_History; }; #endif // RECENTHISTORYITEMSVIEW_H itksnap-3.4.0/GUI/Qt/Components/RecentHistoryItemsView.ui000066400000000000000000000324751263013355200233350ustar00rootroot00000000000000 RecentHistoryItemsView 0 0 662 544 Form 0 255 255 255 35 35 35 53 53 53 44 44 44 17 17 17 23 23 23 255 255 255 255 255 255 255 255 255 0 0 0 35 35 35 0 0 0 17 17 17 255 255 220 0 0 0 255 255 255 35 35 35 53 53 53 44 44 44 17 17 17 23 23 23 255 255 255 255 255 255 255 255 255 0 0 0 35 35 35 0 0 0 17 17 17 255 255 220 0 0 0 17 17 17 35 35 35 53 53 53 44 44 44 17 17 17 23 23 23 17 17 17 255 255 255 17 17 17 35 35 35 35 35 35 0 0 0 35 35 35 255 255 220 0 0 0 75 true true Qt::CustomContextMenu QListView::item:hover { background-color:rgb(88, 88, 88); } QAbstractItemView::NoEditTriggers false QAbstractItemView::NoSelection 128 128 QListView::Static QListView::Adjust QListView::SinglePass 24 200 160 QListView::IconMode false true false itksnap-3.4.0/GUI/Qt/Components/SNAPComponent.cxx000066400000000000000000000016621263013355200215010ustar00rootroot00000000000000#include "SNAPComponent.h" #include "LatentITKEventNotifier.h" #include "QtWidgetActivator.h" #include "GlobalUIModel.h" #include "SNAPUIFlag.h" SNAPComponent::SNAPComponent(QWidget *parent) : QWidget(parent) { } void SNAPComponent ::connectITK(itk::Object *src, const itk::EventObject &ev, const char *slot) { LatentITKEventNotifier::connect(src, ev, this, slot); } void SNAPComponent ::disconnectITK(itk::Object *src, unsigned long tag) { LatentITKEventNotifier::disconnect(src, tag); } /** A QObject used as a convenience to detach observers from ITK objects. When destroyed, this object will disconnect itself from the ITK object */ /* class QtObserverTagHolder : public QObject { public: QtObserverTagHolder(itk::Object *source, unsigned long tag) : m_Source(source), m_Tag(tag) {} ~QtObserverTagHolder() { m_Source->RemoveObserver(m_Tag); } private: itk::Object *m_Source; unsigned long m_Tag; }; */ itksnap-3.4.0/GUI/Qt/Components/SNAPComponent.h000066400000000000000000000022121263013355200211160ustar00rootroot00000000000000#ifndef SNAPCOMPONENT_H #define SNAPCOMPONENT_H #include #include "UIState.h" class EventBucket; class GlobalUIModel; namespace itk { class EventObject; class Object; } /** \class SNAPComponent \brief A base class for all SNAP components that consist of a set of widgets layed out in a parent widget. This class has mostly helper functionality to reduce the number of includes and shorten the common calls made from SNAP widgets */ class SNAPComponent : public QWidget { Q_OBJECT public: explicit SNAPComponent(QWidget *parent = 0); public slots: // Default slot for model updates virtual void onModelUpdate(const EventBucket &bucket) {} protected: /** Register to receive ITK events from object src. Events will be cached in an event bucket and delivered once execution returns to the UI loop */ void connectITK(itk::Object *src, const itk::EventObject &ev, const char *slot = SLOT(onModelUpdate(const EventBucket &))); void disconnectITK(itk::Object *src, unsigned long tag); #if QT_VERSION < 0x050000 float devicePixelRatio() const { return 1.0f; } #endif }; #endif // SNAPCOMPONENT_H itksnap-3.4.0/GUI/Qt/Components/SNAPQtCommon.cxx000066400000000000000000000367041263013355200213010ustar00rootroot00000000000000#include "SNAPQtCommon.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QtCursorOverride.h" #include "GlobalUIModel.h" #include "SystemInterface.h" #include "IRISApplication.h" #include "HistoryManager.h" #include "SimpleFileDialogWithHistory.h" #include "ColorLabelTable.h" #include "ColorMap.h" #include "ColorMapModel.h" #include "ImageIOWizard.h" QIcon CreateColorBoxIcon(int w, int h, const QBrush &brush) { QRect r(2,2,w-5,w-5); QPixmap pix(w, h); pix.fill(QColor(0,0,0,0)); QPainter paint(&pix); paint.setPen(Qt::black); paint.setBrush(brush); paint.drawRect(r); return QIcon(pix); } QIcon CreateColorBoxIcon(int w, int h, const QColor &rgb) { return CreateColorBoxIcon(w, h, QBrush(rgb)); } QIcon CreateColorBoxIcon(int w, int h, const Vector3ui &rgb) { return CreateColorBoxIcon(w, h, QColor(rgb(0), rgb(1), rgb(2))); } QIcon CreateInvisibleIcon(int w, int h) { // Add initial entries to background QPixmap pix(w, h); pix.fill(QColor(0,0,0,0)); return QIcon(pix); } #include #include QIcon CreateColorMapIcon(int w, int h, ColorMap *cmap) { // Maintain a static map of icons for each existing color map typedef std::pair StampedIcon; typedef std::map IconMap; static IconMap icon_map; // Get the color map's timestamp itk::TimeStamp ts_cmap = cmap->GetTimeStamp(); // Try to find the icon in the icon map IconMap::iterator it = icon_map.find(cmap); if(it != icon_map.end()) { // We have created an icon for this before. Check that it's current and // that it matches the requested size (only one size is cached!) itk::TimeStamp ts_icon = it->second.first; if(ts_cmap == ts_icon) return it->second.second; } // Create the actual icon QPixmap pix(w, h); pix.fill(QColor(0,0,0,0)); QPainter paint(&pix); for(int x = 3; x <= w-4; x++) { double t = (x - 3.0) / (w - 7.0); ColorMap::RGBAType rgba = cmap->MapIndexToRGBA(t); paint.setPen(QColor(rgba[0], rgba[1], rgba[2])); paint.drawLine(x, 3, x, w-4); } paint.setPen(Qt::black); QRect r(2,2,w-5,w-5); paint.drawRect(r); QIcon icon(pix); // Save the icon icon_map[cmap] = std::make_pair(ts_cmap, icon); return icon; } QStandardItem *CreateColorMapPresetItem( ColorMapModel *cmm, const std::string &preset) { ColorMap *cm = cmm->GetPresetManager()->GetPreset(preset); QIcon icon = CreateColorMapIcon(16, 16, cm); QStandardItem *item = new QStandardItem(icon, from_utf8(preset)); item->setData(QVariant::fromValue(preset), Qt::UserRole); return item; } void PopulateColorMapPresetCombo(QComboBox *combo, ColorMapModel *model) { // Get the list of system presets and custom presets from the model ColorMapModel::PresetList pSystem, pUser; model->GetPresets(pSystem, pUser); // What is the current item QVariant currentItemData = combo->itemData(combo->currentIndex(), Qt::UserRole); std::string currentPreset = currentItemData.value(); int newIndex = -1; // The system presets don't change, so we only need to set them the // first time around QStandardItemModel *sim = new QStandardItemModel(); for(unsigned int i = 0; i < pSystem.size(); i++) { sim->appendRow(CreateColorMapPresetItem(model, pSystem[i])); if(currentPreset == pSystem[i]) newIndex = i; } for(unsigned int i = 0; i < pUser.size(); i++) { sim->appendRow(CreateColorMapPresetItem(model, pUser[i])); if(currentPreset == pUser[i]) newIndex = pSystem.size() + i; } // Update the model combo->setModel(sim); // Set the current item if possible combo->setCurrentIndex(newIndex); // Insert separator combo->insertSeparator(pSystem.size()); } QBrush GetBrushForColorLabel(const ColorLabel &cl) { return QBrush(QColor(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2))); } QBrush GetBrushForDrawOverFilter(DrawOverFilter flt, const ColorLabel &cl) { switch(flt.CoverageMode) { case PAINT_OVER_VISIBLE: return QBrush(Qt::black, Qt::Dense6Pattern); case PAINT_OVER_ONE: return QBrush(QColor(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2))); case PAINT_OVER_ALL: return QBrush(Qt::black, Qt::BDiagPattern); } return QBrush(); } QString GetTitleForColorLabel(const ColorLabel &cl) { return QString::fromUtf8(cl.GetLabel()); } QString GetTitleForDrawOverFilter(DrawOverFilter flt, const ColorLabel &cl) { switch(flt.CoverageMode) { case PAINT_OVER_VISIBLE: return QString("All visible labels"); case PAINT_OVER_ONE: return QString::fromUtf8(cl.GetLabel()); case PAINT_OVER_ALL: return QString("All labels"); } return QString(); } QBrush GetBrushForColorLabel(int label, ColorLabelTable *clt) { return GetBrushForColorLabel(clt->GetColorLabel(label)); } QBrush GetBrushForDrawOverFilter(DrawOverFilter flt, ColorLabelTable *clt) { return GetBrushForDrawOverFilter(flt, clt->GetColorLabel(flt.DrawOverLabel)); } QString GetTitleForColorLabel(int label, ColorLabelTable *clt) { return GetTitleForColorLabel(clt->GetColorLabel(label)); } QString GetTitleForDrawOverFilter(DrawOverFilter flt, ColorLabelTable *clt) { return GetTitleForDrawOverFilter(flt, clt->GetColorLabel(flt.DrawOverLabel)); } QIcon CreateLabelComboIcon(int w, int h, LabelType fg, DrawOverFilter bg, ColorLabelTable *clt) { // TODO: this could be made a little prettier QGraphicsScene scene(0,0,w,h); QPixmap pm(w, h); pm.fill(QColor(0,0,0,0)); QPainter qp(&pm); QBrush brush_fg = GetBrushForColorLabel(fg, clt); QBrush brush_bg = GetBrushForDrawOverFilter(bg, clt); QGraphicsItem *item_bg = scene.addRect(w/3,h/3,w/2+1,h/2+1,QPen(Qt::black), brush_bg); scene.addRect(2,2,w/2+1,h/2+1,QPen(Qt::black), brush_fg); QGraphicsDropShadowEffect *eff_bg = new QGraphicsDropShadowEffect(&scene); eff_bg->setBlurRadius(1.0); eff_bg->setOffset(1.0); eff_bg->setColor(QColor(63,63,63,100)); item_bg->setGraphicsEffect(eff_bg); scene.render(&qp); return QIcon(pm); } QString CreateLabelComboTooltip(LabelType fg, DrawOverFilter bg, ColorLabelTable *clt) { /* return QString( "" "

Foreground label:
%1

" "

Background label:
%2

" ""). arg(GetTitleForColorLabel(fg, clt)).arg(GetTitleForDrawOverFilter(bg, clt)); */ return QString( "" "Set active label to %1 " " and the paint over mask to %2."). arg(GetTitleForColorLabel(fg, clt)).arg(GetTitleForDrawOverFilter(bg, clt)); } QAction *FindUpstreamAction(QWidget *widget, const QString &targetActionName) { // Look for a parent of QMainWindow type QMainWindow *topwin = NULL; for(QObject *p = widget; p != NULL; p = p->parent()) { if((topwin = dynamic_cast(p)) != NULL) break; } // If nothing found, try a global search if(!topwin) { QWidgetList lst = QApplication::topLevelWidgets(); for(QWidgetList::Iterator it = lst.begin(); it != lst.end(); ++it) { QWidget *w = *it; if((topwin = dynamic_cast(w)) != NULL) break; } } // Look for the action QAction *result = NULL; if(topwin) { result = topwin->findChild(targetActionName); } if(!result) std::cerr << "Failed find upstream action " << targetActionName.toStdString() << std::endl; return result; } void ConnectWidgetToTopLevelAction( QWidget *w, const char *signal, QString actionName) { QAction *action = FindUpstreamAction(w, actionName); QObject::connect(w, signal, action, SLOT(trigger())); } bool TriggerUpstreamAction(QWidget *widget, const QString &targetActionName) { // Find and execute the relevant action QAction *action = FindUpstreamAction(widget, targetActionName); if(action) { action->trigger(); return true; } else return false; } QStringList toQStringList(const std::vector inlist) { QStringList qsl; qsl.reserve(inlist.size()); for(std::vector::const_iterator it = inlist.begin(); it != inlist.end(); ++it) { qsl.push_back(from_utf8(*it)); } return qsl; } void ReportNonLethalException(QWidget *parent, std::exception &exc, QString windowTitleText, QString mainErrorText) { QMessageBox b(parent); b.setWindowTitle(QString("%1 - ITK-SNAP").arg(windowTitleText)); if(mainErrorText.isNull()) { b.setText(exc.what()); } else { b.setText(mainErrorText); b.setDetailedText(exc.what()); } b.setIcon(QMessageBox::Critical); b.exec(); } void PopulateHistoryMenu( QMenu *menu, QObject *receiver, const char *slot, const QStringList &local_history, const QStringList &global_history) { menu->clear(); QStringListIterator itLocal(local_history); itLocal.toBack(); while(itLocal.hasPrevious()) { QString entry = itLocal.previous(); QAction *action = menu->addAction(entry); QObject::connect(action, SIGNAL(triggered()), receiver, slot); } int nLocal = menu->actions().size(); QStringListIterator itGlobal(global_history); itGlobal.toBack(); while(itGlobal.hasPrevious()) { QString entry = itGlobal.previous(); if(local_history.indexOf(entry) == -1) { QAction *action = menu->addAction(entry); QObject::connect(action, SIGNAL(triggered()), receiver, slot); } } if(nLocal > 0 && menu->actions().size() > nLocal) menu->insertSeparator(menu->actions()[nLocal]); } void PopulateHistoryMenu( QMenu *menu, QObject *receiver, const char *slot, GlobalUIModel *model, QString hist_category) { HistoryManager *hm = model->GetDriver()->GetSystemInterface()->GetHistoryManager(); QStringList hl = toQStringList(hm->GetLocalHistory(hist_category.toStdString())); QStringList hg = toQStringList(hm->GetGlobalHistory(hist_category.toStdString())); PopulateHistoryMenu(menu, receiver, slot, hl, hg); } /** Show a generic file save dialog with a history dropdown */ QString ShowSimpleSaveDialogWithHistory( QWidget *parent, GlobalUIModel *model, QString hist_category, QString window_title, QString file_title, QString file_pattern, bool force_extension, QString init_file) { return SimpleFileDialogWithHistory::showSaveDialog( parent, model, window_title, file_title, hist_category, file_pattern, force_extension,init_file); } /** Show a generic file open dialog with a history dropdown */ QString ShowSimpleOpenDialogWithHistory( QWidget *parent, GlobalUIModel *model, QString hist_category, QString window_title, QString file_title, QString file_pattern, QString init_file) { return SimpleFileDialogWithHistory::showOpenDialog( parent, model, window_title, file_title, hist_category, file_pattern, init_file); } bool SaveImageLayer(GlobalUIModel *model, ImageWrapperBase *wrapper, LayerRole role, bool force_interactive, QWidget *parent) { // Create a model for saving the segmentation image via a wizard SmartPtr wiz_model = model->CreateIOWizardModelForSave(wrapper, role); // Interactive or not? if(force_interactive || wiz_model->GetSuggestedFilename().size() == 0) { // Execute the IO wizard ImageIOWizard wiz(parent); wiz.SetModel(wiz_model); wiz.exec(); } else { try { QtCursorOverride curse(Qt::WaitCursor); wiz_model->SaveImage(wiz_model->GetSuggestedFilename()); } catch(std::exception &exc) { ReportNonLethalException( parent, exc, "Image IO Error", QString("Failed to save image %1").arg( from_utf8(wiz_model->GetSuggestedFilename()))); } } return wiz_model->GetSaveDelegate()->IsSaveSuccessful(); } bool SaveWorkspace(QWidget *parent, GlobalUIModel *model, bool interactive, QWidget *widget) { // Get the currently stored project name QString file_abs = from_utf8(model->GetGlobalState()->GetProjectFilename()); // Prompt for a project filename if one was not provided if(interactive || file_abs.length() == 0) { // Use the dialog with history - to be consistent with other parts of SNAP QString file = ShowSimpleSaveDialogWithHistory( parent, model, "Project", "Save Workspace", "Workspace File", "ITK-SNAP Workspace Files (*.itksnap)", "itksnap"); // If user hits cancel, move on if(file.isNull()) return false; // Make sure to get an absolute path, because the project needs that info file_abs = QFileInfo(file).absoluteFilePath(); } // If file was provided, set it as the current project file try { model->GetDriver()->SaveProject(to_utf8(file_abs)); return true; } catch(exception &exc) { ReportNonLethalException(widget, exc, "Error Saving Project", QString("Failed to save project %1").arg(file_abs)); return false; } } #include #include // TODO: this is so f***ng lame! Global variable!!! Put this inside of // a class!!!! #include #include #include QMap g_CategoryToLastPathMap; QString GetFileDialogPath(GlobalUIModel *model, const char *HistoryName) { // Already have something for this category? Then use it if(g_CategoryToLastPathMap.find(HistoryName) != g_CategoryToLastPathMap.end()) return g_CategoryToLastPathMap[HistoryName].absolutePath(); // Is there a main image loaded if(model->GetDriver()->IsMainImageLoaded()) { QString fn = from_utf8(model->GetDriver()->GetCurrentImageData()->GetMain()->GetFileName()); return QFileInfo(fn).absolutePath(); } // Use the initial directory if(model->GetGlobalState()->GetInitialDirectory().length()) { // This directory should alrady be verified readable when assigned in main.cxx QDir dir(from_utf8(model->GetGlobalState()->GetInitialDirectory())); return dir.absolutePath(); } // Last resort : use home directory return QDir::homePath(); } void UpdateFileDialogPathForCategory(const char *HistoryName, QString dir) { g_CategoryToLastPathMap[HistoryName] = QDir(dir); } void TranslateStringTooltipKeyModifiers(QString &tooltip) { tooltip.replace(QChar(0x21e7),"Shift+"); tooltip.replace(QChar(0x2318),"Ctrl+"); tooltip.replace(QChar(0x2325),"Alt+"); tooltip.replace("⌃","Ctrl+"); tooltip.replace(QChar(0x238b),"Esc"); } void TranslateChildTooltipKeyModifiers(QWidget *parent) { // Get all the child widgets QList child_widgets = parent->findChildren(); foreach(QWidget *child, child_widgets) { QString tooltip = child->toolTip(); TranslateStringTooltipKeyModifiers(tooltip); child->setToolTip(tooltip); } // Get all the child actions QList child_actions = parent->findChildren(); foreach(QAction *child, child_actions) { QString tooltip = child->toolTip(); TranslateStringTooltipKeyModifiers(tooltip); child->setToolTip(tooltip); } } itksnap-3.4.0/GUI/Qt/Components/SNAPQtCommon.h000066400000000000000000000131011263013355200207100ustar00rootroot00000000000000#ifndef SNAPQTCOMMON_H #define SNAPQTCOMMON_H #include #include #include #include #include #include Q_DECLARE_METATYPE(std::string) class QWidget; class QAction; class GlobalUIModel; class QMenu; class QSlider; class QSpinBox; class ColorLabelTable; class ColorLabel; class ColorMap; class QComboBox; class ColorMapModel; class ImageWrapperBase; // Generate an icon with a black border and a given fill color QIcon CreateColorBoxIcon(int w, int h, const QBrush &brush); QIcon CreateColorBoxIcon(int w, int h, const QColor &rgb); QIcon CreateColorBoxIcon(int w, int h, const Vector3ui &rgb); QIcon CreateInvisibleIcon(int w, int h); // This creates an icon for a given color map. This function uses internal // caching so that the icon is only regenerated if the color map has been // modified since the last time the icon was generated. QIcon CreateColorMapIcon(int w, int h, ColorMap *cmap); /** * Generate a combo box with color map presets. Ideally, this would be handled * through the coupling mechanism, but the mechanism does not currently support * separators. Such a combo box is required in a few places, so it made sense to * put the code here */ void PopulateColorMapPresetCombo(QComboBox *combo, ColorMapModel *model); // Generate a brush corresponding to a color label QBrush GetBrushForColorLabel(const ColorLabel &cl); QBrush GetBrushForDrawOverFilter(DrawOverFilter flt, const ColorLabel &cl); QString GetTitleForColorLabel(const ColorLabel &cl); QString GetTitleForDrawOverFilter(DrawOverFilter flt, const ColorLabel &cl); QBrush GetBrushForColorLabel(int label, ColorLabelTable *clt); QBrush GetBrushForDrawOverFilter(DrawOverFilter flt, ColorLabelTable *clt); QString GetTitleForColorLabel(int label, ColorLabelTable *clt); QString GetTitleForDrawOverFilter(DrawOverFilter flt, ColorLabelTable *clt); // Create an icon and tooltip corresponding to a combination of a drawing label // and a draw-over label QIcon CreateLabelComboIcon(int w, int h, LabelType fg, DrawOverFilter bg, ColorLabelTable *clt); QString CreateLabelComboTooltip(LabelType fg, DrawOverFilter bg, ColorLabelTable *clt); // Find an upstream action for a widget QAction* FindUpstreamAction(QWidget *w, const QString &targetActionName); // Connect a widget to the trigger slot in an upstream action void ConnectWidgetToTopLevelAction(QWidget *w, const char *signal, QString actionName); // Trigger an upstream action in a Qt widget. Return code is true // if the upstream action was found, false otherwise bool TriggerUpstreamAction(QWidget *w, const QString &targetActionName); // Convert a container of std::strings into a QStringList QStringList toQStringList(const std::vector inlist); // Find a parent window of appropriate class template TWidget *findParentWidget(QObject *w) { do { w = w->parent(); if(w) { TWidget *tw = dynamic_cast(w); if(tw) return tw; } } while(w); return NULL; } // Couple a slider and a // Standard way for handling non-lethal exceptions void ReportNonLethalException(QWidget *parent, std::exception &exc, QString windowTitleText, QString mainErrorText = QString()); /** Populate a menu with history items */ void PopulateHistoryMenu( QMenu *menu, QObject *receiver, const char *slot, GlobalUIModel *model, QString hist_category); void PopulateHistoryMenu( QMenu *menu, QObject *receiver, const char *slot, const QStringList &local_history, const QStringList &global_history); /** Get the path in which to open a file dialog */ QString GetFileDialogPath(GlobalUIModel *model, const char *HistoryName); /** Save the path where something was saved */ void UpdateFileDialogPathForCategory(const char *HistoryName, QString dir); /** Show a generic file save dialog with a history dropdown */ QString ShowSimpleSaveDialogWithHistory(QWidget *parent, GlobalUIModel *model, QString hist_category, QString window_title, QString file_title, QString file_pattern, bool force_extension, QString init_file = QString()); /** Show a generic file open dialog with a history dropdown */ QString ShowSimpleOpenDialogWithHistory(QWidget *parent, GlobalUIModel *model, QString hist_category, QString window_title, QString file_title, QString file_pattern, QString init_file = QString()); /** * This static save method provides a simple interface for saving an * image layer either interactively or non-interactively depending on * whether the image layer has a filename set. Exceptions are handled * within the method. The method returns true if the image was actually * saved, and false if there was a problem, or user cancelled. */ bool SaveImageLayer(GlobalUIModel *model, ImageWrapperBase *wrapper, LayerRole role, bool force_interactive = false, QWidget *parent = NULL); /** * A static method to save the project/workspace to file */ bool SaveWorkspace(QWidget *parent, GlobalUIModel *model, bool interactive, QWidget *widget); /** Convert a QString to a std::string using UTF8 encoding */ inline std::string to_utf8(const QString &qstring) { return std::string(qstring.toUtf8().constData()); } /** Convert an std::string with UTF8 encoding to a Qt string */ inline QString from_utf8(const std::string &input) { return QString::fromUtf8(input.c_str()); } /** Method translates the tooltips in all child widgets from MACOS to windows/linux format */ void TranslateChildTooltipKeyModifiers(QWidget *parent); #endif // SNAPQTCOMMON_H itksnap-3.4.0/GUI/Qt/Components/SliceViewPanel.cxx000066400000000000000000000514271263013355200217330ustar00rootroot00000000000000#include "SliceViewPanel.h" #include "ui_SliceViewPanel.h" #include "GlobalUIModel.h" #include "SNAPEvents.h" #include "IRISException.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "itkCommand.h" #include "CrosshairsRenderer.h" #include "PolygonDrawingRenderer.h" #include "PaintbrushRenderer.h" #include "SnakeROIRenderer.h" #include "SnakeROIModel.h" #include "SliceWindowCoordinator.h" #include "PolygonDrawingModel.h" #include "AnnotationModel.h" #include "AnnotationRenderer.h" #include "QtWidgetActivator.h" #include "SnakeModeRenderer.h" #include "SnakeWizardModel.h" #include "DisplayLayoutModel.h" #include "PaintbrushModel.h" #include "SliceWindowDecorationRenderer.h" #include "LayerInspectorDialog.h" #include "MainImageWindow.h" #include "SNAPQtCommon.h" #include "QtScrollbarCoupling.h" #include "QtSliderCoupling.h" #include #include #include #include #include SliceViewPanel::SliceViewPanel(QWidget *parent) : SNAPComponent(parent), ui(new Ui::SliceViewPanel) { ui->setupUi(this); // Initialize m_GlobalUI = NULL; m_SliceModel = NULL; // Create my own renderers m_SnakeModeRenderer = SnakeModeRenderer::New(); m_DecorationRenderer = SliceWindowDecorationRenderer::New(); QString menuStyle = "font-size: 12px;"; // Create the popup menus for the polygon mode m_MenuPolyInactive = new QMenu(ui->imPolygon); m_MenuPolyInactive->setStyleSheet(menuStyle); m_MenuPolyInactive->addAction(ui->actionPaste); m_MenuPolyDrawing = new QMenu(ui->imPolygon); m_MenuPolyDrawing->setStyleSheet(menuStyle); m_MenuPolyDrawing->addAction(ui->actionComplete); m_MenuPolyDrawing->addAction(ui->actionCompleteAndAccept); m_MenuPolyDrawing->addAction(ui->actionUndo); m_MenuPolyDrawing->addAction(ui->actionClearDrawing); m_MenuPolyEditing = new QMenu(ui->imPolygon); m_MenuPolyEditing->setStyleSheet(menuStyle); m_MenuPolyEditing->addAction(ui->actionAccept); m_MenuPolyEditing->addAction(ui->actionDeleteSelected); m_MenuPolyEditing->addAction(ui->actionSplitSelected); m_MenuPolyEditing->addAction(ui->actionClearPolygon); // Connect the actions to the toolbar buttons (sucks to do this by hand) ui->btnAcceptPolygon->setDefaultAction(ui->actionAccept); ui->btnPastePolygon->setDefaultAction(ui->actionPaste); ui->btnClearDrawing->setDefaultAction(ui->actionClearDrawing); ui->btnCloseLoop->setDefaultAction(ui->actionComplete); ui->btnDeleteNodes->setDefaultAction(ui->actionDeleteSelected); ui->btnDeletePolygon->setDefaultAction(ui->actionClearPolygon); ui->btnSplitNodes->setDefaultAction(ui->actionSplitSelected); ui->btnUndoLast->setDefaultAction(ui->actionUndo); ui->btnAnnotationAcceptLine->setDefaultAction(ui->actionAnnotationAcceptLine); ui->btnAnnotationClearLine->setDefaultAction(ui->actionAnnotationClearLine); ui->btnAnnotationSelectAll->setDefaultAction(ui->actionAnnotationSelectAll); ui->btnAnnotationDeleteSelected->setDefaultAction(ui->actionAnnotationDelete); ui->btnAnnotationNext->setDefaultAction(ui->actionAnnotationNext); ui->btnAnnotationPrevious->setDefaultAction(ui->actionAnnotationPrevious); this->addAction(ui->actionZoom_In); this->addAction(ui->actionZoom_Out); // Connect the context menu signal from polygon mode to this widget connect(ui->imPolygon, SIGNAL(contextMenuRequested()), SLOT(onContextMenu())); // Arrange the interaction modes into a tree structure. The first child of // every interaction mode is an empty QWidget. The tree is used to allow // events to fall through from one interaction mode to another // TODO: can we think of a more dynamic system for doing this? Essentially, // there is a pipeline through which events pass through, which is // reconfigured when the mode changes. // Add all the individual interactors to the main panel using a stacked // layout. This assures that all of the interactors have the same size as // the sliceView. The actual propagation of events is handled by the // event filter logic. /* QStackedLayout *loMain = new QStackedLayout(); loMain->setContentsMargins(0,0,0,0); loMain->addWidget(ui->imCrosshairs); loMain->addWidget(ui->imZoomPan); loMain->addWidget(ui->imThumbnail); loMain->addWidget(ui->imPolygon); loMain->addWidget(ui->imSnakeROI); loMain->addWidget(ui->imPaintbrush); delete ui->sliceView->layout(); ui->sliceView->setLayout(loMain); */ // Configure the initial event chain m_CurrentEventFilter = NULL; ConfigureEventChain(ui->imCrosshairs); // Also lay out the pages QStackedLayout *loPages = new QStackedLayout(); loPages->addWidget(ui->pageDefault); loPages->addWidget(ui->pagePolygonDraw); loPages->addWidget(ui->pagePolygonEdit); loPages->addWidget(ui->pagePolygonInactive); loPages->addWidget(ui->pageAnnotateLineActive); loPages->addWidget(ui->pageAnnotateSelect); delete ui->toolbar->layout(); ui->toolbar->setLayout(loPages); // Send wheel events from Crosshairs mode to the slider ui->imCrosshairs->SetWheelEventTargetWidget(ui->inSlicePosition); // Set page size on the slice position widget ui->inSlicePosition->setPageStep(5); // Set up the drawing cursor QBitmap bmBitmap(":/root/crosshair_cursor_bitmap.png"); QBitmap bmMask(":/root/crosshair_cursor_mask.png"); m_DrawingCrosshairCursor = new QCursor(bmBitmap, bmMask, 7, 7); // Configure the context tool button m_ContextToolButton = new QToolButton(ui->sliceView); m_ContextToolButton->setIcon(QIcon(":/root/context_gray_10.png")); m_ContextToolButton->setVisible(false); m_ContextToolButton->setAutoRaise(true); m_ContextToolButton->setIconSize(QSize(10,10)); m_ContextToolButton->setMinimumSize(QSize(16,16)); m_ContextToolButton->setMaximumSize(QSize(16,16)); m_ContextToolButton->setPopupMode(QToolButton::InstantPopup); m_ContextToolButton->setStyleSheet("QToolButton::menu-indicator { image: none; }"); } SliceViewPanel::~SliceViewPanel() { delete ui; delete m_DrawingCrosshairCursor; } GenericSliceView * SliceViewPanel::GetSliceView() { return ui->sliceView; } void SliceViewPanel::Initialize(GlobalUIModel *model, unsigned int index) { // Store the model this->m_GlobalUI = model; this->m_Index = index; // Get the slice model m_SliceModel = m_GlobalUI->GetSliceModel(index); // Initialize the slice view ui->sliceView->SetModel(m_SliceModel); // Initialize the interaction modes ui->imCrosshairs->SetModel(m_GlobalUI->GetCursorNavigationModel(index)); ui->imZoomPan->SetModel(m_GlobalUI->GetCursorNavigationModel(index)); ui->imZoomPan->SetMouseButtonBehaviorToZoomPanMode(); ui->imThumbnail->SetModel(m_GlobalUI->GetCursorNavigationModel(index)); ui->imPolygon->SetModel(m_GlobalUI->GetPolygonDrawingModel(index)); ui->imSnakeROI->SetModel(m_GlobalUI->GetSnakeROIModel(index)); ui->imPaintbrush->SetModel(m_GlobalUI->GetPaintbrushModel(index)); ui->imAnnotation->SetModel(m_GlobalUI->GetAnnotationModel(index)); // ui->labelQuickList->SetModel(m_GlobalUI); // Initialize the 'orphan' renderers (without a custom widget) GenericSliceRenderer *parentRenderer = static_cast(ui->sliceView->GetRenderer()); m_DecorationRenderer->SetParentRenderer(parentRenderer); m_SnakeModeRenderer->SetParentRenderer(parentRenderer); m_SnakeModeRenderer->SetModel(m_GlobalUI->GetSnakeWizardModel()); // Listen to cursor change events, which require a repaint of the slice view connectITK(m_GlobalUI->GetDriver(), CursorUpdateEvent()); connectITK(m_GlobalUI->GetDriver(), MainImageDimensionsChangeEvent()); // Add listener for changes to the model connectITK(m_SliceModel, ModelUpdateEvent()); // Listen to toolbar change events connectITK(m_GlobalUI, ToolbarModeChangeEvent()); // Listen to polygon and annotation model state change events connectITK(m_GlobalUI->GetPolygonDrawingModel(index), StateMachineChangeEvent()); connectITK(m_GlobalUI->GetAnnotationModel(index), StateMachineChangeEvent()); // Listen to the Snake ROI model too connectITK(m_GlobalUI->GetSnakeROIModel(index), ModelUpdateEvent()); // Listen to paintbrush motion connectITK(m_GlobalUI->GetPaintbrushModel(index), PaintbrushModel::PaintbrushMovedEvent()); // Listen to annotation changes connectITK(m_GlobalUI->GetAnnotationModel(index), ModelUpdateEvent()); // Listen to all (?) events from the snake wizard as well connectITK(m_GlobalUI->GetSnakeWizardModel(), IRISEvent()); // Widget coupling makeCoupling(ui->inSlicePosition, m_SliceModel->GetSliceIndexModel()); // Activation activateOnFlag(this, m_GlobalUI, UIF_BASEIMG_LOADED); // Activation for the tile/thumb controls activateOnFlag(ui->btnToggleLayout, m_GlobalUI, UIF_MULTIPLE_BASE_LAYERS, QtWidgetActivator::HideInactive); // Set up activation for polygon buttons PolygonDrawingModel *pm = m_GlobalUI->GetPolygonDrawingModel(index); activateOnAllFlags(ui->actionAccept, pm, UIF_EDITING, UIF_HAVEPOLYGON); activateOnAllFlags(ui->actionPaste, pm, UIF_INACTIVE, UIF_HAVECACHED); activateOnAllFlags(ui->actionClearDrawing, pm, UIF_DRAWING, UIF_HAVEPOLYGON); activateOnAllFlags(ui->actionComplete, pm, UIF_DRAWING, UIF_HAVEPOLYGON); activateOnAllFlags(ui->actionCompleteAndAccept, pm, UIF_DRAWING, UIF_HAVEPOLYGON); activateOnAllFlags(ui->actionDeleteSelected, pm, UIF_EDITING, UIF_HAVE_VERTEX_SELECTION); activateOnAllFlags(ui->actionSplitSelected, pm, UIF_EDITING, UIF_HAVE_EDGE_SELECTION); activateOnAllFlags(ui->actionUndo, pm, UIF_DRAWING, UIF_HAVEPOLYGON); activateOnAllFlags(ui->actionClearPolygon, pm, UIF_EDITING, UIF_HAVEPOLYGON); // Set up activation from the annotation model AnnotationModel *am = m_GlobalUI->GetAnnotationModel(index); activateOnFlag(ui->actionAnnotationAcceptLine, am, AnnotationModel::UIF_LINE_MODE_DRAWING); activateOnFlag(ui->actionAnnotationClearLine, am, AnnotationModel::UIF_LINE_MODE_DRAWING); // Update the expand view button this->UpdateExpandViewButton(); // Listen to the events affecting the expand view button DisplayLayoutModel *dlm = m_GlobalUI->GetDisplayLayoutModel(); connectITK(dlm, DisplayLayoutModel::ViewPanelLayoutChangeEvent()); connectITK(dlm, DisplayLayoutModel::LayerLayoutChangeEvent()); // Arrange the rendering overlays and widgets based on current mode this->OnToolbarModeChange(); // Listen for hover changes to move and activate widgets connectITK(m_SliceModel->GetHoveredImageLayerIdModel(), ValueChangedEvent(), SLOT(OnHoveredLayerChange(const EventBucket &))); connectITK(m_SliceModel->GetHoveredImageIsThumbnailModel(), ValueChangedEvent(), SLOT(OnHoveredLayerChange(const EventBucket &))); } void SliceViewPanel::onModelUpdate(const EventBucket &eb) { if(eb.HasEvent(ToolbarModeChangeEvent()) || eb.HasEvent(StateMachineChangeEvent())) { OnToolbarModeChange(); } if(eb.HasEvent(DisplayLayoutModel::ViewPanelLayoutChangeEvent()) || eb.HasEvent(DisplayLayoutModel::LayerLayoutChangeEvent())) { UpdateExpandViewButton(); } ui->sliceView->update(); } void SliceViewPanel::on_inSlicePosition_valueChanged(int value) { // Update the text output int pos = ui->inSlicePosition->value(); int lim = ui->inSlicePosition->maximum(); ui->lblSliceInfo->setText(QString("%1 of %2").arg(pos+1).arg(lim+1)); } void SliceViewPanel::ConfigureEventChain(QWidget *w) { // Remove all event filters from the slice view QObjectList kids = ui->sliceView->children(); for(QObjectList::Iterator it = kids.begin(); it!=kids.end(); ++it) ui->sliceView->removeEventFilter(*it); // Now add the event filters in the order in which we want them to react // to events. The last event filter is first to receive events, and should // thus be the thumbnail interaction mode. The first event filter is always // the crosshairs interaction mode, which is the fallback for all others. ui->sliceView->installEventFilter(ui->imCrosshairs); // If the current mode is not crosshairs mode, add it as the filter if(w != ui->imCrosshairs) { ui->sliceView->installEventFilter(w); } // The last guy in the chain is the thumbnail interactor ui->sliceView->installEventFilter(ui->imThumbnail); } // TODO: implement semi-transparent rendering on widgets on top of the // OpenGL scene using code from // http://www.qtcentre.org/wiki/index.php?title=Accelerate_your_Widgets_with_OpenGL void SliceViewPanel::enterEvent(QEvent *) { /* ui->mainToolbar->show(); ui->sidebar->show(); */ } void SliceViewPanel::leaveEvent(QEvent *) { /* ui->mainToolbar->hide(); ui->sidebar->hide(); */ } void SliceViewPanel::SetActiveMode(QWidget *mode, bool clearChildren) { // If the widget does not have a stacked layout, do nothing, we've reached // the end of the recursion QStackedLayout *loParent = dynamic_cast(mode->parentWidget()->layout()); if(loParent) { // Set the mode as the current widget in the parent loParent->setCurrentWidget(mode); // Make sure the parent widget is also the current widget SetActiveMode(mode->parentWidget(), false); // Make sure no children are selected if(clearChildren) { // If the selected mode has child modes, make sure none is selected QStackedLayout *lo = dynamic_cast(mode->layout()); if(lo) lo->setCurrentIndex(0); } } } void SliceViewPanel::OnToolbarModeChange() { // Get the renderer and configure its overlays GenericSliceRenderer *ren = (GenericSliceRenderer *) ui->sliceView->GetRenderer(); // Configure the renderers GenericSliceRenderer::RendererDelegateList &ovTiled = ren->GetTiledOverlays(); GenericSliceRenderer::RendererDelegateList &ovGlobal = ren->GetGlobalOverlays(); // Append the overlays in the right order ovTiled.clear(); ovTiled.push_back(m_SnakeModeRenderer); ovTiled.push_back(ui->imCrosshairs->GetRenderer()); ovTiled.push_back(ui->imAnnotation->GetRenderer()); ovTiled.push_back(ui->imPolygon->GetRenderer()); ovGlobal.clear(); ovGlobal.push_back(m_DecorationRenderer); switch(m_GlobalUI->GetGlobalState()->GetToolbarMode()) { case POLYGON_DRAWING_MODE: ConfigureEventChain(ui->imPolygon); break; case PAINTBRUSH_MODE: ConfigureEventChain(ui->imPaintbrush); ovTiled.push_back(ui->imPaintbrush->GetRenderer()); break; case ANNOTATION_MODE: ConfigureEventChain(ui->imAnnotation); ovTiled.push_back(ui->imAnnotation->GetRenderer()); break; case ROI_MODE: ConfigureEventChain(ui->imSnakeROI); ovTiled.push_back(ui->imSnakeROI->GetRenderer()); break; case CROSSHAIRS_MODE: ConfigureEventChain(ui->imCrosshairs); break; case NAVIGATION_MODE: ConfigureEventChain(ui->imZoomPan); break; } // Need to change to the appropriate page QStackedLayout *loPages = static_cast(ui->toolbar->layout()); if(m_GlobalUI->GetGlobalState()->GetToolbarMode() == POLYGON_DRAWING_MODE) { switch(m_GlobalUI->GetPolygonDrawingModel(m_Index)->GetState()) { case PolygonDrawingModel::DRAWING_STATE: loPages->setCurrentWidget(ui->pagePolygonDraw); break; case PolygonDrawingModel::EDITING_STATE: loPages->setCurrentWidget(ui->pagePolygonEdit); break; case PolygonDrawingModel::INACTIVE_STATE: loPages->setCurrentWidget(ui->pagePolygonInactive); break; } } else if(m_GlobalUI->GetGlobalState()->GetToolbarMode() == ANNOTATION_MODE) { AnnotationModel *am = m_GlobalUI->GetAnnotationModel(m_Index); if(am->GetFlagDrawingLine()) loPages->setCurrentWidget(ui->pageAnnotateLineActive); else if(am->GetAnnotationMode() == ANNOTATION_SELECT) loPages->setCurrentWidget(ui->pageAnnotateSelect); else loPages->setCurrentWidget(ui->pageDefault); } else { loPages->setCurrentWidget(ui->pageDefault); } } void SliceViewPanel::on_btnZoomToFit_clicked() { m_GlobalUI->GetSliceCoordinator()->ResetViewToFitInOneWindow(m_Index); } void SliceViewPanel::onContextMenu() { if(m_GlobalUI->GetGlobalState()->GetToolbarMode() == POLYGON_DRAWING_MODE) { QMenu *menu = NULL; switch(m_GlobalUI->GetPolygonDrawingModel(m_Index)->GetState()) { case PolygonDrawingModel::DRAWING_STATE: menu = m_MenuPolyDrawing; break; case PolygonDrawingModel::EDITING_STATE: menu = m_MenuPolyEditing; break; case PolygonDrawingModel::INACTIVE_STATE: menu = m_MenuPolyInactive; break; } if(menu) { menu->popup(QCursor::pos()); } } } void SliceViewPanel::SetMouseMotionTracking(bool enable) { ui->sliceView->setMouseTracking(enable); // TODO: in the future, consider using a better cursor for polygon drawing operations /* if(enable) this->setCursor(*m_DrawingCrosshairCursor); else this->setCursor(QCursor(Qt::ArrowCursor)); */ } void SliceViewPanel::on_btnExpand_clicked() { // Get the layout applied when the button is pressed DisplayLayoutModel *dlm = m_GlobalUI->GetDisplayLayoutModel(); DisplayLayoutModel::ViewPanelLayout layout = dlm->GetViewPanelExpandButtonActionModel(m_Index)->GetValue(); // Apply this layout dlm->GetViewPanelLayoutModel()->SetValue(layout); } void SliceViewPanel::UpdateExpandViewButton() { // Get the layout applied when the button is pressed DisplayLayoutModel *dlm = m_GlobalUI->GetDisplayLayoutModel(); DisplayLayoutModel::ViewPanelLayout layout = dlm->GetViewPanelExpandButtonActionModel(m_Index)->GetValue(); // Set the appropriate icon static const char* iconNames[] = { "fourviews", "axial", "coronal", "sagittal", "3d" }; QString iconFile = QString(":/root/dl_%1.png").arg(iconNames[layout]); ui->btnExpand->setIcon(QIcon(iconFile)); // Set the tooltip if(layout == DisplayLayoutModel::VIEW_ALL) { ui->btnExpand->setToolTip("Restore the four-panel display configuration"); } else { ui->btnExpand->setToolTip("Expand this view to occupy the entire window"); } // Also expand the tile/cascade button LayerLayout ll = dlm->GetSliceViewLayerLayoutModel()->GetValue(); if(ll == LAYOUT_TILED) { ui->btnToggleLayout->setIcon(QIcon(":/root/layout_thumb_16.png")); } else if(ll == LAYOUT_STACKED) { ui->btnToggleLayout->setIcon(QIcon(":/root/layout_tile_16.png")); } } void SliceViewPanel::on_btnScreenshot_clicked() { MainImageWindow *parent = findParentWidget(this); parent->ExportScreenshot(m_Index); } void SliceViewPanel::on_btnToggleLayout_clicked() { m_GlobalUI->GetDisplayLayoutModel()->ToggleSliceViewLayerLayout(); } void SliceViewPanel::on_actionZoom_In_triggered() { // Zoom in m_GlobalUI->GetSliceCoordinator()->ZoomInOrOutInOneWindow(m_Index, 1.1); } void SliceViewPanel::on_actionZoom_Out_triggered() { // Zoom out m_GlobalUI->GetSliceCoordinator()->ZoomInOrOutInOneWindow(m_Index, 1.0 / 1.1); } void SliceViewPanel::OnHoveredLayerChange(const EventBucket &eb) { // Determine the position where to draw the button const SliceViewportLayout::SubViewport *vp = m_SliceModel->GetHoveredViewport(); if(vp) { // Set up the location of the button int vppr = m_SliceModel->GetSizeReporter()->GetViewportPixelRatio(); int x = (vp->pos[0] + vp->size[0]) / vppr - m_ContextToolButton->width(); int y = (ui->sliceView->height() - 1) - (vp->pos[1] + vp->size[1]) / vppr; m_ContextToolButton->setVisible(true); m_ContextToolButton->move(x, y); // Set up the context menu on the button MainImageWindow *winmain = findParentWidget(this); LayerInspectorDialog *inspector = winmain->GetLayerInspector(); // Get the corresponding context menu QMenu *menu = inspector->GetLayerContextMenu( m_SliceModel->GetDriver()->GetCurrentImageData()->FindLayer( m_SliceModel->GetHoveredImageLayerId(), false, ALL_ROLES)); // Show the menu m_ContextToolButton->setMenu(menu); m_ContextToolButton->setDown(false); } else { m_ContextToolButton->setVisible(false); m_ContextToolButton->setMenu(NULL); m_ContextToolButton->setDown(false); } } void SliceViewPanel::on_actionAnnotationAcceptLine_triggered() { m_GlobalUI->GetAnnotationModel(m_Index)->AcceptLine(); } void SliceViewPanel::on_actionAnnotationClearLine_triggered() { m_GlobalUI->GetAnnotationModel(m_Index)->CancelLine(); } void SliceViewPanel::on_actionAnnotationSelectAll_triggered() { m_GlobalUI->GetAnnotationModel(m_Index)->SelectAllOnSlice(); } void SliceViewPanel::on_actionAnnotationDelete_triggered() { m_GlobalUI->GetAnnotationModel(m_Index)->DeleteSelectedOnSlice(); } void SliceViewPanel::on_actionAnnotationNext_triggered() { m_GlobalUI->GetAnnotationModel(m_Index)->GoToNextAnnotation(); } void SliceViewPanel::on_actionAnnotationPrevious_triggered() { m_GlobalUI->GetAnnotationModel(m_Index)->GoToPreviousAnnotation(); } itksnap-3.4.0/GUI/Qt/Components/SliceViewPanel.h000066400000000000000000000055241263013355200213550ustar00rootroot00000000000000#ifndef SLICEVIEWPANEL_H #define SLICEVIEWPANEL_H #include #include class GenericSliceView; class QMenu; class QtInteractionDelegateWidget; class SnakeModeRenderer; class SliceWindowDecorationRenderer; class GenericSliceModel; class QCursor; class QToolButton; namespace Ui { class SliceViewPanel; } class SliceViewPanel : public SNAPComponent { Q_OBJECT public: explicit SliceViewPanel(QWidget *parent = 0); ~SliceViewPanel(); // Register the data model with this widget void Initialize(GlobalUIModel *model, unsigned int index); // Get the index of this panel irisGetMacro(Index, unsigned int) GenericSliceView *GetSliceView(); // Callback for when the toolbar changes void SetMouseMotionTracking(bool enable); private slots: void on_inSlicePosition_valueChanged(int value); void on_btnZoomToFit_clicked(); void onModelUpdate(const EventBucket &eb); void OnToolbarModeChange(); void onContextMenu(); void on_btnExpand_clicked(); void on_btnScreenshot_clicked(); void on_btnToggleLayout_clicked(); void on_actionZoom_In_triggered(); void on_actionZoom_Out_triggered(); void OnHoveredLayerChange(const EventBucket &eb); void on_actionAnnotationAcceptLine_triggered(); void on_actionAnnotationClearLine_triggered(); void on_actionAnnotationSelectAll_triggered(); void on_actionAnnotationDelete_triggered(); void on_actionAnnotationNext_triggered(); void on_actionAnnotationPrevious_triggered(); private: Ui::SliceViewPanel *ui; // Popup menus used for polygon operations QMenu *m_MenuPolyInactive, *m_MenuPolyEditing, *m_MenuPolyDrawing; // Current event filter on the crosshair widget QWidget *m_CurrentEventFilter; // Global UI pointer GlobalUIModel *m_GlobalUI; // Slice model GenericSliceModel *m_SliceModel; // Custom cursor for drawing operations QCursor *m_DrawingCrosshairCursor; // Context menu tool button QToolButton *m_ContextToolButton; // Some renderers don't require a separate widget (no user interaction) // and so they are owned by this panel. SmartPtr m_SnakeModeRenderer; SmartPtr m_DecorationRenderer; // Index of the panel unsigned int m_Index; void SetActiveMode(QWidget *mode, bool clearChildren = true); /** The common setup is to have an event filter chain widget -- crosshair -- active_mode -- thumbnail In other words, all events first go to the thumbnail, then to the active mode, then to the crosshair mode */ void ConfigureEventChain(QWidget *w); /** * Listen to mouse enter/exit events in order to show and hide toolbars */ void enterEvent(QEvent *); void leaveEvent(QEvent *); /** Update the expand view / contract view button based on the state */ void UpdateExpandViewButton(); }; #endif // SLICEVIEWPANEL_H itksnap-3.4.0/GUI/Qt/Components/SliceViewPanel.ui000066400000000000000000001310341263013355200215370ustar00rootroot00000000000000 SliceViewPanel 0 0 542 382 Form * { font-size:11px; } 0 0 0 0 2 2 0 0 0 0 20 20 ... :/root/dl_axial.png true 20 20 <html><head/><body><p>Toggle between thumbnail and tiled layouts.</p><p>In <span style=" font-weight:600;">thumbnail</span> layout, one image layer occupies most of the available screen space, and other loaded image layers are shown as thumbnails. In <span style=" font-weight:600;">tiled</span> layout, multiple image layers are shown side by side. </p></body></html> ... :/root/layout_thumb_16.png :/root/layout_tile_16.png:/root/layout_thumb_16.png false false true 20 20 <html><head/><body><p><span style=" font-size:11pt;">Save a screenshot of the current slice view.</span></p></body></html> ... :/root/icons8_slr_camera_12.png:/root/icons8_slr_camera_12.png 12 12 true true Slice selection Qt::Vertical 4 10 0 10 0 0 0 0 0 4 0 0 Qt::Horizontal 40 20 16777215 20 -1 zoom to fit 4 0 0 Qt::Horizontal 40 20 64 0 16777215 20 -1 complete Qt::ToolButtonTextOnly 96 0 16777215 20 -1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Helvetica'; font-size:11px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Deletes the last point added to the polygon</p></body></html> undo last point Qt::ToolButtonTextOnly 16777215 20 -1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Helvetica'; font-size:11px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Clears the current polygon so you can start drawing from scratch</p></body></html> clear Qt::ToolButtonTextOnly 4 0 0 Qt::Horizontal 40 20 64 0 16777215 20 -1 accept Qt::ToolButtonTextOnly 46 0 16777215 20 -1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Helvetica'; font-size:11px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Deletes the selected vertices in the polygon</p></body></html> delete Qt::ToolButtonTextOnly 46 0 16777215 20 -1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Helvetica'; font-size:11px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Splits each selected edge in the polygon by inserting a new vertex in the middle</p></body></html> split Qt::ToolButtonTextOnly 16777215 20 -1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Helvetica'; font-size:11px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Deletes the entire polygon, so you can draw a new one from scratch</p></body></html> clear Qt::ToolButtonTextOnly 4 0 0 Qt::Horizontal 313 17 64 0 16777215 20 -1 accept Qt::ToolButtonTextOnly 16777215 20 -1 clear Qt::ToolButtonTextOnly 4 0 0 Qt::Horizontal 313 17 16777215 20 -1 previous Qt::ToolButtonTextOnly 16777215 20 -1 next Qt::ToolButtonTextOnly Qt::Horizontal QSizePolicy::Fixed 10 20 16777215 20 -1 select all Qt::ToolButtonTextOnly 64 0 16777215 20 -1 delete Qt::ToolButtonTextOnly 4 0 0 Qt::Horizontal 40 20 16777215 20 -1 <html><head/><body><p><span style=" font-family:'Helvetica'; font-size:11px;">Pastes the last polygon that you have accepted in this slice view. You can then move the vertices in the pasted polygon to fit image data. This can be more efficient than drawing a new polygon when performing segmentation across multiple slices.</span></p></body></html> paste Qt::ToolButtonTextOnly 64 0 -1 font-size:11px; TextLabel Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 1 40 40 :/root/popup_ok_16.png:/root/popup_ok_16.png Accept Polygon accept Incorporates the polygon into the segmentation. Return :/root/popup_paste_16.png:/root/popup_paste_16.png Paste Last Polygon paste last polygon Pastes the last polygon that you have accepted in this slice view. You can then move the vertices in the pasted polygon to fit image data. This can be more efficient than drawing a new polygon when performing segmentation across multiple slices. Ctrl+V :/root/popup_edit_16.png:/root/popup_edit_16.png Complete Polygon and Edit complete Finishes drawing the polygon by connecting the last point to the first. The window will enter "editing" mode, where you can modify the polygon. Return :/root/popup_undo_16.png:/root/popup_undo_16.png Undo Last Point undo last point Deletes the last point added to the polygon Backspace :/root/popup_clear_16.png:/root/popup_clear_16.png Clear Drawing clear Clears the current polygon so you can start drawing from scratch Esc :/root/popup_delete_16.png:/root/popup_delete_16.png Trim Selected Vertices trim Deletes the selected vertices in the polygon - :/root/popup_split_16.png:/root/popup_split_16.png Split Selected Edges split Splits each selected edge in the polygon by inserting a new vertex in the middle + :/root/popup_clear_16.png:/root/popup_clear_16.png Clear Polygon clear Deletes the entire polygon, so you can draw a new one from scratch Esc :/root/popup_ok_16.png:/root/popup_ok_16.png Complete Polygon and Accept Finishes drawing the polygon by connecting the last point to the first, then incorporates the polygon into the segmentation. This bypasses the editing mode. Ctrl+Return :/root/icons8_zoomin_16.png:/root/icons8_zoomin_16.png Zoom In Ctrl+Up Qt::WidgetWithChildrenShortcut :/root/icons8_zoomout_16.png:/root/icons8_zoomout_16.png Zoom Out Zoom Out Ctrl+Down Qt::WidgetWithChildrenShortcut :/root/popup_ok_16.png:/root/popup_ok_16.png Accept Polygon accept <html><head/><body><p>Add the current line measurement to the annotation.</p></body></html> Return :/root/popup_clear_16.png:/root/popup_clear_16.png Clear measurement clear <html><head/><body><p>Cancels the current measurement</p></body></html> Esc :/root/icons8_cursor_16.png:/root/icons8_cursor_16.png Select all annotations select all Select all annotations on the current slice Ctrl+A :/root/delete_22.png:/root/delete_22.png Delete selected annotations delete Delete selected annotations on this slice Backspace :/root/arrow_down_16.png:/root/arrow_down_16.png Next annotation next <html><head/><body><p>Select the next annotation. Use this to cycle through the annotations in this slice view in forward order.</p></body></html> :/root/arrow_up_16.png:/root/arrow_up_16.png Previous annotation previous <html><head/><body><p>Select the previous annotation. Use this to cycle through the annotations in this slice view in reverse order.</p></body></html> GenericSliceView QWidget
GenericSliceView.h
1
CrosshairsInteractionMode QWidget
CrosshairsInteractionMode.h
1
ThumbnailInteractionMode QWidget
ThumbnailInteractionMode.h
1
PolygonDrawingInteractionMode QWidget
PolygonDrawingInteractionMode.h
1 onAcceptPolygon() onPastePolygon() onSplitSelected() onDeleteSelected() onClearPolygon() onCloseLoopAndEdit() onUndoLastPoint() onCancelDrawing() onCloseLoopAndAccept()
SnakeROIInteractionMode QWidget
SnakeROIInteractionMode.h
1
PaintbrushInteractionMode QWidget
PaintbrushInteractionMode.h
1
AnnotationInteractionMode QWidget
AnnotationInteractionMode.h
1
actionComplete triggered() imPolygon onCloseLoopAndEdit() -1 -1 440 168 actionUndo triggered() imPolygon onUndoLastPoint() -1 -1 466 195 actionClearDrawing triggered() imPolygon onCancelDrawing() -1 -1 486 211 actionDeleteSelected triggered() imPolygon onDeleteSelected() -1 -1 407 188 actionSplitSelected triggered() imPolygon onSplitSelected() -1 -1 499 179 actionClearPolygon triggered() imPolygon onClearPolygon() -1 -1 470 188 actionPaste triggered() imPolygon onPastePolygon() -1 -1 426 204 actionAccept triggered() imPolygon onAcceptPolygon() -1 -1 452 132 actionCompleteAndAccept triggered() imPolygon onCloseLoopAndAccept() -1 -1 452 132
itksnap-3.4.0/GUI/Qt/Components/SnakeToolROIPanel.cxx000066400000000000000000000037011263013355200223020ustar00rootroot00000000000000#include "SnakeToolROIPanel.h" #include "ui_SnakeToolROIPanel.h" #include #include #include #include #include #include #include #include "SnakeROIResampleModel.h" #include "GlobalState.h" #include "ResampleDialog.h" SnakeToolROIPanel::SnakeToolROIPanel(QWidget *parent) : SNAPComponent(parent), ui(new Ui::SnakeToolROIPanel) { ui->setupUi(this); m_ResampleDialog = new ResampleDialog(this); } SnakeToolROIPanel::~SnakeToolROIPanel() { delete ui; } void SnakeToolROIPanel::SetModel(GlobalUIModel *model) { // Store the model m_Model = model; // Pass the model to the resample dialog m_ResampleDialog->SetModel(model->GetSnakeROIResampleModel()); // Hook up the couplings for the ROI size controls makeArrayCoupling( ui->inIndexX, ui->inIndexY, ui->inIndexZ, model->GetSnakeROIIndexModel()); makeArrayCoupling( ui->inSizeX, ui->inSizeY, ui->inSizeZ, model->GetSnakeROISizeModel()); // Hook up the segmentation seeding option makeCoupling(ui->chkCarryLabels, model->GetSnakeROISeedWithCurrentSegmentationModel()); } void SnakeToolROIPanel::on_btnResetROI_clicked() { // Reset the ROI m_Model->GetSnakeROIModel(0)->ResetROI(); } void SnakeToolROIPanel::on_btnAuto_clicked() { // TODO: Check that the label configuration is valid // Handle resampling if requested if(ui->chkResample->isChecked()) { if(m_ResampleDialog->exec() != QDialog::Accepted) return; } else { // Make sure that no interpolation is applied m_Model->GetSnakeROIResampleModel()->Reset(); m_Model->GetSnakeROIResampleModel()->Accept(); } // Switch to crosshairs mode m_Model->GetGlobalState()->SetToolbarMode(CROSSHAIRS_MODE); // Show the snake panel MainImageWindow *main = findParentWidget(this); main->OpenSnakeWizard(); } itksnap-3.4.0/GUI/Qt/Components/SnakeToolROIPanel.h000066400000000000000000000010661263013355200217310ustar00rootroot00000000000000#ifndef SNAKETOOLROIPANEL_H #define SNAKETOOLROIPANEL_H #include class GlobalUIModel; class ResampleDialog; namespace Ui { class SnakeToolROIPanel; } class SnakeToolROIPanel : public SNAPComponent { Q_OBJECT public: explicit SnakeToolROIPanel(QWidget *parent = 0); ~SnakeToolROIPanel(); void SetModel(GlobalUIModel *); private slots: void on_btnResetROI_clicked(); void on_btnAuto_clicked(); private: Ui::SnakeToolROIPanel *ui; GlobalUIModel *m_Model; ResampleDialog *m_ResampleDialog; }; #endif // SNAKETOOLROIPANEL_H itksnap-3.4.0/GUI/Qt/Components/SnakeToolROIPanel.ui000066400000000000000000000300561263013355200221200ustar00rootroot00000000000000 SnakeToolROIPanel 0 0 172 439 Form * { font-size: 12px; } QSpinBox { font-size: 11px; } 6 0 0 0 0 -1 ROI for auto-segmentation: 12 0 0 0 3 2 64 16777215 <html><head/><body><p>The position of the 3D region of interest (ROI) for automatic segmentation.</p></body></html> QAbstractSpinBox::NoButtons 64 16777215 <html><head/><body><p>The position of the 3D region of interest (ROI) for automatic segmentation.</p></body></html> QAbstractSpinBox::NoButtons -1 Position (x,y,z): 64 16777215 <html><head/><body><p>The position of the 3D region of interest (ROI) for automatic segmentation.</p></body></html> QAbstractSpinBox::NoButtons 12 0 0 0 2 -1 Size (x,y,z): 64 16777215 <html><head/><body><p>The size of the 3D region of interest (ROI) for automatic segmentation.</p></body></html> QAbstractSpinBox::NoButtons 64 16777215 <html><head/><body><p>The size of the 3D region of interest (ROI) for automatic segmentation.</p></body></html> QAbstractSpinBox::NoButtons 64 16777215 <html><head/><body><p>The size of the 3D region of interest (ROI) for automatic segmentation.</p></body></html> QAbstractSpinBox::NoButtons 0 0 0 0 0 0 0 120 0 -1 <html><head/><body><p>Reset the region of interest to be the whole image.</p></body></html> Reset ROI Qt::Horizontal font-size:12px; 12 0 0 0 <html><head/><body><p><span style=" font-size:12pt;">When checked, you will have an option to change the resolution of the region of interest (i.e., subsample or supersample) before running automatic segmentation.</span></p></body></html> Resample ROI <html><head/><body><p>When checked, the existing segmentation will be carried over to the automatic segmentation mode and used to initialize the active contour. When unchecked, the active contour will be initialized with bubbles.</p></body></html> Initialize with current segmentation Qt::Horizontal Qt::Vertical 20 1 0 0 0 0 0 0 0 120 0 -1 <html><head/><body><p>Press this button to start semi-automatic segmentation</p></body></html> Segment 3D Qt::Vertical 20 1 itksnap-3.4.0/GUI/Qt/Components/SnakeWizardPanel.cxx000066400000000000000000000365561263013355200222710ustar00rootroot00000000000000#include "SnakeWizardPanel.h" #include "ui_SnakeWizardPanel.h" #include "SpeedImageDialog.h" #include "SnakeParameterDialog.h" #include "GlobalUIModel.h" #include "SnakeWizardModel.h" #include "QtCursorOverride.h" #include "QtComboBoxCoupling.h" #include "QtWidgetActivator.h" #include "QtDoubleSliderWithEditorCoupling.h" #include #include #include "QtSpinBoxCoupling.h" #include "QtDoubleSpinBoxCoupling.h" #include "QtSliderCoupling.h" #include "QtRadioButtonCoupling.h" #include "ColorLabelQuickListWidget.h" #include "IRISException.h" #include #include #include #include Q_DECLARE_METATYPE(PreprocessingMode) /** * An item model for editing bubble data */ void BubbleItemModel::setSourceModel(SnakeWizardModel *model) { m_Model = model; LatentITKEventNotifier::connect( model, SnakeWizardModel::BubbleListUpdateEvent(), this, SLOT(onBubbleListUpdate())); LatentITKEventNotifier::connect( model, SnakeWizardModel::BubbleDefaultRadiusUpdateEvent(), this, SLOT(onBubbleValuesUpdate())); } int BubbleItemModel::rowCount(const QModelIndex &parent) const { // Only top-level items exist if(parent.isValid()) { return 0; } else { std::vector &ba = m_Model->GetParent()->GetDriver()->GetBubbleArray(); return ba.size(); } } int BubbleItemModel::columnCount(const QModelIndex &parent) const { return 4; } QVariant BubbleItemModel::data(const QModelIndex &index, int role) const { std::vector &ba = m_Model->GetParent()->GetDriver()->GetBubbleArray(); Bubble &b = ba[index.row()]; if(role == Qt::EditRole || role == Qt::DisplayRole) { if(index.column()==3) return QString("%1").arg(b.radius); else return QString("%1").arg(b.center[index.column()]+1); } else if(role == Qt::UserRole) { // This is so that the selection model coupling works return index.row(); } return QVariant(); } bool BubbleItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { std::vector &ba = m_Model->GetParent()->GetDriver()->GetBubbleArray(); Bubble b = ba[index.row()]; bool modified = false; if(index.column()==3) { bool convok; double dv = value.toDouble(&convok); if(convok && dv > 0 && b.radius != dv) { b.radius = dv; modified = true; } } else { bool convok; int iv = value.toInt(&convok); if(convok && b.center[index.column()]+1 != iv) { b.center[index.column()] = iv-1; modified = true; } } if(modified) return m_Model->UpdateBubble(index.row(), b); return false; } Qt::ItemFlags BubbleItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; return flags; } QVariant BubbleItemModel::headerData( int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch(section) { case 0 : return tr("X"); case 1 : return tr("Y"); case 2 : return tr("Z"); case 3 : return tr("Radius"); } } return QVariant(); } void BubbleItemModel::onBubbleListUpdate() { emit layoutChanged(); } void BubbleItemModel::onBubbleValuesUpdate() { emit dataChanged(this->index(0,0), this->index(this->rowCount(QModelIndex())-1,3)); } SnakeWizardPanel::SnakeWizardPanel(QWidget *parent) : SNAPComponent(parent), ui(new Ui::SnakeWizardPanel) { ui->setupUi(this); m_SpeedDialog = new SpeedImageDialog(this); m_ParameterDialog = new SnakeParameterDialog(this); // Hook up the timer! m_EvolutionTimer = new QTimer(this); connect(m_EvolutionTimer, SIGNAL(timeout()), this, SLOT(idleCallback())); // Hook up the quick label selector connect(ui->boxLabelQuickList, SIGNAL(actionTriggered(QAction *)), this, SLOT(onClassifyQuickLabelSelection())); // Adjust the shortcuts for increase/decrease behavior ui->actionIncreaseBubbleRadius->setShortcuts( ui->actionIncreaseBubbleRadius->shortcuts() << QKeySequence('=')); ui->actionDecreaseBubbleRadius->setShortcuts( ui->actionDecreaseBubbleRadius->shortcuts() << QKeySequence('_')); this->addAction(ui->actionIncreaseBubbleRadius); this->addAction(ui->actionDecreaseBubbleRadius); // Create model for the classification foreground list view QStandardItemModel *classify_fg_model = new QStandardItemModel(this); classify_fg_model->setColumnCount(1); ui->lstClassifyForeground->setModel(classify_fg_model); } SnakeWizardPanel::~SnakeWizardPanel() { delete ui; } #include "QtToolbarCoupling.h" void SnakeWizardPanel::SetModel(GlobalUIModel *model) { // Store and pass on the models m_ParentModel = model; m_Model = model->GetSnakeWizardModel(); m_SpeedDialog->SetModel(m_Model); m_ParameterDialog->SetModel(model->GetSnakeParameterModel()); // Couple widgets to models makeCoupling(ui->inBubbleRadius, m_Model->GetBubbleRadiusModel()); // Couple the preprocessing mode combo box makeCoupling(ui->inPreprocessMode, m_Model->GetPreprocessingModeModel()); // Couple the preprocessing mode stack std::map preproc_page_map; preproc_page_map[PREPROCESS_THRESHOLD] = ui->pgThreshold; preproc_page_map[PREPROCESS_EDGE] = ui->pgEdge; preproc_page_map[PREPROCESS_GMM] = ui->pgCluster; preproc_page_map[PREPROCESS_RF] = ui->pgClassify; preproc_page_map[PREPROCESS_NONE] = ui->pgUserSpeed; makePagedWidgetCoupling(ui->stackPreprocess, m_Model->GetPreprocessingModeModel(), preproc_page_map); // Couple the thresholding controls makeCoupling(ui->inThreshLowerSlider, m_Model->GetThresholdLowerModel()); makeCoupling(ui->inThreshLowerSpin, m_Model->GetThresholdLowerModel()); makeCoupling(ui->inThreshUpperSlider, m_Model->GetThresholdUpperModel()); makeCoupling(ui->inThreshUpperSpin, m_Model->GetThresholdUpperModel()); makeRadioGroupCoupling(ui->grpThreshMode, m_Model->GetThresholdModeModel()); // Activation of thresholding controls activateOnFlag(ui->inThreshLowerSlider, m_Model, SnakeWizardModel::UIF_LOWER_THRESHOLD_ENABLED); activateOnFlag(ui->inThreshLowerSpin, m_Model, SnakeWizardModel::UIF_LOWER_THRESHOLD_ENABLED); activateOnFlag(ui->inThreshUpperSlider, m_Model, SnakeWizardModel::UIF_UPPER_THRESHOLD_ENABLED); activateOnFlag(ui->inThreshUpperSpin, m_Model, SnakeWizardModel::UIF_UPPER_THRESHOLD_ENABLED); // Couple the clustering controls makeCoupling(ui->inClusterCount, m_Model->GetNumberOfClustersModel()); makeCoupling(ui->inClusterActive, m_Model->GetForegroundClusterModel()); // Set up activation on classification controls activateOnFlag(ui->lstClassifyForeground, m_Model, SnakeWizardModel::UIF_CLASSIFIER_TRAINED); // Make the coupling for foreground label list (some complicated template magic here) typedef DefaultWidgetValueTraits< SnakeWizardModel::ClassifierLabelForegroundMap, QAbstractItemView> ClassifyForegroundValueTraits; typedef QStandardItemModelWidgetDomainTraits< SnakeWizardModel::ClassifierLabelForegroundMapDomain, SingleColumnColorLabelToQSIMCouplingRowTraits> ClassifyForegroundDomainTraits; makeCoupling((QAbstractItemView *) ui->lstClassifyForeground, m_Model->GetClassifierLabelForegroundModel(), ClassifyForegroundValueTraits(), ClassifyForegroundDomainTraits()); // Couple the edge preprocessing controls makeCoupling(ui->inEdgeScale, m_Model->GetEdgePreprocessingSigmaModel()); makeCoupling(ui->inEdgeScaleSlider, m_Model->GetEdgePreprocessingSigmaModel()); // Initialize the label quick list ui->boxLabelQuickList->SetModel(m_Model->GetParent()); // Set up a model for the bubbles table BubbleItemModel *biModel = new BubbleItemModel(this); biModel->setSourceModel(m_Model); ui->tableBubbleList->setModel(biModel); makeCoupling((QAbstractItemView *) ui->tableBubbleList, m_Model->GetActiveBubbleModel()); makeCoupling(ui->inStepSize, m_Model->GetStepSizeModel()); makeCoupling(ui->outIteration, m_Model->GetEvolutionIterationModel()); // Activation flags /* activateOnNotFlag(ui->pgPreproc, m_Model, SnakeWizardModel::UIF_PREPROCESSING_ACTIVE); */ activateOnFlag(ui->btnNextPreproc, m_Model, SnakeWizardModel::UIF_CAN_GENERATE_SPEED); activateOnFlag(ui->btnRemoveBubble, m_Model, SnakeWizardModel::UIF_BUBBLE_SELECTED); activateOnFlag(ui->btnBubbleNext, m_Model, SnakeWizardModel::UIF_INITIALIZATION_VALID); } void SnakeWizardPanel::Initialize() { // Initialize the model m_Model->OnSnakeModeEnter(); // Go to the right page ui->stack->setCurrentWidget(ui->pgPreproc); } void SnakeWizardPanel::on_btnNextPreproc_clicked() { // Compute the speed image m_Model->ApplyPreprocessing(); // Finish preprocessing m_Model->CompletePreprocessing(); // Initialize the model m_Model->OnBubbleModeEnter(); // Move to the bubble page ui->stack->setCurrentWidget(ui->pgBubbles); } void SnakeWizardPanel::on_btnAddBubble_clicked() { m_Model->AddBubbleAtCursor(); } void SnakeWizardPanel::on_btnRemoveBubble_clicked() { m_Model->RemoveBubbleAtCursor(); } void SnakeWizardPanel::on_btnBubbleNext_clicked() { // Call the initialization code try { // Handle cursor QtCursorOverride curse(Qt::WaitCursor); // Initialize the evolution layers m_Model->OnEvolutionPageEnter(); // Move to the evolution page ui->stack->setCurrentWidget(ui->pgEvolution); } catch(IRISException &exc) { QMessageBox::warning(this, "ITK-SNAP", exc.what(), QMessageBox::Ok); } } void SnakeWizardPanel::on_btnBubbleBack_clicked() { // Move to the preprocessing page ui->stack->setCurrentWidget(ui->pgPreproc); // Tell the model m_Model->OnBubbleModeBack(); } void SnakeWizardPanel::on_stack_currentChanged(int page) { // The stack at the top follows the stack at the bottom ui->stackStepInfo->setCurrentIndex(page); } void SnakeWizardPanel::on_btnPlay_toggled(bool checked) { // This is where we toggle the snake evolution! if(checked) m_EvolutionTimer->start(10); else m_EvolutionTimer->stop(); } void SnakeWizardPanel::idleCallback() { // Step the snake. If converged (returns true), stop playing if(m_Model->PerformEvolutionStep()) ui->btnPlay->setChecked(false); } void SnakeWizardPanel::on_btnSingleStep_clicked() { // Turn off the play button (will turn off the timer too) ui->btnPlay->setChecked(false); // Perform a single step m_Model->PerformEvolutionStep(); } void SnakeWizardPanel::on_btnEvolutionBack_clicked() { // Turn off the play button (will turn off the timer too) ui->btnPlay->setChecked(false); // Tell the model to return to initialization state m_Model->OnEvolutionPageBack(); // Flip to previous page ui->stack->setCurrentWidget(ui->pgBubbles); } void SnakeWizardPanel::on_btnEvolutionNext_clicked() { // Turn off the play button (will turn off the timer too) ui->btnPlay->setChecked(false); // Tell the model to return to initialization state m_Model->OnEvolutionPageFinish(); // Tell parent to hide this window emit wizardFinished(); } void SnakeWizardPanel::on_btnRewind_clicked() { // Turn off the play button (will turn off the timer too) ui->btnPlay->setChecked(false); // Tell the model to return to initialization state m_Model->RewindEvolution(); } void SnakeWizardPanel::on_btnEvolutionParameters_clicked() { m_ParameterDialog->show(); m_ParameterDialog->activateWindow(); m_ParameterDialog->raise(); } void SnakeWizardPanel::on_btnCancel_clicked() { // Turn off the play button (will turn off the timer too) ui->btnPlay->setChecked(false); // Make sure all dialogs are closed m_SpeedDialog->close(); m_ParameterDialog->close(); // Tell the model to return to initialization state m_Model->OnCancelSegmentation(); // Tell parent to hide this window emit wizardFinished(); } void SnakeWizardPanel::on_actionIncreaseBubbleRadius_triggered() { ui->inBubbleRadius->stepUp(); } void SnakeWizardPanel::on_actionDecreaseBubbleRadius_triggered() { ui->inBubbleRadius->stepDown(); } void SnakeWizardPanel::on_btnClusterIterate_clicked() { m_Model->PerformClusteringIteration(); } void SnakeWizardPanel::on_btnClusterIterateMany_clicked() { for(int i = 0; i < 10; i++) m_Model->PerformClusteringIteration(); } void SnakeWizardPanel::on_btnClusterReinitialize_clicked() { m_Model->ReinitializeClustering(); } void SnakeWizardPanel::on_btnClassifyTrain_clicked() { try { QtCursorOverride cursy(Qt::WaitCursor); m_Model->TrainClassifier(); } catch (IRISException &exc) { ReportNonLethalException(this, exc, "Classification Failed"); } } void SnakeWizardPanel::on_btnThreshDetail_clicked() { m_SpeedDialog->ShowDialog(); } void SnakeWizardPanel::on_btnClusterDetail_clicked() { m_SpeedDialog->ShowDialog(); } void SnakeWizardPanel::on_btnClassifyClearExamples_clicked() { m_Model->ClearSegmentation(); } void SnakeWizardPanel::onClassifyQuickLabelSelection() { // Enter paintbrush mode - to help the user m_Model->GetParent()->GetGlobalState()->SetToolbarMode(PAINTBRUSH_MODE); } /* void SnakeWizardPanel::UpdateClassifierForegroundTable() { m_IsClassifierForegroundTableChanging = true; ui->lstClassifyForeground->clear(); if(m_Model->IsClassifierTrained()) { SnakeWizardModel::ClassifierFBTable fbtab; m_Model->GetClassifierForegroundBackgroundTable(fbtab); if(m_ClassifierForegroundTableCached == fbtab) return; else m_ClassifierForegroundTableCached = fbtab; for(SnakeWizardModel::ClassifierFBTable::iterator it = fbtab.begin(); it != fbtab.end(); ++it) { QListWidgetItem *item = new QListWidgetItem(ui->lstClassifyForeground); item->setText(from_utf8(it->second.title)); item->setIcon(CreateColorBoxIcon(16, 16, QColor(255 * it->second.color[0], 255 * it->second.color[1], 255 * it->second.color[2]))); item->setData(Qt::UserRole, it->first); item->setSelected(it->second.weight > 0.0); } } else { m_ClassifierForegroundTableCached.clear(); } m_IsClassifierForegroundTableChanging = false; } void SnakeWizardPanel::on_lstClassifyForeground_itemSelectionChanged() { if(m_IsClassifierForegroundTableChanging) return; if(m_Model->IsClassifierTrained()) { SnakeWizardModel::ClassifierFBTable fbtab; for(int i = 0; i < ui->lstClassifyForeground->count(); i++) { QListWidgetItem *item = ui->lstClassifyForeground->item(i); LabelType label = item->data(Qt::UserRole).value(); bool is_selected = item->isSelected(); fbtab[label].weight = is_selected ? 1.0 : -1.0; } m_Model->SetClassifierForegroundBackgroundTable(fbtab); } } void SnakeWizardPanel::onModelUpdate(const EventBucket &bucket) { if(bucket.HasEvent(SnakeWizardModel::RFClassifierModifiedEvent()) || bucket.HasEvent(SegmentationLabelChangeEvent())) { this->UpdateClassifierForegroundTable(); } } */ void SnakeWizardPanel::on_btnEdgeDetail_clicked() { m_SpeedDialog->ShowDialog(); } void SnakeWizardPanel::on_btnClassifyDetail_clicked() { m_SpeedDialog->ShowDialog(); } itksnap-3.4.0/GUI/Qt/Components/SnakeWizardPanel.h000066400000000000000000000055151263013355200217050ustar00rootroot00000000000000#ifndef SNAKEWIZARDPANEL_H #define SNAKEWIZARDPANEL_H #include #include #include #include #include namespace Ui { class SnakeWizardPanel; } class SpeedImageDialog; class GlobalUIModel; class QTimer; class SnakeParameterDialog; class QToolBar; /** * A Qt Model that handles bubble selection */ class BubbleItemModel : public QAbstractTableModel { Q_OBJECT public: BubbleItemModel(QObject *parent) : QAbstractTableModel(parent) {} virtual ~BubbleItemModel() {} void setSourceModel(SnakeWizardModel *model); virtual int rowCount(const QModelIndex &parent) const; virtual int columnCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role); virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; public slots: void onBubbleListUpdate(); void onBubbleValuesUpdate(); private: SnakeWizardModel *m_Model; }; class SnakeWizardPanel : public SNAPComponent { Q_OBJECT public: explicit SnakeWizardPanel(QWidget *parent = 0); ~SnakeWizardPanel(); void SetModel(GlobalUIModel *model); /** Put the panel into the initial state, i.e., ready to perform preprocessing. You must call this method before showing the panel. */ void Initialize(); signals: void wizardFinished(); private slots: void on_btnNextPreproc_clicked(); void on_btnAddBubble_clicked(); void on_btnRemoveBubble_clicked(); void on_btnBubbleNext_clicked(); void on_btnBubbleBack_clicked(); void on_stack_currentChanged(int arg1); void on_btnPlay_toggled(bool checked); void idleCallback(); void on_btnSingleStep_clicked(); void on_btnEvolutionBack_clicked(); void on_btnEvolutionNext_clicked(); void on_btnRewind_clicked(); void on_btnEvolutionParameters_clicked(); void on_btnCancel_clicked(); void on_actionIncreaseBubbleRadius_triggered(); void on_actionDecreaseBubbleRadius_triggered(); void on_btnClusterIterate_clicked(); void on_btnClusterIterateMany_clicked(); void on_btnClusterReinitialize_clicked(); void on_btnClassifyTrain_clicked(); void on_btnThreshDetail_clicked(); void on_btnClusterDetail_clicked(); void on_btnClassifyClearExamples_clicked(); // Slot called when a quick-label is selected in the classify pane void onClassifyQuickLabelSelection(); void on_btnEdgeDetail_clicked(); void on_btnClassifyDetail_clicked(); private: SpeedImageDialog *m_SpeedDialog; SnakeParameterDialog *m_ParameterDialog; GlobalUIModel *m_ParentModel; SnakeWizardModel *m_Model; QTimer *m_EvolutionTimer; Ui::SnakeWizardPanel *ui; }; #endif // SNAKEWIZARDPANEL_H itksnap-3.4.0/GUI/Qt/Components/SnakeWizardPanel.ui000066400000000000000000002440401263013355200220710ustar00rootroot00000000000000 SnakeWizardPanel 0 0 191 654 0 0 191 16777215 Form * { font-size: 12px; } QSpinBox { font-size: 11px; } QGroupBox { background-origin: content; margin-top: 15px; font-weight: bold; font-size: 12px; color: rgb(30,30,160); background-color: rgb(222,222,222); padding: 5px; border-radius: 2px; border: 1px solid rgb(168,168,168); } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; } QToolButton::checked { background-color:rgb(210, 210, 210); } QToolButton::pressed { background-color:rgb(210, 210, 210); } 12 5 5 5 5 0 60 Current Stage: 0 0 0 0 0 0 0 0 0 0 0 16777215 16777215 font-size:10px; <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'.Helvetica Neue DeskInterface'; font-size:10px; font-weight:400; font-style:normal;"> <p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:12px;">Step 1/3</span></p> <p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:12px; font-weight:600;">Presegmentation</span></p></body></html> true 0 0 0 0 16777215 16777215 font-size:10px; <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:10px; font-weight:400; font-style:normal;"> <p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12px;">Step 2/3</span></p> <p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12px; font-weight:600;">Initialization</span></p></body></html> true 0 0 0 0 16777215 16777215 font-size:10px; <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Lucida Grande'; font-size:10px; font-weight:400; font-style:normal;"> <p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12px;">Step 3/3</span></p> <p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12px; font-weight:600;">Evolution</span></p></body></html> true 0 0 Actions: 0 6 0 0 0 0 0 4 0 0 0 0 12 0 0 0 0 Presegmentation mode: true 0 0 150 0 Thresholding Classification Clustering Edge Attraction Qt::Vertical QSizePolicy::Fixed 20 5 border-top: 1px solid rgb(164, 164, 164); border-bottom: 1px solid rgb(164,164, 164); border-left: 0px; border-right: 0px; QFrame::Sunken Qt::Horizontal 12 0 0 0 0 Lower threshold only 2 0 0 0 4 Qt::Vertical QSizePolicy::Fixed 20 10 0 40 4 0 0 0 0 Qt::Horizontal 40 20 28 28 Upper and lower thresholds ... :/root/thresh_both.png:/root/thresh_both.png 22 22 true true 28 28 <html><head/><body><p>Lower threshold only</p></body></html> ... :/root/thresh_lower.png:/root/thresh_lower.png 22 22 true true true 28 28 Upper threshold only ... :/root/thresh_upper.png:/root/thresh_upper.png 22 22 true true Qt::Horizontal Qt::Horizontal false false <html><head/><body><p>Upper<br/>threshold:</p></body></html> 120 0 120 16777215 More ... <html><head/><body><p>Lower<br/>threshold:</p></body></html> Qt::Vertical QSizePolicy::Fixed 20 10 Qt::Vertical 20 40 Threshold mode: 0 0 0 12 4 <html><head/><body><p align="right">Foreground<br/>cluster:</p></body></html> Qt::Vertical 20 40 120 0 120 16777215 More ... <html><head/><body><p align="right">Number of<br/>clusters:</p></body></html> 4 4 4 4 4 Qt::Horizontal 40 20 28 28 <html><head/><body><p>Reinitialize the clusters</p></body></html> ... :/root/view-refresh-4.png:/root/view-refresh-4.png 22 22 28 28 <html><head/><body><p>Perform one iteration of cluster computation</p></body></html> ... :/root/media-playback-start-1x-4.png:/root/media-playback-start-1x-4.png 22 22 28 28 <html><head/><body><p>Perform ten iterations of cluster computation</p></body></html> ... :/root/media-playback-start-10x-4.png:/root/media-playback-start-10x-4.png 22 22 Clustering iteration: Qt::Vertical QSizePolicy::Fixed 20 10 Qt::Vertical QSizePolicy::Fixed 20 10 0 0 0 2 0 0 0 0 0 20 120 0 120 16777215 <html><head/><body><p><span style=" font-size:12pt;">Display additional classification parameters</span></p></body></html> More ... Qt::Vertical QSizePolicy::Fixed 20 10 Foreground class(es): 0 0 140 0 140 16777215 <html><head/><body><p>Clear all the examples of the tissue classes painted in the image. This is useful if you want to retrain your classifier using new examples. It is also a good idea to clear examples before pressing 'Next'.</p></body></html> Clear Examples Qt::Vertical QSizePolicy::Fixed 20 10 0 0 0 68 font-size:10px; color: rgb(64, 64, 64) <html><head/><body><p>Provide examples of two or more tissue classes by selecting the labels below and drawing on the image using the paintbrush tool.</p></body></html> true 0 0 0 56 font-size:10px; color: rgb(64, 64, 64) <html><head/><body><p>Next, train the classifier and select the tissue class that you wish to use for segmentation.</p></body></html> true Qt::Vertical QSizePolicy::Fixed 20 10 Qt::Vertical QSizePolicy::Fixed 20 4 Qt::Vertical QSizePolicy::Expanding 20 10 0 0 140 0 140 16777215 -1 <html><head/><body><p>This button trains the random forest classifier. The classifier assigns a probability value to each voxel of belonging to the 'foreground' class vs. belonging to all other classes. You must train a classifier before proceeding to the next step.</p></body></html> Train Classifier 165 16777215 <html><head/><body><p>Designate one or more classes as the &quot;foreground&quot; class(es). These are the classes that you want to apply segmentation to. The speed image is computed as the sum of probabilities of all the foreground classes, minus the sum of probabilities of the background classes.</p></body></html> QListView:!active { selection-background-color: rgb(162, 204, 255); } QAbstractItemView::NoEditTriggers QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows 0 0 0 0 4 0 0 Import Speed ... Region competition Which version of active contour method is the speed image intended for? true Qt::Horizontal QSizePolicy::Fixed 20 20 Use this mode to perform segmentation using a speed image computed outside of ITK-SNAP. true Edge attraction Qt::Vertical 20 40 Qt::Vertical 20 40 0 0 0 4 Qt::Horizontal <html><head/><body><p>Smoothing<br/>factor:</p></body></html> 120 0 120 16777215 More ... Qt::Vertical 20 196 border-top: 1px solid rgb(164, 164, 164); border-bottom: 1px solid rgb(164,164, 164); border-left: 0px; border-right: 0px; QFrame::Sunken Qt::Horizontal 0 0 0 0 6 4 0 0 0 0 false 0 0 64 16777215 Back 0 0 64 16777215 Next 0 0 0 0 Place bubbles in the image to intialize the contour true 0 0 Add Bubble at Cursor Return 0 0 0 0 0 Bubble radius: 0 0 Qt::Horizontal Active bubbles: 0 0 165 120 -1 font-size: 10px; QAbstractItemView::SingleSelection QAbstractItemView::SelectRows true 32 32 false true false true 18 18 0 0 Delete Active Bubble Backspace QFrame::Plain Qt::Horizontal <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'.Lucida Grande UI'; font-size:12px; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13px;">Press 'Next&quot; to proceed to the next step.</span></p></body></html> true 4 0 0 0 0 0 0 64 16777215 Back 0 0 64 16777215 Next Qt::Vertical 20 0 12 0 0 0 0 0 0 0 0 12 0 0 0 0 16777215 16777215 Configure the parameters of the contour evolution differential equation true 0 0 0 0 0 0 Set Parameters ... QFrame::Sunken Qt::Horizontal 12 0 0 0 0 Execute and control the evolution true 4 4 4 4 4 Qt::Horizontal 5 20 Restart evolution ... :/root/media-seek-backward-4.png 22 22 Run/pause active contour evolution ... :/root/media-playback-start-4.png :/root/media-playback-pause-4.png :/root/media-playback-start-4.png :/root/media-playback-pause-4.png :/root/media-playback-pause-4.png:/root/media-playback-start-4.png 22 22 true Perform a single evolution iteration ... :/root/media-playback-singlestep.png 22 22 Qt::Horizontal 5 20 0 0 QLayout::SetDefaultConstraint 4 4 4 4 12 2 Step size: Iteration: true QAbstractSpinBox::NoButtons 9999 Number of evolution steps per iteration. QFrame::Sunken Qt::Horizontal Press 'Finish' to accept the result true 4 0 0 0 0 0 0 64 16777215 Return to bubble placement Back 0 0 64 16777215 Accept segmentation and return to the main ITK-SNAP window Finish Qt::Vertical 20 0 Qt::Vertical 20 40 Cancel Segmentation Increase bubble radius + Decrease bubble radius Decrease bubble radius - QDoubleSlider QSlider
QDoubleSlider.h
QDoubleSliderWithEditor QSlider
QDoubleSliderWithEditor.h
ColorLabelQuickListWidget QWidget
ColorLabelQuickListWidget.h
1
itksnap-3.4.0/GUI/Qt/Components/SynchronizationInspector.cxx000066400000000000000000000021131263013355200241350ustar00rootroot00000000000000#include "SynchronizationInspector.h" #include "ui_SynchronizationInspector.h" #include "SynchronizationModel.h" #include "QtSpinBoxCoupling.h" #include "QtCheckBoxCoupling.h" SynchronizationInspector::SynchronizationInspector(QWidget *parent) : QWidget(parent), ui(new Ui::SynchronizationInspector) { ui->setupUi(this); // TODO: add the code to support multiple channels ui->panelChannel->setVisible(false); } SynchronizationInspector::~SynchronizationInspector() { delete ui; } void SynchronizationInspector::SetModel(SynchronizationModel *model) { m_Model = model; // Do couplings makeCoupling(ui->chkSync, model->GetSyncEnabledModel()); makeCoupling(ui->chkCursor, model->GetSyncCursorModel()); makeCoupling(ui->chkZoom, model->GetSyncZoomModel()); makeCoupling(ui->chkPan, model->GetSyncPanModel()); makeCoupling(ui->chkCamera, model->GetSyncCameraModel()); // The checkboxes should be deactivated when the sync model is off makeBooleanNamedPropertyCoupling(ui->panelProperties, "enabled", model->GetSyncEnabledModel()); } itksnap-3.4.0/GUI/Qt/Components/SynchronizationInspector.h000066400000000000000000000007651263013355200235750ustar00rootroot00000000000000#ifndef SYNCHRONIZATIONINSPECTOR_H #define SYNCHRONIZATIONINSPECTOR_H #include namespace Ui { class SynchronizationInspector; } class SynchronizationModel; class SynchronizationInspector : public QWidget { Q_OBJECT public: explicit SynchronizationInspector(QWidget *parent = 0); ~SynchronizationInspector(); void SetModel(SynchronizationModel *model); private: Ui::SynchronizationInspector *ui; SynchronizationModel *m_Model; }; #endif // SYNCHRONIZATIONINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/SynchronizationInspector.ui000066400000000000000000000143431263013355200237600ustar00rootroot00000000000000 SynchronizationInspector 0 0 162 349 Form * { font-size: 12px; } QSpinBox { font-size: 11px; } 4 0 0 0 0 <html><head/><body><p><span style=" font-weight:600;">Toggle synchronization</span></p><p><br/></p><p>When enabled, this ITK-SNAP window will synchronize cursor position and other view properties with other ITK-SNAP windows open on the same machine. This is useful for yoking the cursor across two medical images.</p></body></html> Synchronization 0 20 5 -1 font-size:10px; Share state with other ITK-SNAP windows false Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter true Properties to sync: true QCheckBox { padding:0px; } 6 20 0 0 0 <html><head/><body><p>Sync the position of the 3D cursor between ITK-SNAP windows</p></body></html> Cursor position <html><head/><body><p>Sync the zoom level between ITK-SNAP windows. <span style=" font-style:italic;">Linked zoom must be enabled in the zoom inspector.</span></p></body></html> Zoom level <html><head/><body><p>Sync the panning position betweenITK-SNAP windows. <span style=" font-style:italic;">Linked zoom must be enabled in the zoom inspector.</span></p></body></html> Pan <html><head/><body><p>Sync the 3D view camera position between ITK-SNAP windows.</p></body></html> 3D viewpoint Qt::Vertical 20 2 6 20 0 0 0 itksnap-3.4.0/GUI/Qt/Components/ViewPanel3D.cxx000066400000000000000000000173241263013355200211400ustar00rootroot00000000000000#include "ViewPanel3D.h" #include "ui_ViewPanel3D.h" #include "GlobalUIModel.h" #include "Generic3DModel.h" #include "itkCommand.h" #include "IRISException.h" #include #include "MainImageWindow.h" #include "SNAPQtCommon.h" #include "QtWidgetActivator.h" #include "DisplayLayoutModel.h" #include #include #include "itkProcessObject.h" #include ViewPanel3D::ViewPanel3D(QWidget *parent) : SNAPComponent(parent), ui(new Ui::ViewPanel3D) { ui->setupUi(this); // Set up timer for continuous update rendering m_RenderTimer = new QTimer(); m_RenderTimer->setInterval(100); connect(m_RenderTimer, SIGNAL(timeout()), SLOT(onTimer())); // Create a progress command m_RenderProgressCommand = CommandType::New(); m_RenderProgressCommand->SetCallbackFunction(this, &ViewPanel3D::ProgressCallback); // Connect the progress event ui->progressBar->setRange(0, 1000); QObject::connect(this, SIGNAL(renderProgress(int)), ui->progressBar, SLOT(setValue(int)), Qt::DirectConnection); // Set up the context menu m_DropMenu = new QMenu(this); m_DropMenu->setStyleSheet("font-size:11px;"); m_DropMenu->addAction(ui->actionReset_Viewpoint); m_DropMenu->addAction(ui->actionSave_Viewpoint); m_DropMenu->addAction(ui->actionRestore_Viewpoint); m_DropMenu->addSeparator(); m_DropMenu->addAction(ui->actionContinuous_Update); m_DropMenu->addSeparator(); m_DropMenu->addAction(ui->actionClear_Rendering); // Make the actions globally accessible this->addActions(m_DropMenu->actions()); } ViewPanel3D::~ViewPanel3D() { delete ui; } GenericView3D *ViewPanel3D::Get3DView() { return ui->view3d; } void ViewPanel3D::onModelUpdate(const EventBucket &bucket) { GlobalState *gs = m_Model->GetParentUI()->GetGlobalState(); if(bucket.HasEvent(DisplayLayoutModel::ViewPanelLayoutChangeEvent())) { UpdateExpandViewButton(); } if(bucket.HasEvent(ValueChangedEvent(), gs->GetToolbarMode3DModel())) { UpdateActionButtons(); } } void ViewPanel3D::on_btnUpdateMesh_clicked() { try { // Tell the model to update itself m_Model->UpdateSegmentationMesh(m_Model->GetParentUI()->GetProgressCommand()); // m_Model->UpdateSegmentationMesh(m_RenderProgressCommand); } catch(IRISException & IRISexc) { QMessageBox::warning(this, "Problem generating mesh", IRISexc.what()); } // TODO: Delete this later - should be automatic! ui->view3d->repaint(); } void ViewPanel3D::Initialize(GlobalUIModel *globalUI) { // Save the model m_GlobalUI = globalUI; m_Model = globalUI->GetModel3D(); ui->view3d->SetModel(m_Model); // Set activations activateOnFlag(ui->btnAccept, m_Model, Generic3DModel::UIF_MESH_ACTION_PENDING); activateOnFlag(ui->btnCancel, m_Model, Generic3DModel::UIF_MESH_ACTION_PENDING); activateOnFlag(ui->btnFlip, m_Model, Generic3DModel::UIF_FLIP_ENABLED); activateOnFlag(ui->btnUpdateMesh, m_Model, Generic3DModel::UIF_MESH_DIRTY); activateOnFlag(ui->actionRestore_Viewpoint, m_Model, Generic3DModel::UIF_CAMERA_STATE_SAVED); // Listen to layout events connectITK(m_Model->GetParentUI()->GetDisplayLayoutModel(), DisplayLayoutModel::ViewPanelLayoutChangeEvent()); // Listen to changes in active tool to adjust button visibility connectITK(m_Model->GetParentUI()->GetGlobalState()->GetToolbarMode3DModel(), ValueChangedEvent()); // Set up the buttons this->UpdateActionButtons(); // Start the timer m_RenderTimer->start(); } void ViewPanel3D::UpdateExpandViewButton() { // Get the layout a pplied when the button is pressed DisplayLayoutModel *dlm = m_GlobalUI->GetDisplayLayoutModel(); DisplayLayoutModel::ViewPanelLayout layout = dlm->GetViewPanelExpandButtonActionModel(3)->GetValue(); // Set the tooltip if(layout == DisplayLayoutModel::VIEW_ALL) { ui->btnExpand->setIcon(QIcon(":/root/dl_fourviews.png")); ui->btnExpand->setToolTip("Restore the four-panel display configuration"); } else { ui->btnExpand->setIcon(QIcon(":/root/dl_3d.png")); ui->btnExpand->setToolTip("Expand the 3D view to occupy the entire window"); } } // This method is run in a concurrent thread void ViewPanel3D::UpdateMeshesInBackground() { // Make sure the model actually requires updating if(m_Model && m_Model->CheckState(Generic3DModel::UIF_MESH_DIRTY)) { m_Model->UpdateSegmentationMesh(m_RenderProgressCommand); } } void ViewPanel3D::ProgressCallback(itk::Object *source, const itk::EventObject &) { itk::ProcessObject *po = static_cast(source); m_RenderProgressMutex.lock(); m_RenderProgressValue = po->GetProgress(); m_RenderProgressMutex.unlock(); } void ViewPanel3D::on_btnScreenshot_clicked() { MainImageWindow *window = findParentWidget(this); window->ExportScreenshot(3); } void ViewPanel3D::on_btnAccept_clicked() { if(!m_Model->AcceptAction()) { QMessageBox::information(this, "No voxels were updated", "The 3D operation did not update any voxels in " "the segmentation. Check that the foreground and " "background labels are selected correctly."); } } void ViewPanel3D::on_btnCancel_clicked() { m_Model->CancelAction(); } void ViewPanel3D::on_btnExpand_clicked() { // Get the layout applied when the button is pressed DisplayLayoutModel *dlm = m_GlobalUI->GetDisplayLayoutModel(); DisplayLayoutModel::ViewPanelLayout layout = dlm->GetViewPanelExpandButtonActionModel(3)->GetValue(); // Apply this layout dlm->GetViewPanelLayoutModel()->SetValue(layout); } void ViewPanel3D::onTimer() { if(!m_RenderFuture.isRunning()) { // Does work need to be done? if(m_Model && ui->actionContinuous_Update->isChecked() && m_Model->CheckState(Generic3DModel::UIF_MESH_DIRTY)) { // Launch the worker thread m_RenderProgressValue = 0; m_RenderElapsedTicks = 0; m_RenderFuture = QtConcurrent::run(this, &ViewPanel3D::UpdateMeshesInBackground); } else { ui->progressBar->setVisible(false); } } else { // We only want to show progress after some minimum timeout (1 sec) if((++m_RenderElapsedTicks) > 10) { ui->progressBar->setVisible(true); m_RenderProgressMutex.lock(); emit renderProgress((int)(1000 * m_RenderProgressValue)); m_RenderProgressMutex.unlock(); } } } void ViewPanel3D::on_actionReset_Viewpoint_triggered() { m_Model->ResetView(); } void ViewPanel3D::on_actionSave_Viewpoint_triggered() { m_Model->SaveCameraState(); } void ViewPanel3D::on_actionRestore_Viewpoint_triggered() { m_Model->RestoreCameraState(); } void ViewPanel3D::on_actionContinuous_Update_triggered() { ui->btnUpdateMesh->setVisible(!ui->actionContinuous_Update->isChecked()); } void ViewPanel3D::on_btnMenu_pressed() { m_DropMenu->popup(QCursor::pos()); ui->btnMenu->setDown(false); } void ViewPanel3D::on_btnFlip_clicked() { m_Model->FlipAction(); } void ViewPanel3D::UpdateActionButtons() { ToolbarMode3DType mode = m_Model->GetParentUI()->GetGlobalState()->GetToolbarMode3D(); switch(mode) { case TRACKBALL_MODE: case CROSSHAIRS_3D_MODE: ui->btnAccept->setVisible(false); ui->btnCancel->setVisible(false); ui->btnFlip->setVisible(false); break; case SPRAYPAINT_MODE: ui->btnAccept->setVisible(true); ui->btnCancel->setVisible(true); ui->btnFlip->setVisible(false); break; case SCALPEL_MODE: ui->btnAccept->setVisible(true); ui->btnCancel->setVisible(true); ui->btnFlip->setVisible(true); break; } } void ViewPanel3D::on_actionClear_Rendering_triggered() { m_Model->ClearRenderingAction(); } itksnap-3.4.0/GUI/Qt/Components/ViewPanel3D.h000066400000000000000000000037751263013355200205720ustar00rootroot00000000000000#ifndef VIEWPANEL3D_H #define VIEWPANEL3D_H #include #include "Generic3DModel.h" #include #include #include #include #include #include #include namespace Ui { class ViewPanel3D; } namespace itk { template class MemberCommand; } class Generic3DModel; class GenericView3D; class QMenu; class ViewPanel3D : public SNAPComponent { Q_OBJECT public: explicit ViewPanel3D(QWidget *parent = 0); ~ViewPanel3D(); // Register with the global model void Initialize(GlobalUIModel *model); GenericView3D *Get3DView(); signals: void renderProgress(int progress); private slots: virtual void onModelUpdate(const EventBucket &bucket); void on_btnUpdateMesh_clicked(); void on_btnScreenshot_clicked(); void on_btnAccept_clicked(); void on_btnCancel_clicked(); void on_btnExpand_clicked(); void onTimer(); void on_actionReset_Viewpoint_triggered(); void on_actionSave_Viewpoint_triggered(); void on_actionRestore_Viewpoint_triggered(); void on_actionContinuous_Update_triggered(); void on_btnMenu_pressed(); void on_btnFlip_clicked(); void on_actionClear_Rendering_triggered(); private: Ui::ViewPanel3D *ui; GlobalUIModel *m_GlobalUI; Generic3DModel *m_Model; QMenu *m_DropMenu; QTimer *m_RenderTimer; // A future used to track background rendering QFuture m_RenderFuture; // A mutex on the progress value, which is accesed by multiple threads mutable QMutex m_RenderProgressMutex; // Progress of the rendering operation double m_RenderProgressValue; // Elapsed time since begin of render operation int m_RenderElapsedTicks; typedef itk::MemberCommand CommandType; SmartPtr m_RenderProgressCommand; void UpdateExpandViewButton(); void UpdateMeshesInBackground(); void UpdateActionButtons(); void ProgressCallback(itk::Object *source, const itk::EventObject &event); }; #endif // VIEWPANEL3D_H itksnap-3.4.0/GUI/Qt/Components/ViewPanel3D.ui000066400000000000000000000203761263013355200207540ustar00rootroot00000000000000 ViewPanel3D 0 0 462 470 Form 2 0 0 0 0 0 0 4 0 0 16777215 20 update Qt::Horizontal QSizePolicy::Fixed 10 20 Qt::Horizontal QSizePolicy::Fixed 10 20 16777215 20 Accept 3D editing operation accept 16777215 20 Flip the direction of the cut plane flip 16777215 20 Cancel 3D editing operation cancel 40 20 24 Qt::Horizontal 40 20 20 20 Expand this view to occupy the entire window ... :/root/dl_3d.png:/root/dl_3d.png true 20 20 Save a screenshot ... :/root/screencapture2.gif true 20 20 Additional 3D view controls :/root/open_popup_16.png:/root/open_popup_16.png 12 12 true Reset Viewpoint Ctrl+K, K Save Viewpoint Ctrl+K, S Qt::WindowShortcut Restore Viewpoint Ctrl+K, R true Continuous Update Ctrl+K, C Clear 3D Display Clears the meshes rendered in the 3D display Ctrl+K, X GenericView3D QWidget
GenericView3D.h
1
itksnap-3.4.0/GUI/Qt/Components/VoxelIntensityQTableModel.cxx000066400000000000000000000055401263013355200241320ustar00rootroot00000000000000#include "VoxelIntensityQTableModel.h" #include #include #include #include #include "LatentITKEventNotifier.h" #include "SNAPEvents.h" #include "LayerSelectionModel.h" VoxelIntensityQTableModel::VoxelIntensityQTableModel(QObject *parent) : QAbstractTableModel(parent) { } void VoxelIntensityQTableModel::SetParentModel(GlobalUIModel *model) { m_Model = model; // Listen to changes in the model LatentITKEventNotifier::connect(m_Model, CursorUpdateEvent(), this, SLOT(onModelUpdate(const EventBucket &))); // Listen to changes in the model LatentITKEventNotifier::connect(m_Model, LayerChangeEvent(), this, SLOT(onModelUpdate(const EventBucket &))); } int VoxelIntensityQTableModel::rowCount(const QModelIndex &parent) const { return m_Model->GetLoadedLayersSelectionModel()->GetNumberOfLayers(); } int VoxelIntensityQTableModel::columnCount(const QModelIndex &parent) const { return 2; } #include QVariant VoxelIntensityQTableModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) { // Get the corresponding layer LayerIterator it = m_Model->GetLoadedLayersSelectionModel()->GetNthLayer(index.row()); if(index.column() == 0) { return QString(it.GetLayer()->GetNickname().c_str()); } else { // Get the cursor position Vector3ui cursor = m_Model->GetDriver()->GetCursorPosition(); // TODO: do we want to use a tree model here to represent multi-channel // images? For the time being, we can list all of the components, but // we should really come up with something better ImageWrapperBase *iw = it.GetLayer(); vnl_vector voxel(iw->GetNumberOfComponents(), 0.0); iw->GetVoxelMappedToNative(cursor, voxel.data_block()); if(voxel.size() > 1) { std::ostringstream oss; for(int i = 0; i < voxel.size(); i++) { if(i > 0) oss << ","; oss << std::setprecision(3) << voxel[i]; } return QString(oss.str().c_str()); } else { return voxel[0]; } } } return QVariant(); } QVariant VoxelIntensityQTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole) { if (orientation == Qt::Horizontal) { return section == 0 ? "Layer" : "Intensity"; } } return QVariant(); } void VoxelIntensityQTableModel::onModelUpdate(const EventBucket &b) { if(b.HasEvent(LayerChangeEvent())) { this->beginResetModel(); this->endResetModel(); } else { int nr = rowCount(); if(nr > 0) { this->dataChanged(index(0,1), index(nr-1, 1)); } } } itksnap-3.4.0/GUI/Qt/Components/VoxelIntensityQTableModel.h000066400000000000000000000051231263013355200235540ustar00rootroot00000000000000#ifndef VOXELINTENSITYQTABLEMODEL_H #define VOXELINTENSITYQTABLEMODEL_H #include #include class GlobalUIModel; class EventBucket; class VoxelIntensityQTableModel : public QAbstractTableModel { Q_OBJECT public: explicit VoxelIntensityQTableModel(QObject *parent = 0); void SetParentModel(GlobalUIModel *model); int rowCount(const QModelIndex &parent = QModelIndex()) const ; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; signals: public slots: void onModelUpdate(const EventBucket &); private: GlobalUIModel *m_Model; }; template class DefaultQtItemRowTraits { virtual int GetColumnCount() = 0; virtual QVariant GetItemData(TItem &, int, Qt::ItemDataRole) = 0; }; /** This class provides the interface between SNAP's random access collection model (i.e., a list of items with cached random access), and the Qt list and table widgets. This class relies on TItemRowTraits template parameter, which describes how to map objects of type TItem to the cells in the table. */ template > class QtWrappedRandomAccessCollectionModel : public QAbstractItemModel { public: // This is the internal model that we provide a wrapping around typedef AbstractRandomAccessCollectionModel WrappedModel; // Get and set the wrapped model irisGetSetMacro(WrappedModel, WrappedModel *) // The traits for this object do not have to be static. We allow the // user to pass it a traits object TItemRowTraits &GetTraits() { return m_Traits; } // Subclass standard functions QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const { return this->createIndex(row, column); } QModelIndex parent(const QModelIndex &child) const { return QModelIndex(); } int rowCount(const QModelIndex &parent = QModelIndex()) const { return (int) m_WrappedModel->GetSize(); } int columnCount(const QModelIndex &parent = QModelIndex()) const { return m_Traits.GetColumnCount(); } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { // Get the right item TItem item = (*m_WrappedModel)[index.row()]; return m_Traits.GetItemData(item, index.column(), role); } protected: WrappedModel *m_WrappedModel; TItemRowTraits m_Traits; }; #endif // VOXELINTENSITYQTABLEMODEL_H itksnap-3.4.0/GUI/Qt/Components/ZoomInspector.cxx000066400000000000000000000053661263013355200216750ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #include "ZoomInspector.h" #include "ui_ZoomInspector.h" #include "GlobalUIModel.h" #include "SliceWindowCoordinator.h" #include "QtWidgetActivator.h" #include "QtDoubleSpinBoxCoupling.h" #include "QtCheckBoxCoupling.h" #include #include ZoomInspector::ZoomInspector(QWidget *parent) : SNAPComponent(parent), ui(new Ui::ZoomInspector) { ui->setupUi(this); } ZoomInspector::~ZoomInspector() { delete ui; } void ZoomInspector::SetModel(GlobalUIModel *model) { // Set the model m_Model = model; // Connect buttons to global actions ui->btnResetViews->setAction("actionZoomToFitInAllViews"); ui->btnCenterViews->setAction("actionCenter_on_Cursor"); // Conditional activation of widgets activateOnFlag(ui->inZoom, model, UIF_LINKED_ZOOM); activateOnFlag(ui->btnZoom1, model, UIF_LINKED_ZOOM); activateOnFlag(ui->btnZoom2, model, UIF_LINKED_ZOOM); activateOnFlag(ui->btnZoom4, model, UIF_LINKED_ZOOM); activateOnFlag(this, model, UIF_BASEIMG_LOADED); // Couple the linked zoom checkbox makeCoupling(ui->chkLinkedZoom, model->GetSliceCoordinator()->GetLinkedZoomModel()); // Couple zoom widget to the linked zoom level makeCoupling(ui->inZoom, model->GetSliceCoordinator()->GetCommonZoomFactorModel()); } void ZoomInspector::on_chkLinkedZoom_stateChanged(int state) { m_Model->GetSliceCoordinator()->SetLinkedZoom(state == Qt::Checked); } void ZoomInspector::on_btnZoom1_pressed() { m_Model->GetSliceCoordinator()->SetZoomPercentageInAllWindows(1); } void ZoomInspector::on_btnZoom2_pressed() { m_Model->GetSliceCoordinator()->SetZoomPercentageInAllWindows(2); } void ZoomInspector::on_btnZoom4_pressed() { m_Model->GetSliceCoordinator()->SetZoomPercentageInAllWindows(4); } itksnap-3.4.0/GUI/Qt/Components/ZoomInspector.h000066400000000000000000000031431263013355200213110ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef ZOOMINSPECTOR_H #define ZOOMINSPECTOR_H #include #include class GlobalUIModel; namespace Ui { class ZoomInspector; } class ZoomInspector : public SNAPComponent { Q_OBJECT public: explicit ZoomInspector(QWidget *parent = 0); ~ZoomInspector(); irisGetMacro(Model, GlobalUIModel *) void SetModel(GlobalUIModel *model); private slots: void on_chkLinkedZoom_stateChanged(int arg1); void on_btnZoom1_pressed(); void on_btnZoom2_pressed(); void on_btnZoom4_pressed(); private: Ui::ZoomInspector *ui; GlobalUIModel *m_Model; }; #endif // ZOOMINSPECTOR_H itksnap-3.4.0/GUI/Qt/Components/ZoomInspector.ui000066400000000000000000000304031263013355200214760ustar00rootroot00000000000000 ZoomInspector 0 0 173 526 60 0 Form * { font-size: 12px; } QSpinBox { font-size: 11px; } 4 0 0 0 0 -1 Qt::TabFocus <html><head/><body><p><span style=" font-weight:600;">Linked Zoom</span></p><p>When enabled, the zoom level in all three orthogonal slice views is kept equal. </p></body></html> Linked zoom Qt::Vertical QSizePolicy::Fixed 20 5 4 1 0 60 0 -1 <html><head/><body><p><span style=" font-weight:600;">Zoom level for slice views</span></p><p>Set the zoom level in the three orthogonal slice views. Zoom level is expressed in units of screen pixels per physical mm. For example zoom level of 2 means that there are 2 screen pixels for every mm in the scanner coordinate system. </p></body></html> false 3 true Zoom: 0 0 inZoom true font-size:9pt; px/mm 0 0 inZoom 4 0 0 0 0 32 24 <html><head/><body><p><span style=" font-weight:600;">Set zoom to 1:1</span></p><p>Set zoom level such that one screen pixel maps onto one image voxel. <span style=" font-style:italic;">When image voxels have different dimensions, the smallest of the three side lengths of the voxel is used.</span></p></body></html> 1x 0 0 0 0 32 24 0 0 <html><head/><body><p><span style=" font-weight:600;">Set zoom to 2:1</span></p><p>Set zoom level such that two screen pixels map onto one image voxel. <span style=" font-style:italic;">When image voxels have different dimensions, the smallest of the three side lengths of the voxel is used.</span></p></body></html> 2x 0 0 32 24 <html><head/><body><p><span style=" font-weight:600;">Set zoom to 4:1</span></p><p>Set zoom level such that four screen pixels map onto one image voxel. <span style=" font-style:italic;">When image voxels have different dimensions, the smallest of the three side lengths of the voxel is used.</span></p></body></html> 4x false Qt::Horizontal 0 20 .QWidget { padding-left:50px; } 2 0 0 0 0 8 4 4 4 4 1 0 0 0 -1 <html><head/><body><p>Set the zoom level so that the image fully fits into the available screen space.</p></body></html> Zoom to fit 0 0 -1 <html><head/><body><p>Pan so that the 3D cursor is positioned at the center of the orthogonal slice views</p></body></html> Center on cursor Qt::Vertical 20 357 QActionButton QPushButton
QActionButton.h
itksnap-3.4.0/GUI/Qt/Coupling/000077500000000000000000000000001263013355200157575ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/Coupling/QtAbstractButtonCoupling.h000066400000000000000000000104331263013355200230760ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTABSTRACTBUTTONCOUPLING_H #define QTABSTRACTBUTTONCOUPLING_H #include #include "SNAPQtCommon.h" #include #include #include /** Default traits for QColorButtonWidget, to be hooked up to a model of integer 3-vector that specifies color in the 0-255 range */ template class DefaultWidgetValueTraits< iris_vector_fixed, QColorButtonWidget> : public WidgetValueTraitsBase, QColorButtonWidget *> { public: typedef iris_vector_fixed ColorVec; // Get the Qt signal that the widget fires when its value has changed. The // value here is the selected item in the combo box. const char *GetSignal() { return SIGNAL(valueChanged()); } ColorVec GetValue(QColorButtonWidget *w) { QColor qclr = w->value(); return ColorVec(qclr.red(), qclr.green(), qclr.blue()); } void SetValue(QColorButtonWidget *w, const ColorVec &value) { // We have to actually find the item w->setValue(QColor(value[0],value[1],value[2])); } void SetValueToNull(QColorButtonWidget *w) { w->setValue(QColor()); } protected: }; /** Alternative traits for QColorButtonWidget, to be hooked up to a model of double 3-vector that specifies color in the 0-1 range */ template <> class DefaultWidgetValueTraits< iris_vector_fixed, QColorButtonWidget> : public WidgetValueTraitsBase, QColorButtonWidget *> { public: typedef iris_vector_fixed ColorVec; // Get the Qt signal that the widget fires when its value has changed. The // value here is the selected item in the combo box. const char *GetSignal() { return SIGNAL(valueChanged()); } ColorVec GetValue(QColorButtonWidget *w) { QColor qclr = w->value(); return ColorVec(qclr.red() / 255.0, qclr.green() / 255.0, qclr.blue() / 255.0); } void SetValue(QColorButtonWidget *w, const ColorVec &value) { // We have to actually find the item Vector3i cint = to_int(value * 255.0); w->setValue(QColor(cint[0], cint[1], cint[2])); } void SetValueToNull(QColorButtonWidget *w) { w->setValue(QColor()); } protected: }; /** * This traits class allows a color button widget to be hooked up to a model * with a domain. However, the domain will be ignored. */ template class DefaultWidgetDomainTraits, QColorButtonWidget> : public WidgetDomainTraitsBase, QColorButtonWidget *> { public: typedef NumericValueRange DomainType; virtual void SetDomain(QColorButtonWidget *, const DomainType &) { } virtual DomainType GetDomain(QColorButtonWidget *) { return DomainType(); } }; /** * A simple coupling between a button and a boolean value. */ template <> class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(toggled(bool)); } bool GetValue(QAbstractButton *w) { return w->isChecked(); } void SetValue(QAbstractButton *w, const bool &value) { w->setChecked(value); } void SetValueToNull(QAbstractButton *w) { w->setChecked(false); } }; #endif // QTABSTRACTBUTTONCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtAbstractItemViewCoupling.h000066400000000000000000000260201263013355200233530ustar00rootroot00000000000000#ifndef QTABSTRACTITEMVIEWCOUPLING_H #define QTABSTRACTITEMVIEWCOUPLING_H #include #include "SNAPQtCommon.h" #include #include #include #include #include #include #include /** * Some Ideas: * * 1. Coupling between a selection model and an integer value. Treat the * domain as trivial. * * 2. Coupling between a model that represents a list of items with descriptors * and a QStandardItemModel. Mapping is through a coupling. This coupling * maps between rows in the descriptor and items in the item model. */ /** * The default value traits for QAbstractItemView use the currentRow in the item view * to represent a value. Each row is assigned a value of class TAtomic, and the value * of the current row is considered to be the value of the widget. * * in other words, the model holds an item of type TAtomic and the widget selects a * corresponding row in the widget */ template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)); } virtual QObject *GetSignalEmitter(QObject *w) { QAbstractItemView *view = dynamic_cast(w); return view ? view->selectionModel() : NULL; } TAtomic GetValue(QAbstractItemView *w) { // Get the UserData associated with the current item QModelIndex icur = w->currentIndex(); QModelIndex irow = w->model()->index(icur.row(), 0, w->currentIndex().parent()); return irow.data(Qt::UserRole).value(); } bool FindRowRecursive(QAbstractItemView *w, QModelIndex parent, const TAtomic &value) { for(int i = 0; i < w->model()->rowCount(parent); i++) { QModelIndex index = w->model()->index(i, 0, parent); TAtomic val = w->model()->data(index, Qt::UserRole).value(); if(val == value) { w->setCurrentIndex(index); return true; } else if(FindRowRecursive(w, index, value)) { return true; } } return false; } void SetValue(QAbstractItemView *w, const TAtomic &value) { // Find the item in the model FindRowRecursive(w, QModelIndex(), value); } void SetValueToNull(QAbstractItemView *w) { QModelIndex index = w->model()->index(-1, 0); w->setCurrentIndex(index); } }; /** * An alternative value traits for QAbstractItemView uses the selection of all rows in * the view to represent a value. Thus value here is map from an atomic type to boolean, * where the boolean stores the selection state. * * Under this coupling, the model stores a list of on/off items, and the user can use * multiple selection to modify this list. */ template class DefaultWidgetValueTraits< std::map, QAbstractItemView> : public WidgetValueTraitsBase, QAbstractItemView *> { public: typedef std::map AtomicType; const char *GetSignal() { return SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)); } virtual QObject *GetSignalEmitter(QObject *w) { QAbstractItemView *view = dynamic_cast(w); return view ? view->selectionModel() : NULL; } void ScanValuesRecursive(QAbstractItemView *w, QModelIndex parent, AtomicType &result) { for(int i = 0; i < w->model()->rowCount(parent); i++) { QModelIndex index = w->model()->index(i, 0, parent); TItemIndex val = w->model()->data(index, Qt::UserRole).value(); result[val] = w->selectionModel()->isSelected(index); ScanValuesRecursive(w, index, result); } } void SetValuesRecursive(QAbstractItemView *w, QModelIndex parent, const AtomicType &mapping) { for(int i = 0; i < w->model()->rowCount(parent); i++) { QModelIndex index = w->model()->index(i, 0, parent); TItemIndex val = w->model()->data(index, Qt::UserRole).value(); typename AtomicType::const_iterator it = mapping.find(val); if(it != mapping.end()) w->selectionModel()->select(index, it->second ? QItemSelectionModel::Select : QItemSelectionModel::Deselect); SetValuesRecursive(w, index, mapping); } } AtomicType GetValue(QAbstractItemView *w) { AtomicType value_map; ScanValuesRecursive(w, QModelIndex(), value_map); return value_map; } void SetValue(QAbstractItemView *w, const AtomicType &value) { // Find the item in the model SetValuesRecursive(w, QModelIndex(), value); } void SetValueToNull(QAbstractItemView *w) { if(w->selectionModel()) w->selectionModel()->clear(); } }; template class QStandardItemModelWidgetDomainTraits : public WidgetDomainTraitsBase { public: // The information about the item type are taken from the domain typedef typename TItemDomain::ValueType AtomicType; typedef typename TItemDomain::DescriptorType DescriptorType; typedef TItemDomain DomainType; // Navigate through proxy models until we find a standard item model in the // view. Allows couplings to be installed on views with proxies QStandardItemModel *GetTopLevelModel(QAbstractItemView *w) { QAbstractItemModel *model = w->model(); while(model) { QStandardItemModel *msi = dynamic_cast(model); if(msi) return msi; QAbstractProxyModel *mpx = dynamic_cast(model); if(mpx) model = mpx->sourceModel(); } return NULL; } void SetDomain(QAbstractItemView *w, const DomainType &domain) { // Remove everything from the model QStandardItemModel *model = GetTopLevelModel(w); if(!model) return; model->clear(); model->setColumnCount(TRowTraits::columnCount()); // Populate for(typename DomainType::const_iterator it = domain.begin(); it != domain.end(); ++it) { // Get the key/value pair AtomicType value = domain.GetValue(it); const DescriptorType &row = domain.GetDescription(it); // Use the row traits to map information to the widget QList rlist; for(int j = 0; j < model->columnCount(); j++) { QStandardItem *item = new QStandardItem(); TRowTraits::updateItem(item, j, value, row); rlist.append(item); } model->appendRow(rlist); } } void UpdateDomainDescription(QAbstractItemView *w, const DomainType &domain) { // Remove everything from the model QStandardItemModel *model = GetTopLevelModel(w); if(!model) return; // This is not the most efficient way of doing things, because we // are still linearly parsing through the widget and updating rows. // But at least the actual modifications to the widget are limited // to the rows that have been modified. // // What would be more efficient is to have a list of ids which have // been modified and update only those. Or even better, implement all // of this using an AbstractItemModel int nrows = model->rowCount(); for(int i = 0; i < nrows; i++) { QStandardItem *item = model->item(i); AtomicType id = TRowTraits::getItemValue(item); typename DomainType::const_iterator it = domain.find(id); if(it != domain.end()) { const DescriptorType &row = domain.GetDescription(it); for(int j = 0; j < model->columnCount(); j++) TRowTraits::updateItem(model->item(i,j), j, id, row); } } } TItemDomain GetDomain(QAbstractItemView *w) { // We don't actually pull the widget because the domain is fully specified // by the model. return DomainType(); } }; class SingleColumnColorLabelToQSIMCouplingRowTraits { public: static int columnCount() { return 1; } static void updateItem(QStandardItem *item, int column, LabelType label, const ColorLabel &cl) { // Handle the timestamp - if the timestamp has not changed, don't need to update unsigned long ts = item->data(Qt::UserRole+1).toLongLong(); if(ts == cl.GetTimeStamp().GetMTime()) return; // The description QString text = QString::fromUtf8(cl.GetLabel()); // The color QColor fill(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2)); // Icon based on the color QIcon ic = CreateColorBoxIcon(16, 16, fill); // Create item and set its properties item->setIcon(ic); item->setText(text); item->setData(label, Qt::UserRole); item->setData(text, Qt::EditRole); item->setData((qulonglong) cl.GetTimeStamp().GetMTime(), Qt::UserRole+1); } static LabelType getItemValue(QStandardItem *item) { return item->data(Qt::UserRole).value(); } }; class TwoColumnColorLabelToQSIMCouplingRowTraits { public: static int columnCount() { return 2; } static void updateItem(QStandardItem *item, int column, LabelType label, const ColorLabel &cl) { // Handle the timestamp - if the timestamp has not changed, don't need to update unsigned long ts = item->data(Qt::UserRole+1).toLongLong(); if(ts == cl.GetTimeStamp().GetMTime()) return; // The description if(column == 0) { QString text = QString("%1").arg(label); // The color QColor fill(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2)); // Icon based on the color QIcon ic = CreateColorBoxIcon(16, 16, fill); // Create item and set its properties item->setIcon(ic); item->setText(text); item->setData(text, Qt::EditRole); item->setData(label, Qt::UserRole); } else if(column == 1) { QString text = QString::fromUtf8(cl.GetLabel()); item->setText(text); item->setData(text, Qt::EditRole); } // Update the timestamp item->setData((qulonglong) cl.GetTimeStamp().GetMTime(), Qt::UserRole+1); } static LabelType getItemValue(QStandardItem *item) { return item->data(Qt::UserRole).value(); } }; template class DefaultQSIMCouplingRowTraits { }; template<> class DefaultQSIMCouplingRowTraits : public TwoColumnColorLabelToQSIMCouplingRowTraits { }; template class DefaultWidgetDomainTraits : public QStandardItemModelWidgetDomainTraits< TDomain, DefaultQSIMCouplingRowTraits > { }; template<> class DefaultWidgetDomainTraits { public: void SetDomain(QAbstractItemView *w, const TrivialDomain &domain) {} TrivialDomain GetDomain(QAbstractItemView *w) { return TrivialDomain(); } void UpdateDomainDescription(QAbstractItemView *w, const TrivialDomain &domain) {} }; #endif // QTABSTRACTITEMVIEWCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtActionCoupling.h000066400000000000000000000011011263013355200213440ustar00rootroot00000000000000#ifndef QTACTIONCOUPLING_H #define QTACTIONCOUPLING_H #include /** Default traits for a checkable QAction to a boolean */ template <> class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(triggered(bool)); } bool GetValue(QAction *w) { return w->isChecked(); } void SetValue(QAction *w, const bool &value) { w->setChecked(value); } void SetValueToNull(QAction *w) { w->setChecked(false); } }; #endif // QTACTIONCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtActionGroupCoupling.h000066400000000000000000000023171263013355200223730ustar00rootroot00000000000000#ifndef QTACTIONGROUPCOUPLING_H #define QTACTIONGROUPCOUPLING_H #include #include #include /** * Create a coupling between an enum (not necessarily starting with zero) and an * QActionGroup containing a set of actions. The mapping from enum values to * actions is provided by the actionMap parameter */ template void makeActionGroupCoupling( QActionGroup *actionGroup, std::map actionMap, AbstractPropertyModel *model) { makeCheckableWidgetGroupCoupling(actionGroup, actionMap, model); } /** * Create a coupling between an enum (starting with zero) and an QActionGroup */ template void makeActionGroupCoupling( QActionGroup *w, AbstractPropertyModel *model) { QList kids = w->actions(); std::map buttonMap; int iwidget = 0; for(QList::const_iterator it = kids.begin(); it != kids.end(); ++it) { QAction *qab = dynamic_cast(*it); if(qab) buttonMap[static_cast(iwidget++)] = qab; } makeCheckableWidgetGroupCoupling(w, buttonMap, model); } #endif // QTACTIONGROUPCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtCheckBoxCoupling.h000066400000000000000000000031471263013355200216310ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTCHECKBOXCOUPLING_H #define QTCHECKBOXCOUPLING_H #include #include template struct DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(stateChanged(int)); } TAtomic GetValue(QCheckBox *w) { return static_cast(w->isChecked()); } void SetValue(QCheckBox *w, const TAtomic &value) { w->setChecked(static_cast(value)); } void SetValueToNull(QCheckBox *w) { w->setChecked(false); } }; #endif // QTCHECKBOXCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtCheckableWidgetGroupCoupling.h000066400000000000000000000057161263013355200241710ustar00rootroot00000000000000#ifndef QTCHECKABLEWIDGETGROUPCOUPLING_H #define QTCHECKABLEWIDGETGROUPCOUPLING_H #include template struct RadioButtonGroupTraits : public WidgetValueTraitsBase { public: typedef std::map ButtonMap; typedef typename ButtonMap::iterator ButtonIterator; RadioButtonGroupTraits(ButtonMap bm) : m_ButtonMap(bm) {} TAtomic GetValue(TParentWidget *w) { // Figure out which button is checked for(ButtonIterator it = m_ButtonMap.begin(); it != m_ButtonMap.end(); ++it) { TCheckableWidgetBase *qab = it->second; if(qab->isChecked()) return it->first; } // This is ambiguous... return static_cast(0); } void SetValue(TParentWidget *w, const TAtomic &value) { // Set all the buttons for(ButtonIterator it = m_ButtonMap.begin(); it != m_ButtonMap.end(); ++it) { TCheckableWidgetBase *qab = it->second; qab->setChecked(it->first == value); } } void SetValueToNull(TParentWidget *w) { // Set all the buttons for(ButtonIterator it = m_ButtonMap.begin(); it != m_ButtonMap.end(); ++it) { TCheckableWidgetBase *qab = it->second; qab->setChecked(false); } } protected: ButtonMap m_ButtonMap; }; template void makeCheckableWidgetGroupCoupling( TParentWidget *parentWidget, std::map buttonMap, AbstractPropertyModel *model) { typedef AbstractPropertyModel ModelType; typedef RadioButtonGroupTraits WidgetValueTraits; typedef DefaultWidgetDomainTraits WidgetDomainTraits; typedef PropertyModelToWidgetDataMapping< ModelType, TParentWidget *, WidgetValueTraits, WidgetDomainTraits> MappingType; WidgetValueTraits valueTraits(buttonMap); WidgetDomainTraits domainTraits; MappingType *mapping = new MappingType(parentWidget, model, valueTraits, domainTraits); QtCouplingHelper *h = new QtCouplingHelper(parentWidget, mapping); // Populate the widget mapping->InitializeWidgetFromModel(); // Listen to value change events from the model LatentITKEventNotifier::connect( model, ValueChangedEvent(), h, SLOT(onPropertyModification(const EventBucket &))); LatentITKEventNotifier::connect( model, DomainChangedEvent(), h, SLOT(onPropertyModification(const EventBucket &))); // Listen to value change events for every child widget typedef typename std::map::const_iterator Iter; for(Iter it = buttonMap.begin(); it != buttonMap.end(); ++it) { TCheckableWidgetBase *qab = it->second; h->connect(qab, SIGNAL(toggled(bool)), SLOT(onUserModification())); } } #endif // QTCHECKABLEWIDGETGROUPCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtColorWheelCoupling.h000066400000000000000000000022231263013355200222000ustar00rootroot00000000000000#ifndef QTCOLORWHEELCOUPLING_H #define QTCOLORWHEELCOUPLING_H #include "ColorWheel.h" std::ostream &operator << (std::ostream &out, QColor color) { out << color.red() << ", " << color.green() << ", " << color.blue(); return out; } template class DefaultWidgetValueTraits< iris_vector_fixed, ColorWheel> : public WidgetValueTraitsBase, ColorWheel *> { public: typedef iris_vector_fixed ColorVec; // Get the Qt signal that the widget fires when its value has changed. The // value here is the selected item in the combo box. const char *GetSignal() { return SIGNAL(colorChange(const QColor &)); } ColorVec GetValue(ColorWheel *w) { QColor qclr = w->color(); return ColorVec(qclr.red(), qclr.green(), qclr.blue()); } void SetValue(ColorWheel *w, const ColorVec &value) { // We have to actually find the item QColor newcol(value[0],value[1],value[2]); if(newcol != w->color()) { w->setColor(newcol); } } void SetValueToNull(ColorWheel *w) { w->setColor(QColor()); } }; #endif // QTCOLORWHEELCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtComboBoxCoupling.h000066400000000000000000000166331263013355200216570ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTCOMBOBOXCOUPLING_H #define QTCOMBOBOXCOUPLING_H #include #include "SNAPQtCommon.h" #include #include // This is to allow the code below to work with DrawOverFilter Q_DECLARE_METATYPE(DrawOverFilter) /** Default traits for mapping a numeric value (or any sort of key, actually) to a row in a combo box */ template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: // Get the Qt signal that the widget fires when its value has changed. The // value here is the selected item in the combo box. const char *GetSignal() { return SIGNAL(currentIndexChanged(int)); } TAtomic GetValue(QComboBox *w) { int index = w->currentIndex(); QVariant id = w->itemData(index); return id.value(); } void SetValue(QComboBox *w, const TAtomic &value) { // We have to actually find the item. I looked up the Qt findItem // method and it seems quite involved and also quite long. So we // just do our own loop for(int i = 0; i < w->count(); i++) { if(value == w->itemData(i).value()) { w->setCurrentIndex(i); return; } } w->setCurrentIndex(-1); } void SetValueToNull(QComboBox *w) { w->setCurrentIndex(-1); } }; /** These are the row traits for adding and updating rows in combo boxes. This class is further parameterized by the class TItemDesriptionTraits, which is used to obtain the text and icon information from the value/description pairs provided by the model. */ template class TextAndIconComboBoxRowTraits { public: static void removeAll(QComboBox *w) { w->clear(); } static int getNumberOfRows(QComboBox *w) { return w->count(); } static TAtomic getValueInRow(QComboBox *w, int i) { return w->itemData(i).value(); } static void appendRow(QComboBox *w, TAtomic label, const TDesc &desc) { // The description QString text = TItemDesriptionTraits::GetText(label, desc); // QString text(cl.GetLabel()); // The icon QIcon icon = TItemDesriptionTraits::GetIcon(label, desc); // The icon signature - a value that can be used to check if the icon has changed QVariant iconSig = TItemDesriptionTraits::GetIconSignature(label, desc); // Icon based on the color w->addItem(icon, text, QVariant::fromValue(label)); w->setItemData(w->count()-1, iconSig, Qt::UserRole + 1); } static void updateRowDescription(QComboBox *w, int index, const TDesc &desc) { // The current value TAtomic label = w->itemData(index).value(); // Get the properies and compare them to the color label QVariant currentIconSig = w->itemData(index, Qt::UserRole + 1); QVariant newIconSig = TItemDesriptionTraits::GetIconSignature(label, desc); if(currentIconSig != newIconSig) { QIcon ic = TItemDesriptionTraits::GetIcon(label, desc); w->setItemIcon(index, ic); w->setItemData(index, newIconSig, Qt::UserRole + 1); } QString currentText = w->itemText(index); QString newText = TItemDesriptionTraits::GetText(label, desc); if(currentText != newText) { w->setItemText(index, newText); } } }; template class StringRowDescriptionTraits { public: static QString GetText(TAtomic label, const std::string &text) { return QString(text.c_str()); } static QIcon GetIcon(TAtomic label, const std::string &text) { return QIcon(); } static QVariant GetIconSignature(TAtomic label, const std::string &text) { return QVariant(0); } }; // TODO: this stuff should be replaced by coupling with the abstract item model // Specific traits for filling drawing color label combos class DrawingColorRowDescriptionTraits { public: static QString GetText(LabelType label, const ColorLabel &cl) { return GetTitleForColorLabel(cl); } static QIcon GetIcon(LabelType label, const ColorLabel &cl) { QBrush brush = GetBrushForColorLabel(cl); return CreateColorBoxIcon(16, 16, brush); } static QVariant GetIconSignature(LabelType label, const ColorLabel &cl) { return QColor(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2)); } }; // Specific traits for filling draw-over color label combos class DrawOverFilterRowDescriptionTraits { public: static QString GetText(DrawOverFilter filter, const ColorLabel &cl) { return GetTitleForDrawOverFilter(filter, cl); } static QIcon GetIcon(DrawOverFilter filter, const ColorLabel &cl) { QBrush brush = GetBrushForDrawOverFilter(filter, cl); return CreateColorBoxIcon(16, 16, brush); } static QVariant GetIconSignature(DrawOverFilter filter, const ColorLabel &cl) { if(filter.CoverageMode == PAINT_OVER_ONE) return DrawingColorRowDescriptionTraits::GetIconSignature(filter.DrawOverLabel, cl); else return QVariant(filter.CoverageMode); } }; /** Use template specialization to generate default traits based on the model */ template class DefaultComboBoxRowTraits { }; template <> class DefaultComboBoxRowTraits : public TextAndIconComboBoxRowTraits { }; template <> class DefaultComboBoxRowTraits : public TextAndIconComboBoxRowTraits { }; template class DefaultComboBoxRowTraits : public TextAndIconComboBoxRowTraits > { }; // Define the defaults template class DefaultWidgetDomainTraits : public ItemSetWidgetDomainTraits< TDomain, QComboBox, DefaultComboBoxRowTraits > { }; // Define the behavior with trivial domain. In this case, we assume that the combo // box was set up in the GUI to have userdata matching the items in the model template <> class DefaultWidgetDomainTraits : public WidgetDomainTraitsBase { public: virtual void SetDomain(QComboBox *w, const TrivialDomain &domain) {} virtual TrivialDomain GetDomain(QComboBox *w) { return TrivialDomain(); } }; #endif // QTCOMBOBOXCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtDoubleSliderWithEditorCoupling.h000066400000000000000000000046721263013355200245270ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTDOUBLESLIDERWITHEDITORCOUPLING_H #define QTDOUBLESLIDERWITHEDITORCOUPLING_H #include #include /** Default traits for the Qt Double Spin Box */ template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(valueChanged(double)); } TAtomic GetValue(QDoubleSliderWithEditor *w) { return static_cast(w->value()); } void SetValue(QDoubleSliderWithEditor *w, const TAtomic &value) { w->setValue(static_cast(value)); } void SetValueToNull(QDoubleSliderWithEditor *w) { w->setValueToNull(); } }; template class DefaultWidgetDomainTraits, QDoubleSliderWithEditor> : public WidgetDomainTraitsBase, QDoubleSliderWithEditor *> { public: void SetDomain(QDoubleSliderWithEditor *w, const NumericValueRange &range) { w->setMinimum(range.Minimum); w->setMaximum(range.Maximum); w->setSingleStep(range.StepSize); } NumericValueRange GetDomain(QDoubleSliderWithEditor *w) { return NumericValueRange( static_cast(w->minimum()), static_cast(w->maximum()), static_cast(w->singleStep())); } }; #endif // QTDOUBLESLIDERWITHEDITORCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtDoubleSpinBoxCoupling.h000066400000000000000000000054331263013355200226600ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTDOUBLESPINBOXCOUPLING_H #define QTDOUBLESPINBOXCOUPLING_H #include #include /** Default traits for the Qt Double Spin Box */ template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(valueChanged(double)); } TAtomic GetValue(QDoubleSpinBox *w) { return static_cast(w->value()); } void SetValue(QDoubleSpinBox *w, const TAtomic &value) { w->setSpecialValueText(""); w->setValue(static_cast(value)); } void SetValueToNull(QDoubleSpinBox *w) { w->setValue(w->minimum()); w->setSpecialValueText(" "); } }; template class DefaultWidgetDomainTraits, QDoubleSpinBox> : public WidgetDomainTraitsBase, QDoubleSpinBox *> { public: void SetDomain(QDoubleSpinBox *w, const NumericValueRange &range) { w->setMinimum(range.Minimum); w->setMaximum(range.Maximum); w->setSingleStep(range.StepSize); // Make sure the precision is smaller than the step size. This is a // temporary fix. A better solution is to have the model provide the // precision for the widget. if(range.StepSize > 0) { double logstep = std::log10((double)range.StepSize); int prec = std::max((int) (1 - floor(logstep)), 0); w->setDecimals(prec); } else w->setDecimals(0); } NumericValueRange GetDomain(QDoubleSpinBox *w) { return NumericValueRange( static_cast(w->minimum()), static_cast(w->maximum()), static_cast(w->singleStep())); } }; #endif // QTDOUBLESPINBOXCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtLabelCoupling.h000066400000000000000000000013771263013355200211650ustar00rootroot00000000000000#ifndef QTLABELCOUPLING_H #define QTLABELCOUPLING_H #include "QtWidgetCoupling.h" #include "SNAPQtCommon.h" #include #include #include template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: virtual TAtomic GetValue(QLabel *w) { std::istringstream iss(to_utf8(w->text())); TAtomic value; iss >> value; return value; } virtual void SetValue(QLabel *w, const TAtomic &value) { std::ostringstream oss; oss << value; w->setText(from_utf8(oss.str())); } virtual void SetValueToNull(QLabel *w) { w->setText(""); } virtual const char *GetSignal() { return NULL; } }; #endif // QTLABELCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtLineEditCoupling.h000066400000000000000000000055501263013355200216400ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTLINEEDITCOUPLING_H #define QTLINEEDITCOUPLING_H #include "QtWidgetCoupling.h" #include "SNAPQtCommon.h" #include #include #include template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: virtual TAtomic GetValue(QLineEdit *w) { std::istringstream iss(to_utf8(w->text())); TAtomic value; iss >> value; return value; } virtual void SetValue(QLineEdit *w, const TAtomic &value) { std::ostringstream oss; oss << value; w->setText(from_utf8(oss.str())); } virtual void SetValueToNull(QLineEdit *w) { w->setText(""); } virtual const char *GetSignal() { return SIGNAL(textChanged(const QString &)); } }; template<> class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: virtual std::string GetValue(QLineEdit *w) { return to_utf8(w->text()); } virtual void SetValue(QLineEdit *w, const std::string &value) { w->setText(from_utf8(value)); } virtual void SetValueToNull(QLineEdit *w) { w->setText(""); } virtual const char *GetSignal() { return SIGNAL(textChanged(const QString &)); } }; /** Base class for traits that map between a numeric value and a text editor */ template class FixedPrecisionRealToTextFieldWidgetTraits : public DefaultWidgetValueTraits { public: FixedPrecisionRealToTextFieldWidgetTraits(int precision) : m_Precision(precision) {} irisGetSetMacro(Precision, int) virtual void SetValue(QLineEdit *w, const TAtomic &value) { std::ostringstream oss; oss << std::setprecision(m_Precision) << value; w->setText(oss.str().c_str()); } protected: int m_Precision; }; #endif // QTLINEEDITCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtListWidgetCoupling.h000066400000000000000000000070651263013355200222250ustar00rootroot00000000000000 #ifndef QTLISTWIDGETCOUPLING_H #define QTLISTWIDGETCOUPLING_H #include #include "SNAPQtCommon.h" #include #include /** Default traits for mapping a numeric value (or any sort of key, actually) to a row in a list box */ template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: // Get the Qt signal that the widget fires when its value has changed. The // value here is the selected item in the combo box. const char *GetSignal() { return SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)); } TAtomic GetValue(QListWidget *w) { // Get the UserData associated with the current item return w->currentItem()->data(Qt::UserRole).value(); } void SetValue(QListWidget *w, const TAtomic &value) { // Unset the current index int row = -1; // We have to actually find the item for(int i = 0; i < w->count(); i++) { QModelIndex idx = w->model()->index(i, 0); if(w->model()->data(idx, Qt::UserRole).value() == value) { row = i; break; } } // Have we found it? w->setCurrentRow(row); } void SetValueToNull(QListWidget *w) { w->setCurrentRow(-1); } }; template class ListWidgetRowTraitsBase { public: static void removeAll(QListWidget *w) { w->clear(); } static int getNumberOfRows(QListWidget *w) { return w->count(); } static TAtomic getValueInRow(QListWidget *w, int i) { return w->item(i)->data(Qt::UserRole).value(); } }; /** Row traits for mapping a color label into a list widget entry */ class ColorLabelToListWidgetTraits : public ListWidgetRowTraitsBase { public: static void appendRow(QListWidget *w, LabelType label, const ColorLabel &cl) { // The description QString text(cl.GetLabel()); // The color QColor fill(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2)); // Icon based on the color QIcon ic = CreateColorBoxIcon(16, 16, fill); // Create item and set its properties QListWidgetItem *item = new QListWidgetItem(ic, text, w); item->setData(Qt::UserRole, label); item->setData(Qt::UserRole + 1, fill); item->setData(Qt::EditRole, text); // item->setFlags(item->flags() | Qt::ItemIsEditable); } static void updateRowDescription(QListWidget *w, int index, const ColorLabel &cl) { // Get the current item QListWidgetItem *item = w->item(index); // Get the properies and compare them to the color label QColor currentFill = item->data(Qt::UserRole+1).value(); QColor newFill(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2)); if(currentFill != newFill) { QIcon ic = CreateColorBoxIcon(16, 16, newFill); item->setIcon(ic); item->setData(Qt::UserRole + 1, newFill); } QString currentText = item->text(); QString newText(cl.GetLabel()); if(currentText != newText) { item->setText(newText); item->setData(Qt::EditRole, newText); } } }; // Define the defaults template class DefaultWidgetDomainTraits : public ItemSetWidgetDomainTraits { }; #endif // QTLISTWIDGETCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtPagedWidgetCoupling.h000066400000000000000000000050351263013355200223250ustar00rootroot00000000000000#ifndef QTPAGEDWIDGETCOUPLING_H #define QTPAGEDWIDGETCOUPLING_H #include #include #include #include /** * Coupling for widgets such as Stack, Tab, etc. Each value of the * coupled variable corresponds to a page in the widget */ template struct PagedWidgetValueTraits : public WidgetValueTraitsBase { public: // There needs to be a map from the values of the atomic variable to // the pages in the widget typedef std::map PageMap; typedef typename PageMap::iterator PageIterator; PagedWidgetValueTraits(PageMap pm) : m_PageMap(pm) {} // The signal fired when the active page is changed virtual const char *GetSignal() { return SIGNAL(currentChanged(int)); } TAtomic GetValue(TPagedWidget *w) { // Figure out which button is checked for(PageIterator it = m_PageMap.begin(); it != m_PageMap.end(); ++it) { if(it->second == w->currentWidget()) return it->first; } // This is ambiguous... return static_cast(0); } void SetValue(TPagedWidget *w, const TAtomic &value) { // Set all the buttons QWidget *page = m_PageMap[value]; w->setCurrentWidget(page); } void SetValueToNull(TPagedWidget *w) { // Set all the buttons w->setCurrentIndex(-1); } protected: PageMap m_PageMap; }; template class DefaultPagedWidgetDomainTraits : public WidgetDomainTraitsBase { public: // With a trivial domain, do nothing! virtual void SetDomain(TPagedWidget *w, const TDomain &) {} virtual TDomain GetDomain(TPagedWidget *w) { return TDomain(); } }; template class DefaultWidgetDomainTraits : public DefaultPagedWidgetDomainTraits { }; template class DefaultWidgetDomainTraits : public DefaultPagedWidgetDomainTraits { }; template void makePagedWidgetCoupling( TPageWidget *w, TModel *model, std::map pageMap, QtCouplingOptions opts = QtCouplingOptions()) { typedef typename TModel::ValueType ValueType; typedef PagedWidgetValueTraits WidgetValueTraits; WidgetValueTraits traits(pageMap); makeCoupling(w, model, traits, opts); } #endif // QTPAGEDWIDGETCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtRadioButtonCoupling.h000066400000000000000000000047271263013355200224020ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTRADIOBUTTONCOUPLING_H #define QTRADIOBUTTONCOUPLING_H #include #include #include /** Create a coupling between a widget containing a set of radio buttons and a set of values of type TAtomic (true/false, enum, integer, etc). The mapping of values to button widgets is provided in the third parameter. */ template void makeRadioGroupCoupling( QWidget *parentWidget, std::map buttonMap, AbstractPropertyModel *model) { makeCheckableWidgetGroupCoupling(parentWidget, buttonMap, model); } /** Create a coupling between a widget containing a set of radio buttons and an enum. The values of the enum must be 0,1,2,... The order of the radio button widgets in the parent widget *w should match the order of the enum entries. This method only fits specific situations, in other cases see the more general version above */ template void makeRadioGroupCoupling( QWidget *w, AbstractPropertyModel *model) { const QObjectList &kids = w->children(); std::map buttonMap; int iwidget = 0; for(QObjectList::const_iterator it = kids.begin(); it != kids.end(); ++it) { QAbstractButton *qab = dynamic_cast(*it); if(qab) buttonMap[static_cast(iwidget++)] = qab; } makeCheckableWidgetGroupCoupling(w, buttonMap, model); } #endif // QTRADIOBUTTONCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtScrollbarCoupling.h000066400000000000000000000042131263013355200220610ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTSCROLLBARCOUPLING_H #define QTSCROLLBARCOUPLING_H #include #include template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(valueChanged(int)); } TAtomic GetValue(QScrollBar *w) { return static_cast(w->value()); } void SetValue(QScrollBar *w, const TAtomic &value) { w->setValue(static_cast(value)); } }; template class DefaultWidgetDomainTraits, QScrollBar> : public WidgetDomainTraitsBase, QScrollBar *> { public: void SetDomain(QScrollBar *w, const NumericValueRange &range) { w->setMinimum(range.Minimum); w->setMaximum(range.Maximum); w->setSingleStep(range.StepSize); } NumericValueRange GetDomain(QScrollBar *w) { return NumericValueRange( static_cast(w->minimum()), static_cast(w->maximum()), static_cast(w->singleStep())); } }; #endif // QTSCROLLBARCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtSliderCoupling.h000066400000000000000000000064531263013355200213700ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTSLIDERCOUPLING_H #define QTSLIDERCOUPLING_H #include #include #include template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(valueChanged(int)); } TAtomic GetValue(QSlider *w) { return static_cast(w->value()); } void SetValue(QSlider *w, const TAtomic &value) { w->setValue(static_cast(value)); } virtual void SetValueToNull(QSlider *w) { w->setValue(w->minimum()); } }; template class DefaultWidgetDomainTraits, QSlider> : public WidgetDomainTraitsBase, QSlider *> { public: void SetDomain(QSlider *w, const NumericValueRange &range) { w->setMinimum(range.Minimum); w->setMaximum(range.Maximum); w->setSingleStep(range.StepSize); } NumericValueRange GetDomain(QSlider *w) { return NumericValueRange( static_cast(w->minimum()), static_cast(w->maximum()), static_cast(w->singleStep())); } }; template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(valueChanged(int)); } TAtomic GetValue(QDoubleSlider *w) { return static_cast(w->doubleValue()); } void SetValue(QDoubleSlider *w, const TAtomic &value) { w->setDoubleValue(static_cast(value)); } }; template class DefaultWidgetDomainTraits, QDoubleSlider> : public WidgetDomainTraitsBase, QDoubleSlider *> { public: void SetDomain(QDoubleSlider *w, const NumericValueRange &range) { w->setDoubleMinimum(range.Minimum); w->setDoubleMaximum(range.Maximum); w->setDoubleSingleStep(range.StepSize); } NumericValueRange GetDomain(QDoubleSlider *w) { return NumericValueRange( static_cast(w->doubleMinimum()), static_cast(w->doubleMaximum()), static_cast(w->doubleSingleStep())); } }; #endif // QTSLIDERCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtSpinBoxCoupling.h000066400000000000000000000045251263013355200215260ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTSPINBOXCOUPLING_H #define QTSPINBOXCOUPLING_H #include #include /** Default traits for the Qt Spin Box, coupled with a numeric value range */ template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: const char *GetSignal() { return SIGNAL(valueChanged(int)); } TAtomic GetValue(QSpinBox *w) { return static_cast(w->value()); } void SetValue(QSpinBox *w, const TAtomic &value) { w->setSpecialValueText(""); w->setValue(static_cast(value)); } void SetValueToNull(QSpinBox *w) { w->setValue(w->minimum()); w->setSpecialValueText(" "); } }; template class DefaultWidgetDomainTraits, QSpinBox> : public WidgetDomainTraitsBase, QSpinBox *> { public: void SetDomain(QSpinBox *w, const NumericValueRange &range) { w->setMinimum(range.Minimum); w->setMaximum(range.Maximum); w->setSingleStep(range.StepSize); } NumericValueRange GetDomain(QSpinBox *w) { return NumericValueRange( static_cast(w->minimum()), static_cast(w->maximum()), static_cast(w->singleStep())); } }; #endif // QTSPINBOXCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtTableWidgetCoupling.cxx000066400000000000000000000001271263013355200227040ustar00rootroot00000000000000#include "QtTableWidgetCoupling.h" QtTableWidgetCoupling::QtTableWidgetCoupling() { } itksnap-3.4.0/GUI/Qt/Coupling/QtTableWidgetCoupling.h000066400000000000000000000175571263013355200223500ustar00rootroot00000000000000#ifndef QTTABLEWIDGETCOUPLING_H #define QTTABLEWIDGETCOUPLING_H #ifndef QTLISTWIDGETCOUPLING_H #define QTLISTWIDGETCOUPLING_H #include #include "SNAPQtCommon.h" #include #include /** Default traits for mapping a numeric value (or any sort of key, actually) to a row in a table. This is similar to a list box, but with multiple columns. TODO: unify this with ListWidget traits by using a Qt AbstractItemModel This coupling maps an integer value to a row index in a table. The value in the table are specified by the domain of the associated model. */ template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: // Get the Qt signal that the widget fires when its value has changed. The // value here is the selected item in the combo box. const char *GetSignal() { return SIGNAL(currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)); } TAtomic GetValue(QTableWidget *w) { // Get the UserData associated with the current item TAtomic val = w->currentItem()->data(Qt::UserRole).value(); return val; } void SetValue(QTableWidget *w, const TAtomic &value) { // We have to actually find the row for(int i = 0; i < w->rowCount(); i++) { QTableWidgetItem *item = w->item(i, 0); TAtomic stored_value = item->data(Qt::UserRole).value(); if(stored_value == value) { w->setCurrentItem(item); return; } } // Have we found it? w->setCurrentItem(NULL); } void SetValueToNull(QTableWidget *w) { // Have we found it? w->setCurrentItem(NULL); } }; /** Default traits for mapping a matrix of values (int, double) to a table */ template class DefaultWidgetValueTraits< vnl_matrix, QTableWidget> : public WidgetValueTraitsBase< vnl_matrix, QTableWidget* > { public: typedef vnl_matrix TMatrix; const char *GetSignal() { return SIGNAL(cellChanged(int, int)); } TMatrix GetValue(QTableWidget *w) { // Extract the values from all cells in the widget TMatrix mat(w->rowCount(), w->columnCount()); for(int r = 0; r < w->rowCount(); r++) { for(int c = 0; c < w->rowCount(); c++) { TElement val = w->item(r, c)->data(Qt::DisplayRole).value(); mat(r, c) = val; } } return mat; } void SetValue(QTableWidget *w, const TMatrix &mat) { // Adjust the sizes if(w->rowCount() != mat.rows() || w->columnCount() != mat.columns()) { w->setRowCount(mat.rows()); w->setColumnCount(mat.columns()); } // Assign the values for(int r = 0; r < w->rowCount(); r++) { for(int c = 0; c < w->rowCount(); c++) { TElement newval = mat(r,c); QTableWidgetItem *item = w->item(r, c); if(!item) { item = new QTableWidgetItem(); w->setItem(r, c, item); item->setData(Qt::DisplayRole, QVariant(newval)); } else { QVariant oldval = item->data(Qt::DisplayRole); if(oldval.isNull() || oldval.value() != newval) { item->setData(Qt::DisplayRole, QVariant(newval)); } } } } } void SetValueToNull(QTableWidget *w) { // Have we found it? w->clearContents(); } }; template class TextAndIconTableWidgetRowTraits { public: static void removeAll(QTableWidget *w) { while(w->rowCount()) w->removeRow(0); } static int getNumberOfRows(QTableWidget *w) { return w->rowCount(); } static TAtomic getValueInRow(QTableWidget *w, int i) { TAtomic value = w->item(i,0)->data(Qt::UserRole).value(); return value; } static void appendRow(QTableWidget *w, TAtomic value, const TDesc &desc) { // Insert a row int row = w->rowCount(); w->insertRow(row); // Fill all the columns for(int col = 0; col < w->columnCount(); col++) { // The description QString text = TItemDesriptionTraits::GetText(value, desc, col); // The icon QIcon icon = TItemDesriptionTraits::GetIcon(value, desc, col); // The icon signature - a value that can be used to check if the icon has changed QVariant iconSig = TItemDesriptionTraits::GetIconSignature(value, desc, col); // Set the item properties QTableWidgetItem *item = new QTableWidgetItem(); item->setText(text); item->setToolTip(text); item->setIcon(icon); item->setData(Qt::UserRole + 1, iconSig); w->setItem(row, col, item); } // Set the item for the row w->item(row, 0)->setData(Qt::UserRole, QVariant::fromValue(value)); } static void updateRowDescription(QTableWidget *w, int row, const TDesc &desc) { // The current value TAtomic value = w->item(row, 0)->data(Qt::UserRole).value(); // Get the properies and compare them to the color label for(int col = 0; col < w->columnCount(); col++) { QTableWidgetItem *item = w->item(row, col); QVariant currentIconSig = item->data(Qt::UserRole + 1); QVariant newIconSig = TItemDesriptionTraits::GetIconSignature(value, desc, col); if(currentIconSig != newIconSig) { QIcon ic = TItemDesriptionTraits::GetIcon(value, desc, col); item->setIcon(ic); item->setData(Qt::UserRole + 1, newIconSig); } QString currentText = item->text(); QString newText = TItemDesriptionTraits::GetText(value, desc, col); if(currentText != newText) { item->setText(newText); item->setToolTip(newText); } } } }; /** * Declare ItemSetWidgetDomainTraits to be the default domain traits for the * QTableWidget, which makes it easy to use with makeMultiRowCoupling */ template class DefaultMultiRowWidgetDomainTraits : public ItemSetWidgetDomainTraits {}; /** Row traits for mapping a color label into a list widget entry class ColorLabelToListWidgetTraits : public ListWidgetRowTraitsBase { public: static void appendRow(QTableWidget *w, LabelType label, const ColorLabel &cl) { // The description QString text(cl.GetLabel()); // The color QColor fill(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2)); // Icon based on the color QIcon ic = CreateColorBoxIcon(16, 16, fill); // Create item and set its properties QTableWidgetItem *item = new QTableWidgetItem(ic, text, w); item->setData(Qt::UserRole, label); item->setData(Qt::UserRole + 1, fill); item->setData(Qt::EditRole, text); // item->setFlags(item->flags() | Qt::ItemIsEditable); } static void updateRowDescription(QTableWidget *w, int index, const ColorLabel &cl) { // Get the current item QTableWidgetItem *item = w->item(index); // Get the properies and compare them to the color label QColor currentFill = item->data(Qt::UserRole+1).value(); QColor newFill(cl.GetRGB(0), cl.GetRGB(1), cl.GetRGB(2)); if(currentFill != newFill) { QIcon ic = CreateColorBoxIcon(16, 16, newFill); item->setIcon(ic); item->setData(Qt::UserRole + 1, newFill); } QString currentText = item->text(); QString newText(cl.GetLabel()); if(currentText != newText) { item->setText(newText); item->setData(Qt::EditRole, newText); } } }; // Define the defaults template class DefaultWidgetDomainTraits : public ItemSetWidgetDomainTraits { }; */ #endif // QTLISTWIDGETCOUPLING_H #endif // QTTABLEWIDGETCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtToolbarCoupling.h000066400000000000000000000077241263013355200215520ustar00rootroot00000000000000#ifndef QTTOOLBARCOUPLING_H #define QTTOOLBARCOUPLING_H #include "QtWidgetCoupling.h" #include "QToolBar" #include "QAbstractButton" #include #include /** * This class defines a coupling between a QToolbar in which there are a * number of mutually exclusive actions, which are assigned a value of the * TAtomic type using the 'data' parameter (using QAction::setData). */ template class DefaultWidgetValueTraits : public WidgetValueTraitsBase { public: // Luckily a QToolBar provides a signal for us to use virtual const char *GetSignal() { return SIGNAL(actionTriggered(QAction *)); } // We must trigger the action that corresponds to the value. For this // to work, we must set the user data of the action to the value virtual void SetValue(QToolBar *w, const TAtomic &value) { foreach(QAction *action, w->actions()) { if(value == action->data().value()) action->setChecked(true); else action->setChecked(false); } } // Get the value - i.e., the action that is currently checked virtual TAtomic GetValue(QToolBar *w) { foreach(QAction *action, w->actions()) if(action->isChecked()) return action->data().value(); return 0; } // Setting value to NULL means unchecking all actions virtual void SetValueToNull(QToolBar *w) { foreach(QAction *action, w->actions()) { action->setChecked(false); } } }; /** * This class populates an action group to match a domain that is a set of * items. The actions in the action group are dynamically allocated */ /* template class DefaultMultiRowWidgetDomainTraits : public WidgetDomainTraitsBase { // The information about the item type are taken from the domain typedef typename TItemDomain::ValueType AtomicType; typedef typename TItemDomain::DescriptorType DescriptorType; typedef TItemDomain DomainType; void SetDomain(QToolBar *w, const DomainType &domain) { // Remove all the actions from the toolbar w->clear(); // Create an action group unless one already exists in the widget QActionGroup *ag = w->findChild(); if(ag == NULL) ag = new QActionGroup(w); // Populate the toolbar with actions for(typename DomainType::const_iterator it = domain.begin(); it != domain.end(); ++it) { // Get the key/value pair AtomicType value = domain.GetValue(it); const DescriptorType &row = domain.GetDescription(it); // Create an action and assign it to the action group QAction *action = new QAction(w); action->setActionGroup(ag); action->setCheckable(true); action->setData(QVariant::fromValue(value)); // Use the row traits to map information to the widget TRowTraits::updateAction(action, row); } } void UpdateDomainDescription(QToolBar *w, const DomainType &domain) { // Update each of the actions foreach(QAction *action, w->actions()) { AtomicType value = action->data().value(); typename DomainType::const_iterator it = domain.find(value); if(it != domain.end()) { TRowTraits::updateAction(action, domain.GetDescription(it)); } } } TItemDomain GetDomain(QToolBar *w) { // We don't actually pull the widget because the domain is fully specified // by the model. return DomainType(); } }; */ /** * A row traits that populates a QAction from a color label * * Use in conjunction with makeMultiRowCoupling */ /* class ColorLabelToQActionRowTraits { public: static void updateAction(QAction *action, const ColorLabel &label) { action->setIcon(CreateColorBoxIcon(16, 16, GetBrushForColorLabel(label))); action->setToolTip(QString::fromUtf8(label.GetLabel())); } }; */ #endif // QTTOOLBARCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtWidgetArrayCoupling.h000066400000000000000000000320031263013355200223560ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTWIDGETARRAYCOUPLING_H #define QTWIDGETARRAYCOUPLING_H #include /** This class allows widget traits to be extended to an array of widgets. It uses a child traits object to map between an iris_vector_fixed and an array of widgets. */ template class WidgetArrayValueTraits : public WidgetValueTraitsBase< iris_vector_fixed, std::vector > { public: typedef iris_vector_fixed ValueType; typedef std::vector WidgetArrayType; /** Constructor, takes the "child" traits object, i.e., the traits for the individual widgets in the array */ WidgetArrayValueTraits(ChildTraits childTraits) : m_ChildTraits(childTraits) { m_CacheValid.fill(false); } ValueType GetValue(WidgetArrayType wa) { ValueType value = m_CachedModelValue; for(unsigned int i = 0; i < VDim; i++) { TAtomic valWidget = m_ChildTraits.GetValue(wa[i]); if(!m_CacheValid[i] || valWidget != m_CachedWidgetValue[i]) { value(i) = valWidget; m_CacheValid[i] = false; } } return value; } void SetValue(WidgetArrayType wa, const ValueType &value) { for(unsigned int i = 0; i < VDim; i++) { m_ChildTraits.SetValue(wa[i], value(i)); m_CachedModelValue[i] = value(i); m_CachedWidgetValue[i] = m_ChildTraits.GetValue(wa[i]); m_CacheValid[i] = true; } } void SetValueToNull(WidgetArrayType wa) { for(unsigned int i = 0; i < VDim; i++) m_ChildTraits.SetValueToNull(wa[i]); m_CacheValid.fill(false); } const char *GetSignal() { return m_ChildTraits.GetSignal(); } protected: ChildTraits m_ChildTraits; // We must cache the values sent to each widget and the corresponding states // of each widget. This is because some widgets change the value passed to // them. If one of the widgets in the array is edited by the user, we only // want that widget's value to be sent to the model, while using the cached // values for the other widgets. ValueType m_CachedModelValue, m_CachedWidgetValue; iris_vector_fixed m_CacheValid; }; /** Before defining a vectorized domain traits object, we need to define some traits that describe how different kinds of domains map between atomic and vectorized versions. */ template class DomainVectorExpansion { }; template class DomainVectorExpansion, VDim> { public: typedef iris_vector_fixed VectorType; typedef NumericValueRange VectorDomainType; typedef NumericValueRange AtomicDomainType; static AtomicDomainType GetNthElement( const VectorDomainType &dvec, unsigned int n) { return AtomicDomainType( dvec.Minimum(n), dvec.Maximum(n), dvec.StepSize(n)); } static void UpdateNthElement( VectorDomainType &dvec, unsigned int n, const AtomicDomainType &dat) { dvec.Minimum(n) = dat.Minimum; dvec.Maximum(n) = dat.Maximum; dvec.StepSize(n) = dat.StepSize; } }; template class DomainVectorExpansion { public: typedef TrivialDomain VectorDomainType; typedef TrivialDomain AtomicDomainType; static AtomicDomainType GetNthElement( const VectorDomainType &dvec, unsigned int n) { return AtomicDomainType(); } static void UpdateNthElement( VectorDomainType &dvec, unsigned int n, const AtomicDomainType &dat) {} }; template class ComponentDomainTraits { }; template class ComponentDomainTraits< NumericValueRange >, VDim> { public: typedef iris_vector_fixed VectorType; typedef NumericValueRange AtomicDomainType; typedef NumericValueRange VectorDomainType; static AtomicDomainType GetNthElement( const VectorDomainType &dvec, unsigned int n) { return AtomicDomainType( dvec.Minimum(n), dvec.Maximum(n), dvec.StepSize(n)); } static void UpdateNthElement( VectorDomainType &dvec, unsigned int n, const AtomicDomainType &dat) { dvec.Minimum(n) = dat.Minimum; dvec.Maximum(n) = dat.Maximum; dvec.StepSize(n) = dat.StepSize; } }; template class ComponentDomainTraits { public: typedef TrivialDomain AtomicDomainType; typedef TrivialDomain VectorDomainType; static AtomicDomainType GetNthElement( const VectorDomainType &dvec, unsigned int n) { return AtomicDomainType(); } static void UpdateNthElement( VectorDomainType &dvec, unsigned int n, const AtomicDomainType &dat) {} }; template class WidgetArrayDomainTraits : public WidgetDomainTraitsBase > { public: typedef ComponentDomainTraits ComponentTraitsType; typedef TVectorDomain VectorDomainType; typedef typename ComponentTraitsType::AtomicDomainType AtomicDomainType; typedef std::vector WidgetArrayType; /** Constructor, takes the "child" traits object, i.e., the traits for the individual widgets in the array */ WidgetArrayDomainTraits(ChildTraits childTraits) : m_ChildTraits(childTraits) {} void SetDomain(WidgetArrayType wa, const VectorDomainType &range) { for(unsigned int i = 0; i < VDim; i++) { AtomicDomainType di = ComponentTraitsType::GetNthElement(range, i); m_ChildTraits.SetDomain(wa[i], di); } } VectorDomainType GetDomain(WidgetArrayType wa) { VectorDomainType range; for(unsigned int i = 0; i < VDim; i++) { AtomicDomainType ri = m_ChildTraits.GetDomain(wa[i]); ComponentTraitsType::UpdateNthElement(range, i, ri); } return range; } protected: ChildTraits m_ChildTraits; }; /** Create a coupling between an model whose value is of a vector type and an array of widgets of the same type. For example, this function allows you to hook up a model wrapped around a Vector3d to a triple of spin boxes. This is very convenient for dealing with input and output of vector data. */ template class DefaultComponentValueTraits : public DefaultWidgetValueTraits< typename TModel::ValueType::element_type, TWidget> { }; template class DefaultComponentDomainTraits : public DefaultWidgetDomainTraits< typename ComponentDomainTraits::AtomicDomainType, TWidget> { }; /** Create a coupling between a model and an array of widgets. See the more convenient versions of this method below */ template void makeWidgetArrayCoupling( std::vector wa, TModel *model, WidgetValueTraits trValue, WidgetDomainTraits trDomain) { typedef std::vector WidgetArray; typedef typename TModel::ValueType VectorType; typedef typename TModel::DomainType VectorDomainType; typedef typename VectorType::element_type ElementType; const int VDim = VectorType::SIZE; // Define the array traits typedef WidgetArrayValueTraits< ElementType, VDim, TWidget, WidgetValueTraits> ArrayValueTraits; typedef WidgetArrayDomainTraits< VectorDomainType, VDim, TWidget, WidgetDomainTraits> ArrayDomainTraits; // The class of the mapping typedef PropertyModelToWidgetDataMapping< TModel, WidgetArray, ArrayValueTraits, ArrayDomainTraits> MappingType; // Create the mapping MappingType *mapping = new MappingType( wa, model, ArrayValueTraits(trValue), ArrayDomainTraits(trDomain)); // Create the coupling helper (event handler). It's attached to the first // widget, just for the purpose of this object being deleted later. QtCouplingHelper *h = new QtCouplingHelper(wa.front(), mapping); // Populate the widget (force the domain and value to be copied) mapping->InitializeWidgetFromModel(); // Listen to value change events from the model LatentITKEventNotifier::connect( model, ValueChangedEvent(), h, SLOT(onPropertyModification(const EventBucket &))); LatentITKEventNotifier::connect( model, DomainChangedEvent(), h, SLOT(onPropertyModification(const EventBucket &))); LatentITKEventNotifier::connect( model, DomainDescriptionChangedEvent(), h, SLOT(onPropertyModification(const EventBucket &))); // Listen to value change events for this widget for(int i = 0; i < VDim; i++) { h->connect(trValue.GetSignalEmitter(wa[i]), trValue.GetSignal(), SLOT(onUserModification())); } } /** Create a coupling between a model and a triplet of widgets. The model must be an AbstractPropertyModel templated over iris_vector_fixed and some compatible Domain object, i.e., NumericValueRange or TrivialDomain. The caller can optionally pass traits objects for overriding the default behavior of model-to-widget copying of values and domain information. */ template void makeArrayCoupling( TWidget *w1, TWidget *w2, TWidget *w3, TModel *model, WidgetValueTraits trValue, WidgetDomainTraits trDomain) { // Create the array of widgets typedef std::vector WidgetArray; WidgetArray wa(3); wa[0] = w1; wa[1] = w2; wa[2] = w3; // Call the parent method makeWidgetArrayCoupling< TWidget,TModel,WidgetValueTraits,WidgetDomainTraits>( wa, model, trValue, trDomain); } template void makeArrayCoupling( TWidget *w1, TWidget *w2, TWidget *w3, TModel *model, WidgetValueTraits trValue) { typedef DefaultComponentDomainTraits WidgetDomainTraits; makeArrayCoupling (w1,w2,w3,model,trValue,WidgetDomainTraits()); } template void makeArrayCoupling( TWidget *w1, TWidget *w2, TWidget *w3, TModel *model) { typedef DefaultComponentValueTraits WidgetValueTraits; makeArrayCoupling (w1,w2,w3,model,WidgetValueTraits()); } /** Create a coupling between a model and a pair of widgets. The model must be an AbstractPropertyModel templated over iris_vector_fixed and some compatible Domain object, i.e., NumericValueRange or TrivialDomain. The caller can optionally pass traits objects for overriding the default behavior of model-to-widget copying of values and domain information. */ template void makeArrayCoupling( TWidget *w1, TWidget *w2, TModel *model, WidgetValueTraits trValue, WidgetDomainTraits trDomain) { // Create the array of widgets typedef std::vector WidgetArray; WidgetArray wa(2); wa[0] = w1; wa[1] = w2; // Call the parent method makeWidgetArrayCoupling< TWidget,TModel,WidgetValueTraits,WidgetDomainTraits>( wa, model, trValue, trDomain); } template void makeArrayCoupling( TWidget *w1, TWidget *w2, TModel *model, WidgetValueTraits trValue) { typedef DefaultComponentDomainTraits WidgetDomainTraits; makeArrayCoupling (w1,w2,model,trValue,WidgetDomainTraits()); } template void makeArrayCoupling( TWidget *w1, TWidget *w2, TModel *model) { typedef DefaultComponentValueTraits WidgetValueTraits; makeArrayCoupling (w1,w2,model,WidgetValueTraits()); } #endif // QTWIDGETARRAYCOUPLING_H itksnap-3.4.0/GUI/Qt/Coupling/QtWidgetCoupling.h000066400000000000000000000611541263013355200213700ustar00rootroot00000000000000/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Filename.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ Copyright (c) 2011 Paul A. Yushkevich This file is part of ITK-SNAP ITK-SNAP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . =========================================================================*/ #ifndef QTWIDGETCOUPLING_H #define QTWIDGETCOUPLING_H #include #include #include #include #include #include #include "QtWidgetActivator.h" /** Abstract class for hierarchy of data mappings between widgets and models. The mapping is created when a model is coupled to a widget. It handles mapping data from the model to the widget (when the model executes an appropriate event) and from the widget to the model. This abstract interface is generic and does not assume anything about the model or the widget. */ class AbstractWidgetDataMapping { public: virtual ~AbstractWidgetDataMapping() {} // Called the first time the widget is coupled to the model virtual void InitializeWidgetFromModel() = 0; // Called when the widget is changed virtual void UpdateModelFromWidget() = 0; // Called when the model is changed (event bucket contains the events that // have occured to the model, so we know what aspects of the widget need to // be updated) virtual void UpdateWidgetFromModel(const EventBucket &bucket) = 0; }; /** Data mapping between a property model (encapsulating a value and a domain) and an input widget of class TWidget. The behavior of this class is controlled by the template parameters WidgetValueTraits and WidgetDomainTraits, which describe how values and domains are mapped to the specific widget. The traits classes for common Qt widgets are in header files QtXXXCoupling.h */ template class PropertyModelToWidgetDataMapping : public AbstractWidgetDataMapping { public: typedef TModel ModelType; typedef typename ModelType::ValueType AtomicType; typedef typename ModelType::DomainType DomainType; /** * Constructor: set all the parameters of the mapping */ PropertyModelToWidgetDataMapping( TWidgetPtr w, ModelType *model, WidgetValueTraits valueTraits, WidgetDomainTraits domainTraits) : m_Widget(w), m_Model(model), m_Updating(false), m_ValueTraits(valueTraits), m_DomainTraits(domainTraits), m_CachedValueAvailable(false), m_CachedDomainAvailable(false), m_AllowUpdateInInvalidState(false), m_LastBucketMTime(0) {} /** * Called the first time the widget is coupled to the model in order to * populate the widget from the model. */ void InitializeWidgetFromModel() { // Call the update method, including updating the domain this->DoUpdateWidgetFromModel(true, false); } /** * Whether the user can update the model when it is in invalid state */ irisSetMacro(AllowUpdateInInvalidState, bool) /** * Respond to a change in the model */ void UpdateWidgetFromModel(const EventBucket &bucket) { // Make sure we don't process the same event bucket twice if(bucket.GetMTime() <= m_LastBucketMTime) return; // The bucket parameter tells us whether the domain changed or not this->DoUpdateWidgetFromModel( bucket.HasEvent(DomainChangedEvent()), bucket.HasEvent(DomainDescriptionChangedEvent())); // Store the bucket id m_LastBucketMTime = bucket.GetMTime(); } /** * Respond to the user changing the value in the widget */ void UpdateModelFromWidget() { if(!m_Updating) { AtomicType user_value = m_ValueTraits.GetValue(m_Widget); AtomicType model_value; // Get the current value and status from the model // TODO: this information could be cached, and dirtied in the case of // an event. Currently, we are doing a lot of unnecessary accesses to // the model just to check hte validity and current value of th model bool valid = m_Model->GetValueAndDomain(model_value, NULL); // Note: if the model reports that the value is invalid, we are not // allowing the user to mess with the value. This may have some odd // consequences. We need to investigate. if((valid && model_value != user_value) || (!valid && m_AllowUpdateInInvalidState)) { m_Model->SetValue(user_value); m_CachedWidgetValue = user_value; m_CachedValueAvailable = true; } } } protected: void DoUpdateWidgetFromModel(bool flagDomainChange, bool flagDomainDescriptionChange) { m_Updating = true; // The value is always needed AtomicType value; // The domain should only be updated in the bucket contains the range // event (the target range has been modified) DomainType *domptr = NULL; if(flagDomainChange || flagDomainDescriptionChange) { m_DomainTemp = m_DomainTraits.GetDomain(m_Widget); domptr = &m_DomainTemp; } // Obtain the value from the model if(m_Model->GetValueAndDomain(value, domptr)) { // Update the domain if necessary. The updates to the domain never come // in response to the user interaction with m_Widget, so it's safe to // just call SetDomain without first checking if the change is 'real'. if(flagDomainChange) { if(!m_CachedDomainAvailable || m_CachedWidgetDomain != m_DomainTemp) { m_DomainTraits.SetDomain(m_Widget, m_DomainTemp); // Once the domain changes, the current cached value can no longer be // trusted because the widget may have reconfigured. m_CachedValueAvailable = false; // Cache the domain if it is atomic (supports comparison operator) if(m_DomainTemp.isAtomic()) { m_CachedWidgetDomain = m_DomainTemp; m_CachedDomainAvailable = true; } } } else if(flagDomainDescriptionChange) { m_DomainTraits.UpdateDomainDescription(m_Widget, m_DomainTemp); } // Before setting the value, we should check it against the cached value if(!m_CachedValueAvailable || m_CachedWidgetValue != value) { m_ValueTraits.SetValue(m_Widget, value); m_CachedWidgetValue = value; m_CachedValueAvailable = true; } } else { m_ValueTraits.SetValueToNull(m_Widget); m_CachedValueAvailable = false; } m_Updating = false; } private: TWidgetPtr m_Widget; ModelType *m_Model; bool m_Updating; WidgetValueTraits m_ValueTraits; WidgetDomainTraits m_DomainTraits; // Whether the user can update the model when it is in invalid state bool m_AllowUpdateInInvalidState; // A specific property of the widget that should be toggled when the model // is in NULL state (visible/enabled) QString m_NullStateProperty; // A temporary copy of the domain DomainType m_DomainTemp; DomainType m_CachedWidgetDomain; // We cache the last known value in the widget to avoid unnecessary updates // of the widget in response to events generated from the model after the // model has been updated in response to the widget. AtomicType m_CachedWidgetValue; // Whether or not the cached value can be used bool m_CachedValueAvailable, m_CachedDomainAvailable; // The last bucked ID handled by this widget. This is used to protect against // updates being invoked twice with the same event bucket (seems like some // sort of a Qt weirdness) unsigned long m_LastBucketMTime; }; /** Base class for widget value traits. It specifies the methods that the traits classes must provide. See the QtXXXCoupling.h files for concrete implementations. */ template class WidgetValueTraitsBase { public: // Get the Qt signal that the widget fires when its value has changed // For example { return SIGNAL(triggered()); } virtual const char *GetSignal() { return NULL; } // Get the widget that generates the signal above. Normally it's the same // as the input widget, but in some cases, it's some other QObject that // fires the signal, i.e., in QAbstractItemView, it would be the item // selection model rather than the view itself virtual QObject *GetSignalEmitter(QObject *w) { return w; } // Get the value from the widget virtual TAtomic GetValue(TWidgetPtr) = 0; // Set the value of the widget virtual void SetValue(TWidgetPtr, const TAtomic &) = 0; // The default behavior for setting the widget to null is to do nothing. // This should be overridden by child traits classes virtual void SetValueToNull(TWidgetPtr) {} virtual ~WidgetValueTraitsBase() {} }; /** * Widget value traits that connect a boolean property of a Qt widget with * a boolean (or boolean-castable) value. These traits don't handle the * property having a null value - probably should be addressed in the future */ template class WidgetBooleanNamedPropertyTraits : public WidgetValueTraitsBase { public: WidgetBooleanNamedPropertyTraits(const char *propertyName, bool reverse) : m_Property(propertyName), m_Reverse(reverse) {} virtual TAtomic GetValue(QObject *w) { if(m_Reverse) return !qvariant_cast(w->property(m_Property.c_str())); else return qvariant_cast(w->property(m_Property.c_str())); } virtual void SetValue(QObject *w, const TAtomic &value) { if(m_Reverse) w->setProperty(m_Property.c_str(), !value); else w->setProperty(m_Property.c_str(), value); } protected: std::string m_Property; bool m_Reverse; }; /** Base class for widget domain traits. It specifies the methods that the traits classes must provide. See the QtXXXCoupling.h files for concrete implementations. */ template class WidgetDomainTraitsBase { public: /** Set the domain from the values in the model */ virtual void SetDomain(TWidgetPtr w, const TDomain &domain) = 0; /** Update the description of the domain from the values in the model, without changing the configuration of the domain. This is only relevant for some kinds of domains, such as lists of items. In this case, we need to change the presentation of the items to the user, while keeping the list of items intact. By default, this does nothing. */ virtual void UpdateDomainDescription(TWidgetPtr w, const TDomain &domain) {} /** Get the current value of the domain from the model. This is only needed in cases that the model does not provide full domain information (some information is coded in the widget by the developer). For widgets where the model fully specifies the domain, it's safe to just return a trivially initialized domain object. */ virtual TDomain GetDomain(TWidgetPtr w) = 0; virtual ~WidgetDomainTraitsBase() {} }; /** Empty template for default value traits. Specialize for different Qt widgets */ template class DefaultWidgetValueTraits {}; /** Empty template for default domain traits. Specialize for different Qt widgets */ template class DefaultWidgetDomainTraits {}; /** Default domain traits for models with a TrivialDomain. */ template class DefaultWidgetDomainTraits : public WidgetDomainTraitsBase { public: // With a trivial domain, do nothing! virtual void SetDomain(TWidget *w, const TrivialDomain &) {} virtual TrivialDomain GetDomain(TWidget *w) { return TrivialDomain(); } }; /** * Empty template for default domain traits for widgets that contain * multiple rows. Specialize for different Qt widgets. One way to specialize * is to derive from the class ItenSetWidgetDomainTraits below, although it * is not the only way to specialize. */ template class DefaultMultiRowWidgetDomainTraits {}; /** Domain traits for widgets like combo boxes, list views and tables, which present the user with a list of options, from which a single choice is selected. The actual widget-specific logic is handled by the TRowTraits template parameter, which defines how an option description provided by the model (TDesc) is appended as a row in the widget. TRowTraits should provide static methods appendRow and removeAll. The TItemDomain parameter needs to be a subclass of AbstractItemSetDomain or provide the same signature. */ template class ItemSetWidgetDomainTraits : public WidgetDomainTraitsBase { public: // The information about the item type are taken from the domain typedef typename TItemDomain::ValueType AtomicType; typedef typename TItemDomain::DescriptorType DescriptorType; typedef TItemDomain DomainType; void SetDomain(TWidget *w, const DomainType &domain) { // Remove all items from the widget TRowTraits::removeAll(w); // This is where we actually populate the widget for(typename DomainType::const_iterator it = domain.begin(); it != domain.end(); ++it) { // Get the key/value pair AtomicType value = domain.GetValue(it); const DescriptorType &row = domain.GetDescription(it); // Use the row traits to map information to the widget TRowTraits::appendRow(w, value, row); } } void UpdateDomainDescription(TWidget *w, const DomainType &domain) { // This is not the most efficient way of doing things, because we // are still linearly parsing through the widget and updating rows. // But at least the actual modifications to the widget are limited // to the rows that have been modified. // // What would be more efficient is to have a list of ids which have // been modified and update only those. Or even better, implement all // of this using an AbstractItemModel int nrows = TRowTraits::getNumberOfRows(w); for(int i = 0; i < nrows; i++) { AtomicType id = TRowTraits::getValueInRow(w, i); typename DomainType::const_iterator it = domain.find(id); if(it != domain.end()) { const DescriptorType &row = domain.GetDescription(it); TRowTraits::updateRowDescription(w, i, row); } } } TItemDomain GetDomain(TWidget *w) { // We don't actually pull the widget because the domain is fully specified // by the model. return DomainType(); } }; /** * This Qt object is used to maintain the coupling between a model and a widget. It is * memory managed by Qt and attached to the Qt widget that is involved in the coupling. */ class QtCouplingHelper : public QObject { Q_OBJECT public: explicit QtCouplingHelper(QObject *widget, AbstractWidgetDataMapping *dm) : QObject(widget) { m_DataMapping = dm; setObjectName(QString("CouplingHelper:%1").arg(widget->objectName())); } public slots: void onUserModification() { m_DataMapping->UpdateModelFromWidget(); } void onPropertyModification(const EventBucket &bucket) { m_DataMapping->UpdateWidgetFromModel(bucket); } protected: AbstractWidgetDataMapping *m_DataMapping; }; /** This class simplifies passing parameters to the makeCoupling functions. */ struct QtCouplingOptions { enum Option { NO_OPTIONS = 0x0, /** This flag disconnects the Widget -> Model direction of the coupling. The coupling will update the widget value and domain in response to changes in the model, but it will not update the model from changes in the widget. This is useful when we want to explicitly handle changes in the widget. For example, if we need to check the validity of the value and/or present a dialog box, we would use this option, and then set up an explicit slot to handle changes from the widget and update the model. */ UNIDIRECTIONAL = 0x1, /** * This flag makes it possible for the widget to write its value to the model * when the model is in invalid state. By default, this is disabled and any * changes by the user to a widget in invalid state are ignored, i.e. not * sent to the model. */ ALLOW_UPDATES_WHEN_INVALID = 0x2, /** * Whether the widget should be deactivated when the model is in Null state. * Passing this option to makeCoupling will create a widget activator for the * widget. Make sure no other activators are set on the widget */ DEACTIVATE_WHEN_INVALID = 0x4 }; Q_DECLARE_FLAGS(Options, Option) irisGetSetMacro(Options, Options) irisGetSetMacro(SignalOverride, const char *) bool IsUnidirectional() { return m_Options.testFlag(UNIDIRECTIONAL); } bool IsUpdateAllowedWhenInvalid() { return m_Options.testFlag(ALLOW_UPDATES_WHEN_INVALID); } bool IsDeactivatedWhenInvalid() { return m_Options.testFlag(DEACTIVATE_WHEN_INVALID); } QtCouplingOptions(Options opts = NO_OPTIONS) : m_Options(opts), m_SignalOverride(NULL) {} QtCouplingOptions(const char *signal, Options opts = NO_OPTIONS) : m_Options(opts), m_SignalOverride(signal) {} QtCouplingOptions(const QtCouplingOptions &ref) : m_Options(ref.m_Options), m_SignalOverride(ref.m_SignalOverride) {} private: /** * The options */ Options m_Options; /** This value allow the default signal associated with a widget coupling to be overriden. For each Qt widget, there is a default signal that we have picked, but in some cases there may be a reason to choose a different signal. The model is updated in response to this signal. When set to NULL this means that the default signal will be used. */ const char *m_SignalOverride; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QtCouplingOptions::Options) /** * This function is the entry point into the coupling system between property models * (values with a domain) and Qt widgets. There are three versions of this method. * All versions take a model and a widget as parameters. They differ in whether the * user explicitly specifies the traits objects for the values and domains, or whether * the default trait objects are used. * * The last optional parameter allows the user to override the signal specified * in the default value traits for the widget. This is the signal in response to * which the model is updated from the widget */ template void makeCoupling( TWidget *w, TModel *model, WidgetValueTraits valueTraits, WidgetDomainTraits domainTraits, QtCouplingOptions opts = QtCouplingOptions()) { typedef typename TModel::ValueType ValueType; typedef typename TModel::DomainType DomainType; typedef PropertyModelToWidgetDataMapping< TModel, TWidget *, WidgetValueTraits, WidgetDomainTraits> MappingType; MappingType *mapping = new MappingType(w, model, valueTraits, domainTraits); QtCouplingHelper *h = new QtCouplingHelper(w, mapping); // Populate the widget (force the domain and value to be copied) mapping->InitializeWidgetFromModel(); // Listen to value change events from the model LatentITKEventNotifier::connect( model, ValueChangedEvent(), h, SLOT(onPropertyModification(const EventBucket &))); LatentITKEventNotifier::connect( model, DomainChangedEvent(), h, SLOT(onPropertyModification(const EventBucket &))); LatentITKEventNotifier::connect( model, DomainDescriptionChangedEvent(), h, SLOT(onPropertyModification(const EventBucket &))); // Listen to change events on this widget, unless asked not to if(!opts.IsUnidirectional()) { const char *mysignal = (opts.GetSignalOverride()) ? opts.GetSignalOverride() : valueTraits.GetSignal(); QObject *emitter = valueTraits.GetSignalEmitter(w); if(mysignal && emitter) h->connect(emitter, mysignal, SLOT(onUserModification())); } // Set the allow-changes flag in the mapping if(opts.IsUpdateAllowedWhenInvalid()) { mapping->SetAllowUpdateInInvalidState(true); } // Create an activator if(opts.IsDeactivatedWhenInvalid()) { activateOnFlag(w, model, UIF_PROPERTY_IS_VALID); } } template void makeCoupling(TWidget *w, TModel *model, WidgetValueTraits trValue, QtCouplingOptions opts = QtCouplingOptions()) { typedef typename TModel::DomainType DomainType; typedef DefaultWidgetDomainTraits WidgetDomainTraits; makeCoupling( w, model, trValue, WidgetDomainTraits(), opts); } /** Create a coupling between a numeric model and a Qt widget. The widget will listen to the events from the model and update its value and range accordingly. When the user interacts with the widget, the model will be updated. The coupling fully automates mapping of data between Qt input widgets and SNAP numeric models. This version of the method uses default traits. There is also a version that allows you to provide your own traits. */ template void makeCoupling(TWidget *w, TModel *model, QtCouplingOptions opts = QtCouplingOptions()) { typedef typename TModel::ValueType ValueType; typedef DefaultWidgetValueTraits WidgetValueTraits; makeCoupling( w, model, WidgetValueTraits(), opts); } /** Create a coupling between a PropertyModel with some kind of an item set domain and a Qt widget that holds multiple items or rows (QListView, QComboBox, QToolBar, etc.). Unlike simple kinds of couplings (QLineEdit, etc), these more complex couplings are parameterized by the type TRowTraits, which describe the mapping of each item in the PropertyModel to each row in the widget. This version of the coupling method takes TRowTraits as an extra parameter, and creates an appropriate domain */ template void makeMultiRowCoupling(TWidget *w, TModel *model, TRowTraits rowTraits, QtCouplingOptions opts = QtCouplingOptions()) { typedef typename TModel::ValueType ValueType; typedef DefaultWidgetValueTraits WidgetValueTraits; typedef typename TModel::DomainType DomainType; typedef DefaultMultiRowWidgetDomainTraits WidgetDomainTraits; makeCoupling( w, model, WidgetValueTraits(), WidgetDomainTraits(), opts); } /** * Create a coupling between a boolean (or integer) model and a named property * in a Qt widget, such as "visible". This allows us to hide/show and otherwise * modify widgets in response to changes in the model layer. */ template void makeBooleanNamedPropertyCoupling( QObject *w, const char *propertyName, TModel *model, bool reverse = false, QtCouplingOptions opts = QtCouplingOptions()) { // Create the traits for the property typedef typename TModel::ValueType ValueType; typedef WidgetBooleanNamedPropertyTraits TraitsType; TraitsType traits(propertyName, reverse); // Call the main coupling method makeCoupling(w, model, traits, opts); } /** * Couple the visibility of a widget to a boolean or integer-valued model */ template void makeWidgetVisibilityCoupling( QWidget *w, TModel *model, bool reverse = false, QtCouplingOptions opts = QtCouplingOptions()) { makeBooleanNamedPropertyCoupling(w, "visible", model, reverse, opts); } /** * Couple the visibility of a widget to a boolean or integer-valued model */ template void makeActionVisibilityCoupling( QAction *w, TModel *model, bool reverse = false, QtCouplingOptions opts = QtCouplingOptions()) { makeBooleanNamedPropertyCoupling(w, "visible", model, reverse, opts); } #endif // QTWIDGETCOUPLING_H itksnap-3.4.0/GUI/Qt/External/000077500000000000000000000000001263013355200157615ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/External/ColorWheel/000077500000000000000000000000001263013355200200245ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/External/ColorWheel/ColorWheel.cxx000066400000000000000000000201031263013355200226070ustar00rootroot00000000000000#include "ColorWheel.h" ColorWheel::ColorWheel(QWidget *parent) : QWidget(parent), initSize(300,300), mouseDown(false), margin(5), m_WheelWidth(30), current(Qt::red), inWheel(false), inSquare(false) { resize(initSize); // setSizeIncrement(10,10); setMinimumSize(200,200); // setMaximumSize(400,400); setCursor(Qt::CrossCursor); wheel = QImage(initSize, QImage::Format_ARGB32_Premultiplied); wheel.fill(Qt::transparent); } QColor ColorWheel::color() { return current; } void ColorWheel::setColor(const QColor &color) { if(color.hue() != current.hue()){ hueChanged(color.hue()); } if( color.saturation() != current.saturation() || color.value() != current.value() ){ svChanged(color); } } QColor ColorWheel::posColor(const QPoint &point) { // if( ! wheel.valid(point) ) return QColor(); if(inWheel) { qreal hue = 0; int r = qMin(width(), height()) / 2; if( point.x() > r ) { if(point.y() < r ) { //1 hue = 90 - (qAtan2( (point.x() - r) , (r - point.y()) ) / 3.14 / 2 * 360); } else { //4 hue = 270 + (qAtan2( (point.x() - r) , (point.y() - r ) ) / 3.14 / 2 * 360); } } else { if(point.y() < r ) { //2 hue = 90 + (qAtan2( (r - point.x()) , (r - point.y()) ) / 3.14 / 2 * 360); } else { //3 hue = 270 - (qAtan2( (r - point.x()) , (point.y() - r )) / 3.14 / 2 * 360); } } return QColor::fromHsv(hue, current.saturation(), current.value() ); } if(inSquare) { // region of the widget int w = qMin(width(), height()); // radius of outer circle qreal r = w/2-margin; // radius of inner circle qreal ir = r-m_WheelWidth; // left corner of square qreal m = w/2.0-ir/qSqrt(2); QPoint p = point - QPoint(m, m); qreal SquareWidth = 2*ir/qSqrt(2); return QColor::fromHsvF( current.hueF(), std::min(std::max(p.x()/SquareWidth, 0.0), 1.0), std::min(std::max(p.y()/SquareWidth, 0.0), 1.0) ); } return QColor(); } QSize ColorWheel::sizeHint () const { return QSize(height(),height()); } QSize ColorWheel::minimumSizeHint () const { return QSize(30,30); } void ColorWheel::mousePressEvent(QMouseEvent *event) { lastPos = event->pos(); if(wheelRegion.contains(lastPos)){ inWheel = true; inSquare = false; QColor color = posColor(lastPos); hueChanged(color.hue()); }else if(squareRegion.contains(lastPos)){ inWheel = false; inSquare = true; QColor color = posColor(lastPos); svChanged(color); } mouseDown = true; } void ColorWheel::mouseMoveEvent(QMouseEvent *event) { lastPos = event->pos(); if( !mouseDown ) return; if(inWheel) { QColor color = posColor(lastPos); hueChanged(color.hue()); } else if(inSquare) { QColor color = posColor(lastPos); svChanged(color); } } void ColorWheel::mouseReleaseEvent(QMouseEvent *event) { mouseDown = false; inWheel = false; inSquare = false; } void ColorWheel::resizeEvent(QResizeEvent *event) { wheel = QImage(event->size(), QImage::Format_ARGB32_Premultiplied); wheel.fill(Qt::transparent); // source = wheel; // source.fill(Qt::transparent); drawWheel(event->size()); drawSquare(current.hue()); drawIndicator(current.hue()); drawPicker(current); update(); } void ColorWheel::paintEvent(QPaintEvent *event) { QPainter painter(this); QStyleOption opt; opt.init(this); painter.drawImage(0,0,wheel); style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); } #include void ColorWheel::drawWheel(const QSize &newSize) { int r = qMin(newSize.width(), newSize.height()); QPainter painter(&wheel); painter.setRenderHint(QPainter::Antialiasing); // wheel.fill(Qt::white); wheel.fill(Qt::transparent); QConicalGradient conicalGradient(0, 0, 0); conicalGradient.setColorAt(0.0, Qt::red); conicalGradient.setColorAt(60.0/360.0, Qt::yellow); conicalGradient.setColorAt(120.0/360.0, Qt::green); conicalGradient.setColorAt(180.0/360.0, Qt::cyan); conicalGradient.setColorAt(240.0/360.0, Qt::blue); conicalGradient.setColorAt(300.0/360.0, Qt::magenta); conicalGradient.setColorAt(1.0, Qt::red); /* outer circle */ painter.translate(r / 2, r / 2); QBrush brush(conicalGradient); // painter.setPen(Qt::NoPen); painter.setPen(Qt::darkGray); painter.setBrush(brush); painter.drawEllipse(QPoint(0,0),r/2-margin,r/2-margin); /* inner circle */ painter.setBrush(palette().background().color()); painter.drawEllipse(QPoint(0,0),r/2-margin-m_WheelWidth,r/2-margin-m_WheelWidth); // QPainter painter2(&wheel); // painter2.drawImage(0,0,source); //caculate wheel region wheelRegion = QRegion(r/2, r/2, r-2*margin, r-2*margin, QRegion::Ellipse); wheelRegion.translate(-(r-2*margin)/2, -(r-2*margin)/2); int tmp = 2*(margin+m_WheelWidth); QRegion subRe( r/2, r/2, r-tmp, r-tmp, QRegion::Ellipse ); subRe.translate(-(r-tmp)/2, -(r-tmp)/2); wheelRegion -= subRe; } void ColorWheel::drawSquare(const int &hue) { QPainter painter(&wheel); painter.setRenderHint(QPainter::Antialiasing); // region of the widget int w = qMin(width(), height()); // radius of outer circle qreal r = w/2-margin; // radius of inner circle qreal ir = r-m_WheelWidth; // left corner of square qreal m = w/2.0-ir/qSqrt(2); painter.translate(m, m); // painter.setPen(Qt::NoPen); painter.setPen(Qt::darkGray); QImage square(255,255,QImage::Format_ARGB32_Premultiplied); QColor color; QRgb vv; for(int i=0;i<255;++i){ for(int j=0;j<255;++j){ color = QColor::fromHsv(hue,i,j); vv = qRgb(color.red(),color.green(),color.blue()); square.setPixel(i,j,vv); } } qreal SquareWidth = 2*ir/qSqrt(2); square = square.scaled(SquareWidth,SquareWidth); painter.drawImage(0,0,square); painter.drawRect(0,0,SquareWidth,SquareWidth); // QPainter painter2(&wheel); // painter2.drawImage(0,0,source); squareRegion = QRegion(m, m, SquareWidth, SquareWidth); } void ColorWheel::drawIndicator(const int &hue) { QPainter painter(&wheel); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::black); painter.setBrush(Qt::NoBrush); QPen pen = painter.pen(); pen.setWidth(2); painter.setPen(pen); qreal r = qMin(height(), width()) / 2.0; painter.translate(r, r); painter.rotate( -hue ); r = qMin(height(), width()) / 2.0 - margin - m_WheelWidth/2; painter.drawEllipse(QPointF(r,0.0),4,4); } void ColorWheel::drawPicker(const QColor &color) { QPainter painter(&wheel); painter.setRenderHint(QPainter::Antialiasing); QPen pen; // region of the widget int w = qMin(width(), height()); // radius of outer circle qreal r = w/2-margin; // radius of inner circle qreal ir = r-m_WheelWidth; // left corner of square qreal m = w/2.0-ir/qSqrt(2); painter.translate(m-4, m-4); qreal SquareWidth = 2*ir/qSqrt(2); qreal S = color.saturationF()*SquareWidth; qreal V = color.valueF()*SquareWidth; if(color.saturation() > 30 ||color.value() < 50){ pen.setColor(Qt::white); } pen.setWidth(2); painter.setPen(pen); painter.drawEllipse(S,V,8,8); } void ColorWheel::hueChanged(const int &hue) { if( hue<0 )return; int s = current.saturation(); int v = current.value(); current.setHsv(hue, s, v); drawWheel(size()); drawSquare(hue); drawIndicator(hue); drawPicker(current); repaint(); emit colorChange(current); } void ColorWheel::svChanged(const QColor &newcolor) { int hue = current.hue(); current.setHsv(hue, newcolor.saturation(), newcolor.value()); drawWheel(size()); drawSquare(hue); drawIndicator(hue); drawPicker(newcolor); repaint(); emit colorChange(current); } itksnap-3.4.0/GUI/Qt/External/ColorWheel/ColorWheel.h000066400000000000000000000030471263013355200222440ustar00rootroot00000000000000#ifndef COLORWHEEL_H #define COLORWHEEL_H /** * From: https://github.com/liuyanghejerry/Qt-Plus/tree/master/develop/ColorWheel * Imported: 03/16/2013 */ #include #include #include #include #include #include class ColorWheel : public QWidget { Q_OBJECT Q_PROPERTY(int wheelWidth READ wheelWidth WRITE setWheelWidth) public: explicit ColorWheel(QWidget *parent = 0); virtual QSize sizeHint () const; virtual QSize minimumSizeHint () const; QColor color(); void setColor(const QColor &color); int wheelWidth() { return m_WheelWidth; } void setWheelWidth(int value) { m_WheelWidth = value; this->update(); } signals: QColor colorChange(const QColor &color); public slots: void hueChanged(const int &hue); void svChanged(const QColor &newcolor); protected: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); void resizeEvent(QResizeEvent *event); void paintEvent(QPaintEvent *event); private: QSize initSize; QImage wheel; bool mouseDown; QPoint lastPos; int margin; int m_WheelWidth; QRegion wheelRegion; QRegion squareRegion; QColor current; bool inWheel; bool inSquare; QColor posColor(const QPoint &point); void drawWheel(const QSize &newSize); void drawIndicator(const int &hue); void drawPicker(const QColor &color); void drawSquare(const int &hue); }; #endif // COLORWHEEL_H itksnap-3.4.0/GUI/Qt/ModelView/000077500000000000000000000000001263013355200160725ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/ModelView/GMMTableModel.cxx000066400000000000000000000224071263013355200211740ustar00rootroot00000000000000#include "GMMTableModel.h" #include "SnakeWizardModel.h" #include "QtCheckBoxCoupling.h" #include "QtSpinBoxCoupling.h" #include #include "GlobalUIModel.h" #include "IRISApplication.h" #include "UnsupervisedClustering.h" #include "GaussianMixtureModel.h" #include "ImageWrapperBase.h" #include "SNAPQtCommon.h" /* ============================================ * Qt MODEL Behind the Cluster Listing Table * ============================================ */ void GMMTableModel::SetParentModel(SnakeWizardModel *parent) { m_ParentModel = parent; LatentITKEventNotifier::connect(m_ParentModel, SnakeWizardModel::GMMModifiedEvent(), this, SLOT(onMixtureModelChange(const EventBucket &))); LatentITKEventNotifier::connect(m_ParentModel, itk::DeleteEvent(), this, SLOT(onMixtureModelChange(const EventBucket &))); } void GMMTableModel::onMixtureModelChange(const EventBucket &b) { if(b.HasEvent(itk::DeleteEvent())) m_ParentModel = NULL; this->layoutChanged(); } GaussianMixtureModel *GMMTableModel::GetGMM() const { if(!m_ParentModel) return NULL; // Get the unsupervised clustering class UnsupervisedClustering *uc = m_ParentModel->GetParent()->GetDriver()->GetClusteringEngine(); // If we are not in GMM mode, there is no uc! if(!uc) return NULL; // Get the number of clusters return uc->GetMixtureModel(); } int GMMTableModel::rowCount(const QModelIndex &parent) const { // Get the number of clusters GaussianMixtureModel *gmm = this->GetGMM(); return gmm ? gmm->GetNumberOfGaussians() : 0; } int GMMTableModel::columnCount(const QModelIndex &parent) const { // Get the number of clusters GaussianMixtureModel *gmm = this->GetGMM(); return gmm ? gmm->GetNumberOfComponents() + 3 : 0; } QVariant GMMTableModel::data(const QModelIndex &index, int role) const { // Get the current row (cluster index) int cluster = index.row(); Column ctype = columnType(index); // Get a pointer to the GMM GaussianMixtureModel *gmm = this->GetGMM(); assert(gmm); // For first row, return checkboxes if(role == Qt::CheckStateRole && ctype == COLUMN_PRIMARY) { return gmm->IsForeground(cluster) ? Qt::Checked : Qt::Unchecked; } else if((role == Qt::DisplayRole || role == Qt::EditRole) && ctype == COLUMN_WEIGHT) { double weight = gmm->GetWeight(cluster); return QString::number(weight, 'f', 2); } else if((role == Qt::DisplayRole || role == Qt::EditRole) && ctype == COLUMN_MEAN) { double mean = m_ParentModel->GetClusterNativeMean(cluster, columnIndexInType(index)); return QString("%1").arg(mean, 8, 'g', -1); } else if((role == Qt::DisplayRole || role == Qt::EditRole) && ctype == COLUMN_TRACE) { double var = m_ParentModel->GetClusterNativeTotalVariance(cluster); return QString("%1").arg(var, 8, 'g', -1); } else if(role == Qt::TextAlignmentRole && ctype == COLUMN_PRIMARY) { return Qt::AlignCenter; } else if(role == Qt::TextAlignmentRole) { return Qt::AlignRight; } else return QVariant(); } Qt::ItemFlags GMMTableModel::flags(const QModelIndex &index) const { Qt::ItemFlags f = QAbstractTableModel::flags(index); switch(columnType(index)) { case COLUMN_PRIMARY: return Qt::ItemIsUserCheckable | f; case COLUMN_WEIGHT: return Qt::ItemIsEditable | f; case COLUMN_MEAN: return Qt::ItemIsEditable | f; default: return f; } } QVariant GMMTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Horizontal) { if(columnType(section) == COLUMN_PRIMARY) { if(role == Qt::DisplayRole) return "Foreground"; else if(role == Qt::ToolTipRole) return "

Use this column to tag clusters as foreground or background.

" "

Foreground clusters are added to the speed function; background " "clusters are subtracted from the speed function.

"; } else if(columnType(section) == COLUMN_WEIGHT) { if(role == Qt::DisplayRole) return "Weight"; else if(role == Qt::ToolTipRole) return "

The relative weight of the cluster in the Gaussian mixture

"; } else if(columnType(section) == COLUMN_TRACE) { if(role == Qt::DisplayRole) return "Variance"; else if(role == Qt::ToolTipRole) return "

The overall variance of the cluster

" "

Larger variance values result in smoother clusters

"; } else if(columnType(section) == COLUMN_MEAN) { if(role == Qt::DisplayRole) return QString("%1[%2]").arg((QChar) 0x03BC).arg(section-1); else if(role == Qt::ToolTipRole) { // Get the component information SnakeWizardModel::ComponentInfo ci = m_ParentModel->GetLayerAndIndexForNthComponent(section-COLUMN_MEAN); QString nick = from_utf8(ci.ImageWrapper->GetNickname()); return QString("Cluster mean for image %1" " component %2").arg(nick).arg(ci.ComponentIndex); } } } else if(orientation == Qt::Vertical) { if(role == Qt::DisplayRole) return QString("Cluster %1").arg(section+1); else if(role == Qt::DecorationRole) { Vector3d rgb = ColorLabelTable::GetDefaultColorLabel(section+1).GetRGBAsDoubleVector(); return CreateColorBoxIcon(16, 16, to_unsigned_int(rgb * 255.0)); } } return QVariant(); } bool GMMTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { GaussianMixtureModel *gmm = this->GetGMM(); int ngauss = gmm->GetNumberOfGaussians()-1; if(columnType(index) == COLUMN_PRIMARY) { bool state = value.toBool(); // TODO: it would be nice to do this in the mixture model class, without having to // have a special method in the SpeedWizardModel class if(m_ParentModel->SetClusterForegroundState(index.row(), state)) { emit dataChanged(this->index(0,index.column()), this->index(ngauss, index.column())); return true; } } else if(columnType(index) == COLUMN_WEIGHT) { double weight = value.toDouble(); if(m_ParentModel->SetClusterWeight(index.row(), weight)) { emit dataChanged(this->index(0,index.column()), this->index(ngauss, index.column())); return true; } } else if(columnType(index) == COLUMN_MEAN) { double mean = value.toDouble(); if(m_ParentModel->SetClusterNativeMean( index.row(), columnIndexInType(index), mean)) { emit dataChanged(index, index); return true; } } return false; } GMMTableModel::GMMTableModel(QObject *parent) : QAbstractTableModel(parent) { m_ParentModel = NULL; } GMMTableModel::Column GMMTableModel::columnType(int k) const { GaussianMixtureModel *gmm = this->GetGMM(); if(!gmm) return COLUMN_NONE; int n = gmm->GetNumberOfComponents(); if(k == 0) return COLUMN_PRIMARY; else if(k == 1) return COLUMN_WEIGHT; else if(k >= 2 && k < 2 + n) return COLUMN_MEAN; else if (k == 2 + n) return COLUMN_TRACE; else return COLUMN_NONE; } int GMMTableModel::columnIndexInType(int k) const { if(columnType(k) == COLUMN_MEAN) return k - 2; else return 0; } GMMItemDelegate::GMMItemDelegate(QObject *parent) : QItemDelegate(parent) { } GMMTableModel::Column GMMItemDelegate::columnType(const QModelIndex &index) const { const GMMTableModel *model = dynamic_cast(index.model()); return model->columnType(index); } QWidget *GMMItemDelegate ::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if(columnType(index) == GMMTableModel::COLUMN_WEIGHT) { QDoubleSpinBox *editor = new QDoubleSpinBox(parent); editor->setMinimum(0.0); editor->setMaximum(1.0); editor->setSingleStep(0.01); editor->setDecimals(2); return editor; } else { return QItemDelegate::createEditor(parent, option, index); } } void GMMItemDelegate ::setEditorData(QWidget *editor, const QModelIndex &index) const { if(columnType(index) == GMMTableModel::COLUMN_WEIGHT) { double value = index.model()->data(index, Qt::EditRole).toDouble(); QDoubleSpinBox *spinBox = static_cast(editor); spinBox->setValue(value); } else { QItemDelegate::setEditorData(editor, index); } } void GMMItemDelegate ::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if(columnType(index) == GMMTableModel::COLUMN_WEIGHT) { QDoubleSpinBox *spinBox = static_cast(editor); spinBox->interpretText(); double value = spinBox->value(); model->setData(index, value, Qt::EditRole); } else { QItemDelegate::setModelData(editor, model, index); } } void GMMItemDelegate ::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { if(columnType(index) == GMMTableModel::COLUMN_WEIGHT) { editor->setGeometry(option.rect); } else { QItemDelegate::updateEditorGeometry(editor, option, index); } } itksnap-3.4.0/GUI/Qt/ModelView/GMMTableModel.h000066400000000000000000000042761263013355200206250ustar00rootroot00000000000000#ifndef GMMTABLEMODEL_H #define GMMTABLEMODEL_H #include #include #include #include class SnakeWizardModel; class ThresholdSettingsRenderer; class EdgePreprocessingSettingsRenderer; class GaussianMixtureModel; class EventBucket; /** * The qt model for the GMM cluster list */ class GMMTableModel : public QAbstractTableModel { Q_OBJECT public: /** Classes of columns in the table */ enum Column { COLUMN_PRIMARY, COLUMN_TRACE, COLUMN_MEAN, COLUMN_WEIGHT, COLUMN_NONE }; GMMTableModel(QObject *parent); Column columnType(int column) const; Column columnType(const QModelIndex &index) const { return columnType(index.column()); } int columnIndexInType(int column) const; int columnIndexInType(const QModelIndex &index) const { return columnIndexInType(index.column()); } virtual int rowCount(const QModelIndex &parent) const; virtual int columnCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role); void SetParentModel(SnakeWizardModel *parent); public slots: void onMixtureModelChange(const EventBucket &); protected: SnakeWizardModel *m_ParentModel; GaussianMixtureModel *GetGMM() const; }; /** * The delegate model for the same */ class GMMItemDelegate : public QItemDelegate { Q_OBJECT public: GMMItemDelegate(QObject *parent = 0); GMMTableModel::Column columnType(const QModelIndex &index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; #endif // GMMTABLEMODEL_H itksnap-3.4.0/GUI/Qt/Resources/000077500000000000000000000000001263013355200161515ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/Resources/COPYING000066400000000000000000001045131263013355200172100ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . itksnap-3.4.0/GUI/Qt/Resources/SNAPResources.qrc000066400000000000000000000157161263013355200213260ustar00rootroot00000000000000 paintbrush.gif zoom3d.gif zoom.gif spray.gif snake.gif screencapture2.gif screencapture.gif scalpel.gif rotate3dTiny.gif rotate3d.gif rc.gif rb.gif ra.gif poly.gif outputintensity.gif logo.gif logo_new.gif itkLogoSmallTransparentBackground.gif formula03.gif formula02.gif formula01.gif crosshair3Dtiny.gif crosshair3D.gif crosshair.gif fltkbutton.png fltkbutton_pressed.png fltkpanel.png icontabwidget.css itksnap.css combo_all_labels.png combo_visible_labels.png dlg_error_32.png dlg_warning_32.png popup_edit_16.png popup_ok_16.png popup_paste_16.png popup_clear_16.png popup_delete_16.png popup_split_16.png popup_undo_16.png delete_22.png tools.png arrow_down_16.png arrow_up_16.png media-playback-pause-4.png media-playback-start-4.png media-playback-stop-4.png media-seek-backward-4.png media-skip-forward-4.png media-playback-singlestep.png edgefunction.png revertaxis_16.png dl_fourviews.png dl_3d.png dl_axial.png dl_coronal.png dl_sagittal.png dl_toolbox.png expand_16.png brush_shape_adaptive.png brush_shape_round.png brush_shape_square.png menu-arrow.png network-wireless.png mb_left.png undo_22.png redo_22.png snap_splash_172.png speed_bluegray.png speed_redoverlay.png layout_overlay_16.png layout_tile_16.png layer_Inspector_16.png layer_invisible_16.png layer_visible_16.png triangle_small_down_16.png triangle_small_right_16.png lock_blue_16.png lock_gray_16.png fancyslider.css save_22.png open_popup_16.png icons8_pin_16.png icons8_unpin_16.png icons8_down_18.png icons8_invisible_16.png icons8_up_18.png icons8_visible_16.png icons8_close_16.png logo_square.png logo_square_shadow.png icons8_fantasy_16.png COPYING license.txt icons8_zoomin_16.png icons8_zoomout_16.png credits.html thresh_both.png thresh_lower.png thresh_upper.png view-refresh-4.png media-playback-start-1x-4.png media-playback-start-10x-4.png open_22.png crosshair_cursor_bitmap.png crosshair_cursor_mask.png icons8_undo-48.png icons8_redo-48.png icons8_refresh-48.png icons8_pin-48.png icons8_star-48.png icons8_pin_12.png icons8_star_12.png icons8_pin_10.png icons8_star_8.png icons8_whitepin_16.png context_gray_48.png context_gray_16.png context_gray_12.png context_gray_12@2x.png context_gray_10@2x.png context_gray_10.png icons8_pin_12@2x.png icons8_unpin_12.png icons8_unpin_12@2x.png icons8_up_12@x2.png icons8_up_12.png icons8_down_12@x2.png icons8_down_12.png icons8_visible_12@2x.png icons8_visible_12.png icons8_invisible_12@2x.png icons8_invisible_12.png icons8_film_reel_12.png icons8_film_reel_12@2x.png layout_thumb_16.png icons8_slr_camera_16@2x.png icons8_slr_camera_16.png icons8_slr_camera_12@2x.png icons8_slr_camera_12.png icons8_cursor_16@2x.png icons8_cursor_16.png icons8_ruler_16.png icons8_ruler_16@2x.png annotation_28.png menu-arrow_8.png menu-arrow_8@2x.png icons8_generic_text_16.png icons8_generic_text_16@2x.png icons8_mouse_scrolling_16.png icons8_mouse_scrolling_16@2x.png icons8_mouse_right_click_16@2x.png icons8_mouse_right_click_16.png icons8_mouse_left_click_16@2x.png icons8_mouse_left_click_16.png icons8_palette_16@2x.png icons8_palette_16.png icons8_layers_16@2x.png icons8_layers_16.png snapres/EdgeForcesExample.png snapres/RegionForcesExample.png snapres/SnakeParameterPreviewCurve.txt html/tooltips/ModeTipTemplate.html html/tooltips/TipTableRow.html itksnap-3.4.0/GUI/Qt/Resources/SNAPResources_Linux.qrc000066400000000000000000000000101263013355200224620ustar00rootroot00000000000000 itksnap-3.4.0/GUI/Qt/Resources/SNAPResources_MacOS.qrc000066400000000000000000000000101263013355200223250ustar00rootroot00000000000000 itksnap-3.4.0/GUI/Qt/Resources/SNAPResources_Windows.qrc000066400000000000000000000000101263013355200230150ustar00rootroot00000000000000 itksnap-3.4.0/GUI/Qt/Resources/annotation_28.png000066400000000000000000000035751263013355200213540ustar00rootroot00000000000000PNG  IHDRr ߔiCCPICC Profile8UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic~,^@{˧?俆6v jj23OظO߳Ol298{EGR g`U?ghca@R"`hc :?#B)yy-dB6ٽ P%dt9!Qnn  #I/Y]y$AP``Յba/?``gK 6v,ll _Aׇ`E/>cx|O"(f} $."FUΌ*)xvJm[6@).At`J黶 JLJ \m;& n(99ʽ3W![cXs+\.n׾l\by8D;S#UaKCA.]彋xZ'5#U8w[*" ի,Mu%YH=Wg'g׼ֈ@huvbCs,UJhAhj(%"1~]~= XrzjV4@@#3 9`3h!=m$EDK@XRJ#NSq:1 I,۳{St1^m2V%tEXtcreate-date2009-11-15T17:02:34-07:00%tEXtdate:create2010-02-20T23:26:15-07:00;\%tEXtdate:modify2010-01-11T09:24:24-07:00`gtEXtLicensehttp://creativecommons.org/licenses/by-sa/3.0/ or http://creativecommons.org/licenses/LGPL/2.1/[5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- IcΏeIENDB`itksnap-3.4.0/GUI/Qt/Resources/brush_shape_round.png000066400000000000000000000022571263013355200223770ustar00rootroot00000000000000PNG  IHDRaiCCPICC Profile(UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- IchndStCkWZ6!Hm\$~ًo:w> كo{ a"L"4M'S9'^qZ/USO^C+hMJ&G@Ӳylto߫c՚  5"Yi\t։15LsX g8ocግ#f45@ B:K@8i ΁'&.)@ry[:Vͦ#wQ?HBd(B acĪL"JitTy8;(Gx_^[%׎ŷQ麲uan7m QH^eOQu6Su 2%vX ^*l O—ޭˀq,>S%LdB1CZ$M9P 'w\/].r#E|!3>_oa۾d1Zӑz'=~V+cjJtO%mN |-bWO+ o ^ IH.;S]i_s9*p.7U^s.3u |^,<;c=ma>Vt.[՟Ϫ x# ¡_2FIDAT8c?% ,0Ir (80Ahd(¨ aA9J!QIENDB`itksnap-3.4.0/GUI/Qt/Resources/combo_visible_labels.png000066400000000000000000000145461263013355200230270ustar00rootroot00000000000000PNG  IHDRaiCCPICC ProfilexYw cF2 kvoܺ~=;=Xg '2,&~80 !ޤ0${z Ź0aOO|zz;'Eyy kb︰H] +)ƌ02wuaS%`MЃ~P]?:>0WTmYq 4 I~EZưEHK6[1 8#vr8f/pݰ5cC }B7地=x{(d@/-AŞ;)C u$> Wohe/c!xEڼ`1C_ Q3G`_>pY}H2gfJ%RD4P(UbFIJBm?V{s} xGՑ>p^hXɫ>xMBRxdde޳W=b~_Yp*$x]W ;_p0 H"G}W@P@S` 3pDK1  A)ՠ4kt>``,lBbx !HT M2l gB((Jr:CW6.C3"@"pFB!PA vC?D8"C wq4b  Hf$RCZ"]H22,BV! ۑci2rAP(I8({JFJQu[>jFќh CǠ3EZM=8z0cD0u`0&L7f3bٰX %Ğ^b<r.!E((F)SlRQ QQZRzSQl|B9OIEO%BAeG@u =WT_UIԇKRPop8=+. =}xm >4)o2[44h)ihuhhih>]ӣK+kNOϥD~ `͐P0G@z"!PCGg00000621010)09021u2M3#MO0_c`ŢÒreeUՇ5u'/[ [>[ kv85{ {=eFu"G65NqNjA\\F\a\gz y<<$B;<xxuxxKxxW8 m7P(X< ( BRHE_XКQVxWxQ-p*ѧb1@bqEq2' % Y}}BU훔IHFK6HH1KKJH}vΗ~ -($S#RAT6U]QL<^P>EU~UABGB"AQ_JJd+Jʂʓ**V**UѪ)jJjj>K_R_/g9 ~ OӚ4JJIHIO?ltՑ#SeR R9sN0hȤ$gNU?Zy utl(K>LvwvLNQV.1;yyC'NTĜ 99_W@__0w[مN~TPTYLUU<]b^zF3[eeMYkgώVhW\̩yty󷪄1jj\PP_^Sb:zKN4 /^nol"y|sSN3hjp5k=U_!t&f-VܭV֑6Ӷvn_(dgv}tyڣ~[7+>94ta#]Zw?5y:0n1>2a?lurAW_D|yUkESSUo4M+Mw ξ#-x5=E?/-m.g~XIӍڟWVWɫ;_r}M[wS?lemlx5~ҵ RUb(KZXx9%2'sTWt^kDϸRJ&i,2%T4t LcY썜/Kg^=9SvJa3Eyi% gJSJo\>r~jsͧ k..Խ4װHE)k77 xZ[۴Mnvxuvܹ۽Ѓ%/@#~كlk=^j|BVFV?5g4;Y/_ɿZ~8f4fzt|wNvnw>>,v}]r\[^xSg/_o|[bCg/ @g$!*ma¼^ȥ hhE !L,}K$RA>H+%DEĖ$$%oJmh{@hWs]1usjkkmq3+NYwƞޓ}=)7Ud >Ry7L=>27x 3\//_>}uu7a3so3/[}wǗWTWk~m}VնsBd05NhbֱG)ipi*hӥ3D㘒3YNekbx#ȫ&P-xOhQFTV^!ߔp0؜t0ܜvȑT4t£tX>f)=G7}dA>b![EM%D:çxʲsgVWgp6bR]p%fW<K޸[-­۬n'wqAwG=zukAË*  >J{1?R7hlukd2Y_Nx3=Cc}=z1ywԺnsDgF Zq8 5Rg P!L`N 3~ @@L\q iAO#`! B^h@ J/фx spd)^F(T5j D+Y)zĨb0uY,' [8E1NFNYKJ*j:Lj sc4J4ghvhhGtӋ204 ߘ33Op4p_-#/,-.&!#/N2KB]fR' O{n)_QZvIyFּ.aU㗦X39sd4V-6/(5bX׻nxwWze"o1kAYoBNϷkL1, nuI;)gR=d8gsY?[^dZVZRZ1tnbf תnJTtvݾۛ}oc'9#sOL =x1U<2]ʯlߪ~h(ݚ&9Pb@fa&䃋 LBP2TuAp0D ITC wQp~͆@g;[V닭.QHSDQtRb((R~RʡzC@C;ߣ)EF.?0h re_d*c+c,<㼍|Y~B›"DmI HjJyHgɴ~TRQTWuT;>i kjm7@54c6w8Pd1fEcmdbn>hZy苻G/5O}O" r P,ڊqgMI,}x=5 uUf1<j [KRK+L)T\ع]óƇMWۮ߽u.n>q^?|1qNLƼ}2,+/_׾_Z8v͟co,Ѕ% a;:ZvYxi}IfA#LQ+1gLdb4LɣYvWBN珝 (ZULLVyfceyZuƗ/^Qhz{-mŎƞO Nu{*3}bM][xau/MVrW[m܏Gkmol 9ygqW6ŶvНGWa٬U ]e #,qޕQAVbgt pHYs  IDAT8c & 􂵲2 vK+{d1$JcUpZQip6:nV]fswa6c`8]>pp> A ` >e8~P Þ5wXAWă1ܽwL1G rl@!J2 (2$n\(r0o^ vpAgXIDAT8m]lϽsNv1]ǰ2ACJVIZVRS)%!T T*B}D R%CQ(1G՟ג^{gvfvf?ν 9~G#p5{sb{{{TSSGJ ccc0:BȳgcrkD"Q)J`6`X,fhL2/^| :44XMCӪhEQ8(OaT pRԎnm:u1[Ji |OJys4cl16cf~ʕMMMMiRYJS;t>v&Ywi8x B0B 9gJ`636BPU; 022..4jgg `R@mdu !9C}}}@q?Hӡ(\f ;BRJsRJ c+Bajb1[T*hu===9"B`.[}}}+X-8!<4DVw{ !y4iӺw"Z(a" c,}c DOOWs&zcv$u=6%˲ }u;eY+lذ3 CkkH$)e[Vi"7_p~ K)jZv]=qDx8v˗ȑ#@!uuu!q.vK)R@q`ԶgfvWӴN< =5JoGb7o\"9ic9ZXKSdeW7.sS[%H$r]L"|va㫣Emą ԨƒbE祳o ݂L i.9u]pM,X~zzFLJU17˟Z+SSS^۱T_GDF(!ql^럻&" ,BuUmDR*fνp0iX1\!jKwywitlIRյz'+²/ePd "4Ç Bh;BuP#'9XlCf|'@Zz4% 4Q4F[)g?~<%xcNz fnrܹsg*9عjfoBag޼P"I&ZRA5,6zeSJlz`/ &,:;999&9GUq m[q)qX'!O1zqnOLL0 ^U orKF&Fuz=hppP-˭BX4٬`EQ!p%DRsOĺژWg3CEgرcjf< C8R*㇦kZ!D$n3=¼Z/MV MGӕ̋%tEXtdate:create2010-02-23T18:14:17-07:00%tEXtdate:modify2010-02-23T18:14:17-07:00ώm5tEXtLicensehttp://creativecommons.org/licenses/LGPL/2.1/;tEXtSourceCrystal Clear:tEXtSource_URLhttp://commons.wikimedia.org/wiki/Crystal_ClearEcIENDB`itksnap-3.4.0/GUI/Qt/Resources/context_gray_10.png000066400000000000000000000006131263013355200216650ustar00rootroot00000000000000PNG  IHDR ';6bKGD̿tIME 648oIDATcda(c//jصϺnXX00|sB3g 2021Iܱ=!7o~-Yo0lga; AVqK  6100p20d```zyo7..7_gf}&b!%Π ̱/\)͟3ǎdxo[C9p%tEXtdate:create2015-06-09T13:20:54+02:00X{G%tEXtdate:modify2015-06-09T13:20:54+02:00nIENDB`itksnap-3.4.0/GUI/Qt/Resources/context_gray_10@2x.png000066400000000000000000000012621263013355200222400ustar00rootroot00000000000000PNG  IHDR'ՆbKGD̿tIME 648oIDAT(ϕOSQk- ?H!ĉn \XtUMH418h8`b,Iim"`@{qn['}3}W1Le4vH7qT媑 tLRm4^o`'1y>?;y5?Oo-W<^ ? H:8*]~/ 6exEknYc9%tEXtdate:create2015-06-09T13:20:54+02:00X{G%tEXtdate:modify2015-06-09T13:20:54+02:00nIENDB`itksnap-3.4.0/GUI/Qt/Resources/context_gray_12.png000066400000000000000000000007001263013355200216640ustar00rootroot00000000000000PNG  IHDR |lbKGD̿tIME 648oIDATuлJP?9iOؠAMPCW]LE,.},СP v^Sɗ oI" o.YM pb˙ -I|of,~֮u/z3"D(zKҢNS,/&U.a28$`S# 8*^% MXL=tOE}J!'ȒEcB9>2kv*F|F?+܆[6+%tEXtdate:create2015-06-09T13:20:54+02:00X{G%tEXtdate:modify2015-06-09T13:20:54+02:00nIENDB`itksnap-3.4.0/GUI/Qt/Resources/context_gray_12@2x.png000066400000000000000000000014441263013355200222440ustar00rootroot00000000000000PNG  IHDRJ~sbKGD̿tIME 648ohIDAT8˝KQ?skw"${-0&z%e]RA?]e!B7Laﺘ3wt-̠99}A P1B V^haǛ_w=a .y5S0@ {9ua}{6,`$r/#;B)L. @+7C5o+}"ģt7:7f ѧ52Xa6-l F]<I7ĉ~,=cM 9&i-A:GutH '(gDIa@{d-tMtӞ6;8K&a.MKP[h":HPA~„2XdPL 8L`5u$GLJE Ci NZ#)JHLZ$Uwi H0d4R*˭MP$e.(^r A-StD2Ŭ\^|+j1*kǾE HυH<ʋC8ErN@4\Tf,@clx[7oҷP :5ݕgGƈAF %tEXtdate:create2015-06-09T13:20:54+02:00X{G%tEXtdate:modify2015-06-09T13:20:54+02:00nIENDB`itksnap-3.4.0/GUI/Qt/Resources/context_gray_16.png000066400000000000000000000033021263013355200216710ustar00rootroot00000000000000PNG  IHDRaiCCPICC Profile8UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic 1 L'Y(IDAT81D@E [ف! dvd"H[fWx0dIԻxܠ3࿰Ymk ]fɾ?Zͼo6m+5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- IcIDAThݘ0`$ lGO6aI29ld/1|>ߒrIr Ic %BYZXHR%"b)#H`mYZ8׍Vc_Lہ>7w)M  l'O} ߁AmۚNj#\.:s>J `/&HnC&Ax0  ٽljHSs %N t:av{s<G!`׫` H vщTxM0YC">(D.Z%Q%JEKIh%$4J%3Oc,"<ޑ

ITK-SNAP 3.x Team (2011 - Present)

Paul A. Yushkevich (University of Pennsylvania) - PI and Lead Developer
Guido Gerig (University of Utah) - Co-PI
Octavian Soldea (University of Pennsylvania) - Developer
Yang Gao (University of Utah) - Developer
Baohua Wu (University of Pennsylvania) - Developer
Michael Stauffer (University of Pennsylvania) - Developer
Supported by the U.S. National Institute of Biomedical Imaging and BioEngineering through grant R01 EB014346

ITK-SNAP 2.x Team (2004 - 2012)

Paul A. Yushkevich (University of Pennsylvania) - Lead Developer
Hui Zhang (University of Pennsylvania) - Developer
Casey Goodlett (University of Utah) - Contributor
Timothy Burke - Contributor
Nicholas Tustison - Contributor
Supported by the U.S. National Institute of Biomedical Imaging and BioEngineering and the NIH Blueprint for Neuroscience through grant R03 EB008200

SNAP/ITK Integration (2003-2004)

Paul A. Yushkevich (University of Pennsylvania) - Technical Director
Daniel S. Fritsch - Contract PI
Guido Gerig (University of Utah) - Scientific Director
Stephen R. Aylward (Kitware) - Consultant
Supported by the U.S. National Library of Medicine through PO 467-MZ-202446-1

Original SNAP/IRIS Team (199x - 2003)

Guido Gerig (University of Utah) - Scientific Director
Silvio Turello, Joachim Schlegel, Gabor Szekely (ETH Zurich) - Original AVS Module
Chris Wynn, Arun Neelamkavil, David Gregg, Eric Larsen, Sanjay Sthapit (UNC Chapel Hill) - IRIS 1999 Project
Sean Ho, Ashraf Farrag, Amy Henderson, Robin Munesato, Ming Yu (UNC Chapel Hill) - IRIS 2000 Project
Nathan Moon, Thorsten Scheuermann, Konstantin Bobkov, Nathan Talbert, Yongjik Kim (UNC Chapel Hill) - SnAP 2002 Project
Pierre Fillard (UNC Chapel Hill) - SnAP-VTK Integration

Special Thanks

Terry S. Yoo (NLM)
Zohara Cohen (NIBIB)
Luis Ibanez (Kitware)
Joshua Cates (University of Utah)
James C. Gee (University of Pennsylvania)
All who generously contribute to ITK, VTK, FLTK, and SNAP

itksnap-3.4.0/GUI/Qt/Resources/crosshair.gif000066400000000000000000000033311263013355200206350ustar00rootroot00000000000000GIF89a  %% %+&++,22.99;FGCPPKWWL__S_g\ppdxxluw~~¤˥! !!ICCRGBG10120appl mntrRGB XYZ   acspAPPLappl-appl dscmdescogXYZlwtptrXYZbXYZrTRCcprt8chad,gTRCbTRCmluc enUS&~esES&daDK.deDE,fiFI(frFU(*itIT(VnlNL(nbNO&ptBR&svSE&jaJPRkoKR@zhTWlzhCNruRU"plPL,Yleinen RGB-profiiliGenerisk RGB-profilProfil Gnrique RVBN, RGB 000000u( RGB r_icϏPerfil RGB GenricoAllgemeines RGB-Profilfn RGB cϏeNGenerel RGB-beskrivelseAlgemeen RGB-profiel| RGB \ |Profilo RGB GenericoGeneric RGB Profile1I89 ?@>D8;L RGBUniwersalny profil RGBdescGeneric RGB ProfileGeneric RGB ProfileXYZ Zus4XYZ RXYZ tM=XYZ (6curvtextCopyright 2007 Apple Inc.C, all rights reserved.sf32 B&l,pH ȤҸl*((R+vz`n8h!zMeqxT`S' `Tw`!  ^ B  }VCB B QW  UZ  D MZ  ! ErHA;itksnap-3.4.0/GUI/Qt/Resources/crosshair3D.gif000066400000000000000000000045451263013355200210340ustar00rootroot00000000000000GIF89a $,%%%+++33,:::@I:HHAPPIXXI``Q`iYqqazyirzrzΟßͨ! #! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxlcprt8wtptrXYZ0gXYZDbXYZXrTRClchad|,bTRClgTRCldescGeneric RGB ProfileGeneric RGB Profilemluc skSK(xhrHR(caES$ptBR&ukUA*frFU(Vaeobecn RGB profilGeneri ki RGB profilPerfil RGB genricPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBu( RGB r_icϏProfilo RGB genericoGenerisk RGB-profil| RGB \ |Obecn RGB profil RGB Allgemeines RGB-Profilltalnos RGB profilfn RGB cϏeNN, RGB 000000Profil RGB generic  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfileGenerel RGB-beskrivelsetextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l,pHȤҸl*((R+vf\0+ܳozބGc ݙ# wTUZT_I# IhC B  xCC VCB QW  D  D C uD # Cy@ß&A;itksnap-3.4.0/GUI/Qt/Resources/crosshair3Dtiny.gif000066400000000000000000000016721263013355200217360ustar00rootroot00000000000000GIF89a!!!!!)11)191991BB9BB9JJBJJBRRJZZRccZkkcsscs{k{{s{ƥƥέέֵ!, LȰ  4`*p@A A\ !(xh,`84`4( !i j $ г忋NUN`<>10 ;itksnap-3.4.0/GUI/Qt/Resources/crosshair_cursor_bitmap.png000066400000000000000000000002431263013355200236040ustar00rootroot00000000000000PNG  IHDR:&sCAL1.000000000000e+001.000000000000e+00x8IDATx108N( `4q6= 4K^# +IENDB`itksnap-3.4.0/GUI/Qt/Resources/crosshair_cursor_mask.png000066400000000000000000000002221263013355200232600ustar00rootroot00000000000000PNG  IHDR:&sCAL1.000000000000e+001.000000000000e+00x'IDATxca001j0aP[8#]KIENDB`itksnap-3.4.0/GUI/Qt/Resources/delete_22.png000066400000000000000000000030621263013355200204250ustar00rootroot00000000000000PNG  IHDRĴl;bKGDC pHYsvv}Ղ vpAgXIDAT8˵OTW^fm`XhM4tեI_h7ctĤq.L Khڄt#ܩVVUeƈ0Do Aor6>9&cİHꆈ=,_˪ԯmmO]h~R߯eDƊOֲ%y/ZùYΞMȆINqk>Ujx Fw$)k~doDȇERi8a2 `8-?Oj,"U冈S4/ \`a~ 4M)/MJWPm+q_P5k;ຨajkia[O[L EJY\HWv `/9pPM ]E܉ Bwj[™3T;c_}筨SӨ N#V:1?S fH׉~IaK)tr\cq`Sx-<)xٵJa@Tǂ0B-qq1éPU؁~䓍U%DDbISd78a11G5{/$hSC*ų[H ;aT% v9񾥛como"xMM58XT;7tH*wJ##y5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic`GȲ$IP5D `LDuwC:<<(]m ۶e`Yqu]O "Ԛq4MsA/C7oϟ㎤G9$}oR_,I\IENDB`itksnap-3.4.0/GUI/Qt/Resources/dl_coronal.png000066400000000000000000000025301263013355200207730ustar00rootroot00000000000000PNG  IHDRaiCCPICC Profile(UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic7!Ec+q=IENDB`itksnap-3.4.0/GUI/Qt/Resources/dl_fourviews.png000066400000000000000000000022361263013355200213720ustar00rootroot00000000000000PNG  IHDRaiCCPICC Profile(UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic8NCѠ,znUU1M^GQ(tJr p9>pmJ4AοJk¿IENDB`itksnap-3.4.0/GUI/Qt/Resources/dl_toolbox.png000066400000000000000000000022641263013355200210300ustar00rootroot00000000000000PNG  IHDRr ߔiCCPICC Profile(UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Icp?_IENDB`itksnap-3.4.0/GUI/Qt/Resources/dlg_error_32.png000066400000000000000000000036111263013355200211430ustar00rootroot00000000000000PNG  IHDR szzbKGDC pHYsHHFk> vpAg IDATXí[lgg.;{mx#_ NVUUR0@*f`0{(;TMd d户Dcx:$A}s"K e$#BvBv[Q:MǍqgfhnl&Uaj6S.J1dHfU~uR͍cc}hQ+"dfg٪CMwTUcw}gf籣O}htI@o훍Й=}iٳDݻ JU1Upjv~ow7;U8nBGD?xWo@]D N%g}~r&yrz|<Cj ,R Xdgo/i:nk9`^_H_ױ,qs9u;ۋ>0f^mc`kp۶|&dDz- +mF8IvIٛ}nN pwP|(@ a% % _<&arJ1ك[p*{'r:>i < z6u7]xꉋ$;Yv̛%tEXtcreate-date2009-11-15T23:04:33-07:00†%tEXtdate:create2010-02-20T23:26:24-07:00.P%tEXtdate:modify2010-01-11T09:11:28-07:00fQ4tEXtLicensehttp://creativecommons.org/licenses/GPL/2.0/lj%tEXtmodify-date2009-11-15T23:04:33-07:007tEXtSoftwarewww.inkscape.org<tEXtSourceGNOME Icon Theme&i tEXtSource_URLhttp://art.gnome.org/2yIENDB`itksnap-3.4.0/GUI/Qt/Resources/dlg_warning_32.png000066400000000000000000000034051263013355200214600ustar00rootroot00000000000000PNG  IHDR szzbKGDC pHYsHHFk> vpAg #IDATX[hTGsً1Y&݈˪xɂ VD4R%Z[郈}j,yFQQ0H^ BfcIzo#G76wv˳ ъ ,]]2cZa!pulbWpMwg s@fA|H}^(+KfplD@-͞3GUW-^6>Bh +]NV0IcY1 Dz/]ocb{ D Nν{1b1̦&1%01vIjk؊PN0 ڈFc&T]v @V˲p @$!  D"!pb) w3w.*>}99iiʛ:7#~\\.BdZ:#=5U|),B_&Zśjۍ}{Hs61 T +6hsU̙ynӔ w@:r8(({#Ӊkn:?+pM{;pT('òeߏ^\7NN{B֡MLG}=9yy"?d6߼y^'Opڅ[ZpDxsrtc'oRt|-VU:mzQQ?B~;ls, @ĉ6iO{|~(Dwk+β2J*++ABSˆP`8cl^oYV(jj,@[΅lٲwlO},\k!~r> ';8Niw-1?e!?'#0Dˤsl>_IFn~&ի^AEE.\й[v6[zq^rOU?d}f{?3zx -(檱WnH5)30ηqc7Ji`YXe=}2͞Oib㴼xaYwE*Q\%,%F<$1=~.ɥ2=)I1\ěh:w ! vީ5:p ^m`%tEXtcreate-date2009-11-10T19:38:24-07:00t2%tEXtdate:create2010-02-20T23:26:24-07:00.P%tEXtdate:modify2010-01-11T08:57:34-07:00 Ρ2tEXtLicensehttp://en.wikipedia.org/wiki/Public_domain?%tEXtmodify-date2009-11-10T19:38:24-07:00+tEXtSourceTango Icon LibraryT:tEXtSource_URLhttp://tango.freedesktop.org/Tango_Icon_LibraryȭIENDB`itksnap-3.4.0/GUI/Qt/Resources/edgefunction.png000066400000000000000000000223611263013355200213350ustar00rootroot00000000000000PNG  IHDRSpًiCCPICC ProfileXY8?;zgw#ZwYeD$($(ZHFhH-D|뻾s]<>}<9v-8A @@`@球# ΍eaa k1Ď g(B&ޑ'O+`x`JSQ-t7, }1f偱:6Wg_@@΂?_2tseCݢK9Jgm?`EalO#=zupfdc#? 7~Z0_?l'K9a,@q-cccᩫcx!FXu3753Hku@y+a6{s ֋ozww0a-E7Rʇd쿻HRՎ`h'k&R xw~` t^?\I<4zz />@Otk>R=APl(u*n5*RB)\j^o]{=#Y{b_Ƹ:郷R86NJ1z]!F#@E>B#92۞UH5gỿF9bj#nWkI ;,3?+FvmbB lq-yZ kˈݵw0 :A$O-=㑑;j7BLOA h@AscbA*/pRojA @~V8rXOT& [G8p Lp ŠTPf 1L9AAx!qHR!= W ¡8(ʄbT ]Z#hzMC 'B0 8BIB aAAx#B1D6QG!HD2!yH%B dyQr  (IC- A%PŨ-T/j5ZFmqh8Zm>FGjM=}C`0La"0XLӅbcXV8V kuÆaӰgWw#9%7 >#Y YYeNd*9UmO7(()(l(|)QQ\G+%%%2%%(ee#Ciu*z*1**'plKT]Tϩp8!&zp5j<~j#jD[#ԟhiihibh hh,ђ к&ЖжN~Iәe]{D7OףO#x<1`|2K;LBLFLL9Lיƙ~2s2k1{23_ea^eagdd`i`cêǚ &fVvm]~CÊ#c;'g0Y%.&.M._|Nn<:7;F-"^e^^Cp |||I| |) $T,| *$,d/t\Yh^EH8FNNDC$DB(FTIOBL^GD8B\A(~N|xz}&$$$"$$37ݟy'IIG\[RRRUR/饍ۤȈdJddee[dW<@R(X8ĠdPܮr]峪e<TUSsS6Σ~^}JWMBcF_CZ󽖨OR$ګ:*::]H] A=z=[bI}>}o:eyX.CahX8޸ׄڤdT̔dvq郯͚́i!-1%ﬤX]/[Ѷɱyi+bnmGcdWkjkg?uHP~6C#αag9;9>u3s K+릛[ww#ReK+k[OQXL\5-]3o@H4,<r&ddBB1܁pHȦ(h11cQ8޸cqZDĔĹGkQ;8I*)/[}r[ gєTԺ44Rq'P''eϦoexdeJednfNJ,:=Sv s*xFnM]^L郧og;rQ\Ay!EaxTiQYnh4r87RYv~[7?90xⓖ!塶Ý##wGuG?5z?f66wFdG:EG)%)cGjHbHIMK=;AJXxr&EȩW+Ϝ-.L.=]TRTzPL ˕>]Xximٺz: \_,"ߪftۺݽ#ݮnT}i}`X`Ґ04xl4اg>ozJ70S#ӥ3^ҳo|2O?aC֢kb>k/S.)z?7~noojD8 Qih #5,ܟ–RJ'J-H#B+I'OcdLadeYdcW rs po*_ $.#R#(.$qs1r يx } 헺z*uF f**V66m_uWpt>}dƹץ5]AXJvQ$}~ H#-6ELҌFE?9k/L8 G=ʓSS :>1y+٤#Ls5O9WQ\DwUYtTD co*֪kXk.K_o~ա1Z7WV6;;tޭ9v+±?u!ʡّǣןŏ&L=|~ۯ+'O {?.}Ņm=K?RU;ok?G6+D (vT?: Yv'wzk.9ILCffgJca9[Gו/R0Xx^I:)EiGX2; [J*V j4۵i =пcdXaahbbc}Bs%ru-]}!UÐcaON?t:]'܊8M8+8{? yF:> \7KHȍhDLC8t\ctBcQ TXQT[POr@ g@ya8cNy` 9Ch@#L/рx sd1ރpT%jDY1z(c15,6;BFGfIvl܅E2%e e/];.7F@}zƛfVN^`paxI95m$!'>W%G^JL!agQW1O}$ۤ&?|}+TGUʵ*Ujikyg\3|i51?H2K6ϱ(lziCnn w_G9uƹ8ֺ-8=4<O\'~SOx|*|]` auN9:&!mCf|vv|Җ?.R_5ok^qSuݩ]{}F=}zp|i7R7>U- eVC}zцi7~@ (pQ= 8 .N0>C @ P MB#N;$-REA~Dq5[4+ o)?wXAH&INA! ?GB" e&[* sTp{ԅ4hEZ;h< 2~ Rd9,3UΪAB^Z'u2^&LLZP[X%Z٬)GpDp*?صSe}~!8#BFC 7#]:cXb''f]KM~jsB1>/89YbXyRewkg>ܪ?3Km`|@<'t:te#TID bGj!IJ8WPshnatzÄc&X_lv̘l\$,|eJsF*uA҉_>-g4yuV[OY_svƝMMf/__[d[[E[ow%+{|@T' WFJ8x@]vZ~|9{_5-dP pHYs   IDATx]UW2т4'JV"56 & 5jCc4>$E MѾT-/%1\RfX>us9{>gϜ5޽{13 c!M̗ˠҪ7D4BD3^|dLZ2qxXnWصgF?Q QWL0j 3n,Nk"+4h0& 0c0 h0& 0c0 h0& 0c0 h0& 0S iQ1 2:!+xa3C^PY/jV)w2\\[Q?hG1N*3QZ2@FfnX3X^f/.FkZL$9_&ih|PW <5=xODO.%ڤaR.+?wT:QЇ0FS@D$dbΪE"w3kh Og#P+!+0-g30Ka.U<_ ,Rh:EjQpF(SmFD6"ZXyNϣ*׌e(s,hw/ D4%%_lŖ\׽i~%'2 pL.p3"63%Uv rh'&?o8Scv &3(Z~`6@%<Qm]̧*} BA6_Г_R6|0=a {_`{16r8I̺?v|"DeYʫzS>țEj~Y?ͫ3~4S((3_#;l?[6t纔J|l;Fd?8g1 a8{UzvIz>oEWXk)mߨ-~ENkk E΂SG=U,QR kViqcY8 $ryDg"0> UsoOGMx+M RP4}f!Q/o)yR`_k98 F9QJc 4Ǥ -( Meĝׇ} O~"w|!߱4â)uhO¹0CAhfP,2Af&:*[^TZffzi;BPnh!MrDID ~`{rYّ,.B,%NC]cK{P~&Pv "|)"x̑:r/R@Sޛpo#ʍ;f+S&hv5~&MK|_j`"#C+pT.ogS\o JeO=z B7 qw&#w03pzѫ̈^&TMYֿM+:{`&N}KKfܗ˪/+[23M/XW%ucwo?qUa{M_ *JhsNׯ9;s`񣀋>vUګI@nbDc/hr %@boFy`1f~}OílzpsӨ%ɛBڈar7ǛQ kB^܉H*%, z"íԗX<쬔M3]@g օd\2ƒ/ag*-3I=Y݆CD 4]`}YvSl#s*8̈́Ԡ6ҿ$wc+I\Fs'e]Lc\ p>3I{-Lh //"m;Ah|~pcp}m.0 2>0@V >%,'-솛bgwUo'h\b}Ӵ},@dOEVt1nĠ6IȧX߄ZڒWfS#!H"*mPV( I9H4ގԆD؆,ۊy01V(:?6J;ĥha/Z? "ZQ Ԇh;c"j$&P^Gxw -s5M4{r+Z{ Jj(oe3`!38_bt3wCwD*<1[<%sP[4rAMflW_,Uu{F=& &"3#$/Tp]ObcjP":=j.2*&Jh0<՟py6*ML4 duB: ;Zaw~(&Ë1e] 1;v9}EךAژq":ID92&3 AhĞ!BG{tRv' j$0QOhJm<ЊZ|nSD^{Ah<|:!o\if>APFT kTq~G &^&gkAD \Z(3FAQw#(vME Q.2%WZ2- K^Ah򦲠6CRD-`F.}0ADp K 60 Y/YgVAb&! B7Ʀ"{1`S9xZ,hZIENDB`itksnap-3.4.0/GUI/Qt/Resources/expand_16.png000066400000000000000000000023021263013355200204410ustar00rootroot00000000000000PNG  IHDR;֕JiCCPICC Profile(UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic׋infObN~N>! ?F?aĆ=5`5_M'Tq. VJp8dasZHOLn}&wVQygE0  HPEaP@<14r?#{2u$jtbDA{6=Q<("qCA*Oy\V;噹sM^|vWGyz?W15s-_̗)UKuZ17ߟl;=..s7VgjHUO^gc)1&v!.K `m)m$``/]?[xF QT*d4o(/lșmSqens}nk~8X<R5 vz)Ӗ9R,bRPCRR%eKUbvؙn9BħJeRR~NցoExSIDATH c?Oay!"tMLLA,PPH=uaVM!#c%EDTTüw6WX@ƉKJ2>66[2)]ͼQK N4$\܉%䊱k+l4񿡥/XYu0N}.PE)`eO~A/ ; sA ެ&ZΜ<ɐnV\'u( T8IENDB`itksnap-3.4.0/GUI/Qt/Resources/fltkbutton_pressed.png000066400000000000000000000020231263013355200225750ustar00rootroot00000000000000PNG  IHDRciCCPICC ProfilexTkA6n"Zkx"IYhE6bk Ed3In6&*Ezd/JZE(ޫ(b-nL~7}ov r4 Ril|Bj A4%UN$As{z[V{wwҶ@G*q Y<ߡ)t9Nyx+=Y"|@5-MS%@H8qR>׋infObN~N>! ?F?aĆ=5`5_M'Tq. VJp8dasZHOLn}&wVQygE0  HPEaP@<14r?#{2u$jtbDA{6=Q<("qCA*Oy\V;噹sM^|vWGyz?W15s-_̗)UKuZ17ߟl;=..s7VgjHUO^gc)1&v!.K `m)m$``/]?[xF QT*d4o(/lșmSqens}nk~8X<R5 vz)Ӗ9R,bRPCRR%eKUbvؙn9BħJeRR~NցoExIDATH c? Oɤ{aZa,PPSLߺqfl33`&ׯ_ B05߽ff6VVbd -5 ,%<@Ky~{ %Zң5\$IGS .xQ*YAfC1I<8' Ԫ.~LiXt,ID ]+Mc{=IENDB`itksnap-3.4.0/GUI/Qt/Resources/fltkpanel.png000066400000000000000000000021151263013355200206360ustar00rootroot00000000000000PNG  IHDRciCCPICC ProfilexTkA6n"Zkx"IYhE6bk Ed3In6&*Ezd/JZE(ޫ(b-nL~7}ov r4 Ril|Bj A4%UN$As{z[V{wwҶ@G*q Y<ߡ)t9Nyx+=Y"|@5-MS%@H8qR>׋infObN~N>! ?F?aĆ=5`5_M'Tq. VJp8dasZHOLn}&wVQygE0  HPEaP@<14r?#{2u$jtbDA{6=Q<("qCA*Oy\V;噹sM^|vWGyz?W15s-_̗)UKuZ17ߟl;=..s7VgjHUO^gc)1&v!.K `m)m$``/]?[xF QT*d4o(/lșmSqens}nk~8X<R5 vz)Ӗ9R,bRPCRR%eKUbvؙn9BħJeRR~NցoExIDATH 햱 @ sb&Z }3]p| APy:`nviJڤI{ᔵ`;p8P )@ I2iJHbe0~2nvCMeY&Cfp"@U,,㞋\%(!3 0'b! ˸"Z2dFyiT?`9<&=SzI܎nZ{齔z"/RO{%K[ wvTUv srm8~ie:('|y }aI]ŤIENDB`itksnap-3.4.0/GUI/Qt/Resources/formula01.gif000066400000000000000000000037061263013355200204540ustar00rootroot00000000000000GIF89a 999RRRkkk{{{!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜIMrLA |.DГ&IQ z#DT8< V(HX˶-[$h`A :t`Ձ},A  }0f FȀgv`]眠20`pC:+A&YH؀7t9C`W$W*&x@zC V:rЃć4Lygh eD0WؕUB^]AűԀcQTu`iȑvME@2e,HY%!Q~怇Uv.NDrͥT_T`֐D%>{@U\[S^ye05deP:HA 2h]o Q%*ic 2_y։=xڥ5IAMmњ@b M؞}UQuFA#w!)z(Iiʝe 7G^gV]$ @k ˑqx& iR?YصTDVFМcV Q @؄xT  &N!Rc{A ,pd]y XS PyCv*e& 1`&'teA pbVp;Gv Hq=#'v湧@a\4Jp2FW@jD|'p Ħ:,Iy=oI5{ ga{J4]^ζUQHfA 6e@0ܛD1G@7̐h/jԶ=_YM\Uy2RXmxC,f0 l-޴vpm8j@2 =I}x#JiYu6=<  s+ȍj"RIՀpB/ B;rZo.rD`o?iYV"5]_"F@, ?CY1!_K  "D4wMDSđ0m1.3lp# PtzLJLB!L mlq3I"Yf)c$IRL*WV򕰌,gIZ#;itksnap-3.4.0/GUI/Qt/Resources/formula02.gif000066400000000000000000000025701263013355200204530ustar00rootroot00000000000000GIF89a999RRRkkk{{{!,H*\ȰÇ#JHŋ3jȱǏ CIɓ $@1 (68pb~,% `ЁF!_:e`@O1fc3Rc7І=BlE@~fB1Z(xGQ gZ2A)0W-#Bk)bQ |akU#F7 d]O}%tYDif@ Ԅa6MVD}u@vWe9PPWCq5PC:E t)QS(}w{RkEMd+K.Q& X-KѨHXE7J*ַyC+A;itksnap-3.4.0/GUI/Qt/Resources/formula03.gif000066400000000000000000000043131263013355200204510ustar00rootroot00000000000000GIF89aj 999RRRkkk{{{!,j H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@  (PЅX*L j2p pƨS TUx@[)20 t RR P 01pǎ Tp4 FnC(wh Qw*@JJ;uPZhKxXΜ \Wۡʛ/hy#罠7Gp(ՠQ<[@Tt]TA\TBB py"Ut! @-p@ Tggs@%ȉha(ЃAH5UP~Q](P5V$AmxJ A m YQ&$djV%Vs.וv@J 5gyF  \oV>@ZA1z@ %@hFJZX6d@~+Xv+TS!Z$y.`A7Ug/:bWWШF"ƚmW>TuV*4@Zj08 $ X@?ӱ%@u;l.][ 7~@0)\b,^ ת AH2EF( p !W"Ps4 tJ|&5E-0Fϊy4f] u@NnXpcPѮ>p7Ay;TTΦgFXp"!QS 3)E>Ry @@; :B9y3@]mi\xzS wA;^yPش|x7$B Ha32AEoX0Y[m,@#Mmq 5Pr8?C^~ F_E@ yS%Xaa\x]|/A,`P pL Q A Kmf%P*dP4PbaC➾gA:@ؒT2=CȼVm]%'6 gH%7áDʣ1"&{rd,qUnz*AlBe H"KƎ 2'Uhg|6R &7%y$J3t45:J] 77Z3R.1I&&

%1

%2

%3
itksnap-3.4.0/GUI/Qt/Resources/html/tooltips/TipTableRow.html000066400000000000000000000004041263013355200240520ustar00rootroot00000000000000 %3

%2

itksnap-3.4.0/GUI/Qt/Resources/icons8_close_16.png000066400000000000000000000030201263013355200215500ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDAT8JPƓеs@pqGwEX;..  X;w='yIIF>bH(}0 a+91ˑƓ"ӹ9Z]]qK!X/ $ `=W" r mU4K58[ilWOYJ5#ZcT ,N$d29Td 2/W^GQtB]IqKDvat9],\hX-1e~f8g05HY"jn׊c{mTحX`]=XVP\R:^(<{b=-}! 1[̃,?8>T~{"JOꅄ,IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_cursor_16.png000066400000000000000000000010141263013355200217610ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿tIME  0SY"IDAT(mѿ+q;1ܥL,&ga?@(@$(&lؔ/e&$)g:_gޯ;MEQ>*DGQoh4€woH[Wo^rxxW(k!<5+ɘ q%Ț wH} 鶪 #kZ @ @&7rzLT[NFJ_SRN,sI#ŊÊ]:_+-^=i\~2_`ξ.=6x].(@ [0Q9ӡ#SLOOKQ%tEXtdate:create2014-12-19T01:25:16+01:00$%tEXtdate:modify2013-09-03T10:23:48+02:00/IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_cursor_16@2x.png000066400000000000000000000016251263013355200223430ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿tIME  0SY"IDATHǕ_he9;6<(B?hAZb4$QJ+ol`Stx!@@0dqen־.39;{>7b$xa}IDIQAn$C{"hF2JǼ)L(E;%ѬCN@ٚ5MDDsfg`@5`R@:.w4ă"0HBzzG&ŒQ#'aw}/j" #Dg1VdE#y{ :BZt %f0ZU}c.mE{P6";f{;-9-ժV]l0WzU !T;F-NydMSM񋟰JGxN&3* u0`ڲ p:[$|SbMd,cteDfyS= t AW%^9ٿ`ÔB5w-ؽ? gs:c{}yUfXz}gZرiG)-\{%.yE%tEXtdate:create2014-12-19T01:25:09+01:00#!S%tEXtdate:modify2013-09-03T15:26:49+02:00^{]IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_down_12@x2.png000066400000000000000000000010051263013355200217610ustar00rootroot00000000000000PNG  IHDRJ~sgAMA a cHRMz&u0`:pQ<bKGD̿tIME 1~ IDAT8;JAoB0)B BVK*ml<`% o`lW,B K#`a@Xd]Zw ;2AzR _=#;уsړANC{&:et! *SVfQfhXX5jV!! Nl-7^ڭ}.lYjijK]<_IhHGFK$z L=&\=dCO ɝu/d빟ѧ!O%tEXtdate:create2014-12-19T01:25:09+01:00#!S%tEXtdate:modify2013-09-03T15:26:49+02:00^{]IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_down_18.png000066400000000000000000000030421263013355200214200ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDAT8mR;N@ K( HlǧC܃C܅+$)\&mޛxVJάݪrk'kVGl-4=*_1 L:V <;`;EQ,Wae_%hs>xWö`A&\nZ- 4IZ7fkWT$V P/yënTLĥmuz&E;ypB;Ƥ?{_AkgIn'48&1q9,'`y)so ?GBsZ~U#U DQM(.C)8g8;!<ƲfՄSG,AU$Iw9r3]$\@|KBSA6 &[Hm!=iIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_fantasy_16.png000066400000000000000000000032351263013355200221200ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDAT8mAggwoN!FlU"*Xr'؉vjqŁ"Vh&KŨG0~{q;3>1*"{u\R9{O|,>a0p0 pr6$8[,i!R`CDͦ9^2-p\ƺG/ְj"OyN>LOpu]ž M"7y ;Az("uzAwƽW,dD#|.#\YQ Wj*H-E 9qHn4bBRS"/KYIYs yeYgȫYd% 鉇țxI5V2aSY2gȢAn AIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_film_reel_12.png000066400000000000000000000007301263013355200224020ustar00rootroot00000000000000PNG  IHDR |lgAMA a cHRMz&u0`:pQ<bKGD̿tIME  IDAT]+w$?:Aʷ0 d1brb3LR6&hu8J!E\q IY4S\NƝK%AʖYG&X0n@c4X1(P6*ƽHx!-aOIUv̛Ңː7iy~dEgʾ Ta؋* &,kPNޕ5UPhY4xrjʴ:Zl8*m3ss:4{w ^NŸY B1%tEXtdate:create2014-12-19T01:25:15+01:00(+׹%tEXtdate:modify2013-09-03T15:30:09+02:00 IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_film_reel_12@2x.png000066400000000000000000000015461263013355200227620ustar00rootroot00000000000000PNG  IHDRJ~sgAMA a cHRMz&u0`:pQ<bKGD̿tIME  nIDAT8ˍ_hug銜br?6.r,d^1p7zaPyE]O *l`Ii 3IP/rx֬9bo#!h{ z^ԡYdy_8"7#HխAIK,U3証K5[}ŧڒ/kNj!'rU0iL^gQޛLsJV/p%;VZ`F+~t5}(uXq?EW==uVfŘm89-p:ZgW`T `PVt-vC s) UE,Aaژicm(Ҧnn -rʷr6[pz;7'44CХec ?aTՄ/Uqݻ`H4'rV?L;>3.IO\ ]E w0i&U_Gi.ъ4gva]n&5n $jףRR4cqyd5kʝꌻ'OM ]eRӣLo܍2I&FO, 5agpEs~kʛޕ%tEXtdate:create2014-12-19T01:25:15+01:00(+׹%tEXtdate:modify2013-09-03T15:30:09+02:00 IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_generic_text_16.png000066400000000000000000000007211263013355200231300ustar00rootroot00000000000000PNG  IHDR7gAMA abKGD̿tIME  !&,_IDAT(]JBx# rQYo/=J^h/-Q/ -R Bmq`,BXؘY=az !43b,KjZV]e P2!n<E)aR2vTj6Ȳqh+rH%r /ت.k0JTRSZѱ.0SufkUMP Yupodjj4e#EƛO!mEe *S#?<$k%tEXtdate:create2015-07-27T16:25:24-04:00>P%tEXtdate:modify2013-09-03T09:33:38-04:00̉8IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_generic_text_16@2x.png000066400000000000000000000013201263013355200234760ustar00rootroot00000000000000PNG  IHDR sgAMA abKGD̿tIME  !&,_IDATHKUQ&>R3PKCSfv+A[4mԼY5KHAM.aͤAK6ϹǬ|k^Z| M@uVLxTNl:W]6X{?WRwytx]ʧ`P_o-%H`L[MA'HUhTP[HmL" Q;+f#aGRK^TTTXy$j_G~{*Y{}ޠC8-)1d)4*D7Ņl x4(Z ѨlN `:ۈBB ̚f!'^5m(Ŝij/,kk5ϪSS8iq.g6=c1@; +x6qrЪN7Xm '&؝TU8ٸ۾f{075"7AEZC@ɛ}vIUE5cji }oO3}Hp(=4+Cɓت;>i ,O_jw%tEXtdate:create2015-07-27T16:25:24-04:00>P%tEXtdate:modify2013-09-03T09:33:38-04:00̉8IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_invisible_12.png000066400000000000000000000006501263013355200224310ustar00rootroot00000000000000PNG  IHDR |lgAMA a cHRMz&u0`:pQ<bKGD̿tIME ';CtIDATӕ!OBaFz/M#f`Dݴ IT/0H$ LK`THJBlnlg忋 sSQFԥ,L򞶮;OT5B\vVBHD͋g www.inkscape.org 4IDAT8KBa}5¡E[hZ 2# +%".~!4`(^뽢i =hMv[B34Z=s|%w'SEt`% .~V&`i(V>_\N_@ 0Ɉg_/^ML[*>נKP4gz߳Ԍ9AFhl72ܒIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_layers_16.png000066400000000000000000000010631263013355200217470ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿tIME '$l;IDAT(m1HW@= LTNK C;nn+@MA 'A'\͆.(A!C 5C(LO^7pn-Z0oɚ2?B@Ʀ7%JAHpְ *˜94x1u 4kk)1V/\^ q'wP%9eZ"o½__ZrOQwj)ceLL&q!So\;x]| pΡ9 ?J)^N)3>8Kwo5j̒p@U@TW NZEuzV+Y"b%]TsRm^P8fZB%ޕL6{mQ5[ R6>0Đ!G7\ߥI'2Dr/|R'HlmWw5 w6+s wyG\V;F۽-6* KN xNjiHԣCoCZ~}^k2璂#j3[v:&)U n{(Hm(:eZVfϧѯO.h̍t- yVZ:WñSݧv 4{) :jSd:,G2< W"QtSkϲo5:=]{.q߫L/NY!T%tEXtdate:create2015-08-07T13:39:36-04:004%tEXtdate:modify2015-08-07T13:39:36-04:00EsIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_mouse_left_click_16.png000066400000000000000000000010051263013355200237530ustar00rootroot00000000000000PNG  IHDR7gAMA abKGD̿tIME :'b9IDAT(e?HA9i/ Inpp!KC5KS (M4*99)jPT4$3j;<=wm3CՁMH9|x_{o/Mdk(o\)yyg#Mk&4NOw*ZӪ]k v52@62 TL9YldդJR][ԭءey]n()츣k][qdH\P$"1lgHQ j+eW7,#?!텼&͚w/LRΫ$1%tEXtdate:create2014-12-18T19:25:10-05:00JL/%tEXtdate:modify2013-09-17T00:58:39-04:00>IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_mouse_left_click_16@2x.png000066400000000000000000000016511263013355200243340ustar00rootroot00000000000000PNG  IHDR sgAMA abKGD̿tIME :'bIDATHǕ]HuyHA:t,bt]_ɐf@OdiD. ZuQ(G7`JL+m-Gr&+pюc>w~O<^]@HBZRlqnҸѣFVnBjբAڟF|u?2@N]ڥ4)u{"g749:4iI{5xnT/D r)\ 6ȧ|>ۤJ|cbMve0[:=#m/T~?uLfYIl9K6[Č^wB #uۭZOX,aT΀z?q(&`SrѾhoi CMP~萨BkY;L/SLTN^%M$V3WWecu+Vx؝%)y9-wdM,ׁ:i\C7 F]߅yoJLx*B~Vu M06[+QF ?"3*~Uo׉>c]J9)?{l05C<"z0 &'7cvv\+vJӦR"!)[dVR:X "Džφ %tEXtdate:create2014-12-18T19:25:10-05:00JL/%tEXtdate:modify2013-09-17T00:58:39-04:00>IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_mouse_right_click_16.png000066400000000000000000000010051263013355200241360ustar00rootroot00000000000000PNG  IHDR7gAMA abKGD̿tIME :*yu9IDAT(e?HU߽^BɖĞ hpQ0M!"Alqx8 B]Cģ%ph\:7||;]ѩnuuAn7ZR-< b/4O4f5yowF|u qC^ym9BuѬ XMe"Yy5R'({d+ScU`^Q  gHm˔r)mr*`%( ಾJ}h؞wֽ1aR:=ܷT ܱbŮ~^_$Ի뱫r~zoʚ4 BJL5%tEXtdate:create2014-12-18T19:25:10-05:00JL/%tEXtdate:modify2013-09-17T00:58:42-04:00.cIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_mouse_right_click_16@2x.png000066400000000000000000000016331263013355200245170ustar00rootroot00000000000000PNG  IHDR sgAMA abKGD̿tIME :*yuIDATHǕ_heߗMm*:ҎpN1#]YعLf).PXMD yQQo^TY٩uZG-vIEY%ss缁tS8""U!]zܣ2~w69[>z\.ec :d|0d\qޜx!9bȝp͠A7u WzT C&$D$Al!^E<4 3"N{)i5b:p}P!吘 LPڪǾH^uՁC5HRup>njG!B`z-$ONY4T2A:1BchTXAA &Vda`ـIńLI浢DIي\#hp U 5WFdmѿ` :b<|[X@zrV>zW-O٥GVY3&|qj Ͽ:*%tEXtdate:create2014-12-18T19:25:10-05:00JL/%tEXtdate:modify2013-09-17T00:58:42-04:00.cIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_mouse_scrolling_16.png000066400000000000000000000010241263013355200236510ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿tIME :#XIDAT(]ѿ+=St:i8vPK^Uν ߒ3B:=''mVuG,d"mܪ0DZu5cQvQU=(QVuSa_?o]WXN%tEXtdate:create2014-12-18T19:25:10-05:00JL/%tEXtdate:modify2013-09-17T00:58:35-04:00qLT`IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_mouse_scrolling_16@2x.png000066400000000000000000000017351263013355200242340ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿tIME :#XIDATHǝKh\uLNҀI&(P# E܈, .݈2 j5%]hBFM$HL\;3mzV=;'n '>u[xZ~}W"6hT1\+ЄoMCшN3~POxAogQq1Fk"%#f|nLt)Fs3"5zWZ\*W:Lf%Y{nS/:aL gZ:gq.ӣhЕ#/zVd=&Pfk=npiLӢc9Ԗ7 qS]wyf!l\vhKIKy݈d4ٱ+hELbT;u\9 ۢ|bĆ|Q~P“Wy0OgLh'JP#K{*[DR5 o LfXk,pg@J&Wp`ɇ~)u-+鰺j@NnKb qn`ĀEmn6ri R6KʂJZ7 oՋ|%eWD[vV!a%i`~i ýz'.yFv+*|r1ߨTdcv_W/RxNlGx^_,7Jk/WФ}S,/rzWv|(} Q%tEXtdate:create2014-12-18T19:25:10-05:00JL/%tEXtdate:modify2013-09-17T00:58:35-04:00qLT`IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_palette_16.png000066400000000000000000000010541263013355200221060ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿tIME (#J4IDAT(]бKqW/!t[)ʡ?%]hl"IhPMDD :Oߟyx>45gGY"v.5iU-ӔłYU%Z2VUi' {^{ k"~PPҏu" Β kt@Q%у 0u@TŞDnCS9M 0nfvkhi xN hq U7qR/ dIl"6 跈jjFnqڔsJ\h\cuE?\R"W1\y*/sX3"NwH]ReGm!a6ȇk0â%qt1?3<S8+a ,Le'鬡O´Tl)5=}<ƭ~WqHxŎaD1 K)e[>'a7A"ViZ ),%tEXtdate:create2015-08-07T13:40:22-04:005%tEXtdate:modify2015-08-07T13:40:22-04:00higIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_pin-48.png000077500000000000000000000013141263013355200211630ustar00rootroot00000000000000PNG  IHDR00WIDAThՙ;hQDŽ(0 XǽsswX (2haЀvZ*E,l4b% A# Dcl6,ݽswAL WD$dlg^UOWgKf"Z`|g(Jw'BUO49"Tu0ݤ֮>>@|M>\r?w8GRGQT&S9U‡aw O@s"U R3hY'Ibcu˜MD$Iwpe}il2O||~akg U=€b`3[.ۥ7<;o <TkEZk&>KumiVvu:\ t4[hMYgfa_(V_M6g j6;!_*_fe/k, V_p$A8US,7d ^Dv[R7Sq//ǂM$5_F+5\_aIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_pin_10.png000066400000000000000000000013041263013355200212260ustar00rootroot00000000000000PNG  IHDR 2ϽsRGB pHYs  YiTXtXML:com.adobe.xmp 1 L'YIDAT]1J@FRTQD<@HbD@0el<"bH|_Wt`vfv|8,KOWinEQGL4}/7Ɯj. a>W zh9gwh88Nn۶R.ceښ|(B1ߢUac^u_j%ܣ풗B 0 YSw2$$ЍX[WIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_pin_12.png000066400000000000000000000006641263013355200212400ustar00rootroot00000000000000PNG  IHDR |lgAMA a cHRMz&u0`:pQ<bKGD̿tIME 59eMIDATUбJBaOjr8Wtjp͋-0lk[0hڴ@[RX4Ow|IKFݽ+a siil') ϡBM `;dpFۮ=0s1vWΉpH#ҹWr;`m9bǙ  ͣ1إ[#7?d-=/Ơf̫4z;w.dY%tEXtdate:create2015-06-11T11:43:08+02:00sP%tEXtdate:modify2013-09-03T15:53:57+02:00YIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_pin_12@2x.png000066400000000000000000000012261263013355200216050ustar00rootroot00000000000000PNG  IHDRJ~sgAMA a cHRMz&u0`:pQ<bKGD̿tIME 59eMIDAT8ˍѽkSambU$R.( ^]uA$vt):(R FR|"BAqPẊFII&z{s.g}"FxFCNCޠa~n4 z*4V7|'u3F:j6]^S聉` 3d>95Y[AϻޮWZQ4-oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDAT8nAg /_R!ACtmJRFy7(2 UP$wF3F3g91UUMcL$N:mc1U=-s|Hx mCt$kwx $)|>09C`?0l)#g:/''#H֎ad\/U"@b6.g9cbΔ` G\@+~2XJlv7A]Ibd0+vi>uڸz1ҋbvX,^;PE1"RuD|ZwZ5ȕg$I>-n{ϟV0 lU~/j;O0g%\a mzxϩ7d7DM "Cɡ'ycfny&˲⏈9JMlq?f=~y{Clԑs_IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_redo-48.png000077500000000000000000000013271263013355200213320ustar00rootroot00000000000000PNG  IHDR00WIDAThc``h###MCCCccc!xo >g``0<`jjju< ƈP4Pcb#[&&&:C 艈XZ,C CPXYqhZ . =H/⻁ JT ^&@h. ~FH ---9v50*tc:9>offHj,:?&AK>(M'b%;bvV%Kƀ/4)–lDX$AŶLS p|Y߃@L3>*Ff o6 lXּْTh ]U 5H0} X4n-b.a=#2rb6n<}FQy06V agXC@50qvWyK C.$U'@өGpJ*=d-zm@Ge]ڔ{`zTNCZPs0e RXbhc(#{ i(z i4xy@OOO ? !9^V@ǯCyg YkC!mmm`0u@EEaj6[CIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_refresh-48.png000077500000000000000000000013451263013355200220370ustar00rootroot00000000000000PNG  IHDR00WIDAThZ=HCA ~8n:8;إY" Tpr\]Tp:w.AN"8N.":B.⢴ Er׾=wy_\T;ME"]p8|F# MC\!}?!%#্(NĂ'D| %9#a2 jCFπp+*#Xգкd !**80LxdɩpaF%USLPxGUOj[ߦICI$C繁33k\O.8K<;*.\ ?`hσn5vk>-"Em a'~IriVCs5["Y3U6Hu% pWLn\cI^q^q|6>9XM@>~1 "ڠzVFIIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_ruler_16.png000066400000000000000000000010171263013355200216000ustar00rootroot00000000000000PNG  IHDR7gAMA a cHRMz&u0`:pQ<bKGD̿tIME  03кIDAT(mO+eq;w ;d:Xȟ +5EɬYudfecA6՝k:>2$glGy%V,o)Yd 8͞+Vu]K˫/7rݲ^ߜ1bVםK_5$RNIyÅ?k(qJM ta۔O!1bGRw'3̱K4$LAYZdB<򗁦L6j|•ؔV1VdEXaC%tEXtdate:create2014-12-19T01:25:16+01:00$%tEXtdate:modify2013-09-03T10:48:51+02:00H|GIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_ruler_16@2x.png000066400000000000000000000015551263013355200221610ustar00rootroot00000000000000PNG  IHDR sgAMA a cHRMz&u0`:pQ<bKGD̿tIME  03кuIDATHǕMhTWgӉFI "P%JSԅgtSn ]Hօm]XE݈HєVꦔئ[J6FEE$3E&$;<}?܄*jzDjYgP(3R>nVj."ی٭GT (juRVԈJ^+aH\1ɀ0>xtxF$Wh*6~֫wP$RE{Aƭkz32œԫOV֊#&w[إY_hXZZAwXL }TZaiN8_qAL ;lW5{#eHb^#z_yy$QȬ(C+ugrS6yqK˸w;-ii;1-R҆k!YU tuLˈ-Yt^S]YajNՑGZgOg}Hąsh:B{%tEXtdate:create2014-12-19T01:25:15+01:00(+׹%tEXtdate:modify2013-09-03T10:52:27+02:00mIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_slr_camera_12@2x.png000066400000000000000000000013231263013355200231250ustar00rootroot00000000000000PNG  IHDRJ~sgAMA a cHRMz&u0`:pQ<bKGD̿tIME  4A IDAT8˵MHQ;cQ)En SȤx "R7(U*BrQ$Ha "}.Z)%B!haDeVYe5N צpsW9{ 4;ؽ m"naG AHK|wÐZ͛Š2HtH-[5]޺tkq!?BrU\:=6f| iuK4cb7/:&P6༄۬g@a斯8 %. eTK˚jTo`RJ:.kרuœa,2mPTo&Q)/(7n07Gj29R% { ainJnWq=Ҫ@zIڍJF(%'"o&gT(7 M~ UI[z0bG-ӊaݐf 0oՠM5 7q%zg-S B%Κf=ZbUOFlʺw ]ǞLd#9h[ _["rp>?ա6ꍏA0l]+Z0ĄթCRݚPA i@|V+'ŀ)AeS[UujJNKo4eJ^!m56"cw|l[䜿5tnP  ,? -EgԆômW%tEXtdate:create2014-12-19T01:25:15+01:00(+׹%tEXtdate:modify2013-09-03T10:52:27+02:00mIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_star-48.png000077500000000000000000000012251263013355200213470ustar00rootroot00000000000000PNG  IHDR00W\IDATh홽K1ƻ:)*:ڣ*蠃S : BI\DPsZ}r}Kă3yߓɛ$H$R1 ˲ gk -߷ܫ>,;eT޹ `Um"TUXV5y}WF@2+uTyߟH ØD .ǸAc>cl!>{ ^V9š9V(բMAiHf~ ZrdEnu@f{F>< li: w趪n`[A6ټkRE& *+NavXo¿ E8z8%?"[4!Xv^:V$P &>ˍyW.c)UbpWb+b%Oކug2 l2߹cJ=QgV1BG@Tr=9m]v5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic 1 L'Y:IDAT(UQ-O@/BBqp)9R<A#Μ8AG!!u,$l2}{} (eYAhc"U׵E:A^1|,|d>meIUUI RJ i.,}߯蕘xx 4n};S@Xk9qM9@k=q EQm-b2 \擮v_#pG[j'a8 rU#9Wdk`Aj_;_C<%Y|ulG=S "{cf)~]s` BIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_star_8.png000066400000000000000000000031371263013355200213460ustar00rootroot00000000000000PNG  IHDRiCCPICC Profile8UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic 1 L'YIDAT=1 1Eg=@@<6[[Y-,m^>'X $Y62L&} JuB=yJ=/?d6 mU]׋R0/1~+kSJs.l0 l[_2(޽'D0 x!X7-/BM 7tx#=HW.HiBX/IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_undo-48.png000077500000000000000000000013031263013355200213400ustar00rootroot00000000000000PNG  IHDR00 1 IDATxOH[3茦!r0HM$0\EM޸˽w.* \(ִiB"ȬtcBtSvcNsV}vz?"tRDlLaLn*a>62[+X$('H@en9 `$uB0gu`$ᚽ.WhCC} 3=')nq J,ӈS31>E EÔOn cCoTõcw^H.WA]M*}P<[u2@h-Bv6kx‘\z!fT)$k+2xP"x>b༚ dYSo_?{{Wtp-nvjU:j 5U^cܒ kI`ӹvy*Aj⼋-U@y!=W*YiNruT'g gXi _<"xK\eVBpgE$J¦œJ0CL3VϗFXCl4 (#"؋ySxȗ !6!C}!)ugaAB+x[?@vaOEGTVIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_unpin_12.png000066400000000000000000000007151263013355200216000ustar00rootroot00000000000000PNG  IHDR |lgAMA a cHRMz&u0`:pQ<bKGD̿tIME  #MYnIDAT5;/CaИ  h>`(  *nnF4&Sҭ4SnIrt췌M,:2-)Ri_C˼Kc"3,u]{M( ]%]u ̺w*1j׫eU7 k*mw59]u4U9Bκ;  QXF@[="k%tEXtdate:create2014-12-19T01:25:16+01:00$%tEXtdate:modify2013-09-03T10:04:35+02:00'IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_unpin_12@2x.png000066400000000000000000000013611263013355200221500ustar00rootroot00000000000000PNG  IHDRJ~sgAMA a cHRMz&u0`:pQ<bKGD̿tIME  #MYnIDAT8˅;hQd1 D$&hD2BBB,$ >Pge!M!Qa q5uwvx;psA d Wb]a#qrQP/oЫ_N#"2Nl6٨)TpF3:3 ">ۡMV/1lt2_FB "Wde.ۚZ/BD,pARV(oPci&?d&,u H./6,:[CnV.螜qIA %hp߈؊>+&͟s|9вьQ mCrj.fy$crR E4e$'SP  QJ!u S9lwV_ Kcָ xu3V۱?h G\UXgXHLhOl>q칏FcA|]'M&iVL(%1lpC`;n>Hyz< yoyA:o+e%uf%tEXtdate:create2014-12-19T01:25:16+01:00$%tEXtdate:modify2013-09-03T10:04:35+02:00'IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_unpin_16.png000066400000000000000000000032751263013355200216100ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  ?IDAT8U;SQhlFH(h?`ga( N#FD@$HةEr~p/ k:y;Fv_+"v8뺷(n+`0Vuؕf.veY|>_d@8B)-1^f.$G]FQBHV[0AIA&V OkZT*'=/ֱ : @&.- fi^.TT-dU z3 LC_Bϲmd2Wڲ.tRoÏ`|.K&c%tEXtdate:create2015-06-11T13:11:31+02:00 T%tEXtdate:modify2013-09-03T10:57:52+02:00%IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_up_18.png000066400000000000000000000030301263013355200210720ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDAT8m;N@p^GPR qJ+ qD.SфHG`d"H뙝n\.">p޶I3^H +?EQt.,ht%FP~|WI-`0pZ!t^VUU +R f` ԑ@@;rrj7m*'=q<C7B?F7K|T}jB $I0$'v²V ̰wAjxȅN O5(G>H%tEXtdate:create2014-12-19T01:25:10+01:00z%tEXtdate:modify2013-09-03T10:58:43+02:00%0IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_visible_12@2x.png000066400000000000000000000012171263013355200224540ustar00rootroot00000000000000PNG  IHDRJ~sgAMA a cHRMz&u0`:pQ<bKGD̿tIME  :+S!IDAT8ҿK}8ԗ Zj  "nhLAk ( šB3D Y~g*<4n|RZ&9Ԣ^Ӟ{h"u KZiGob5c~!KSn/0vh++%,K4bk5$3jy/gP3jYjO,"Sۡ*N"У3[4OuF,2]*""LY08Bt`@WP@&2t www.inkscape.org 4}IDAT8R=KPk%dЂ888tRph8E ]uw?AnD/6%n)I='&% \x{9%b0Hq"g 0iq˲l8ށB-W~ ,%AV@B 諾BWl6|"A66D5F? <m8al}482Lv@M&۶ߣњ5ǯP"R/?Ǔܑy_8OK$NkN'a8U6ML&3π@x(E^o&E3jg@ɡв/^BM$9Lm f}^UIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_whitepin_16.png000066400000000000000000000017651263013355200223100ustar00rootroot00000000000000PNG  IHDRasRGB pHYs  YiTXtXML:com.adobe.xmp 1 L'Y5IDAT8R=ZA}  1-? +!u. aAH֯"`P! (6ƧNΙ=\Ʌy;̽s)Lq O27") 2@jCfƪXv}FB `P;DvBQZeE27{\.v]|<7R! F"pn1N@j-)+a,Ɇ7V7zX,h6b+/..7y1ͮX4yR)v^~mySͣ:HJ.KŕNSѨt: Qb|>LRx<{ n ,/ rxH¤ hoوi4,rk׀}AVr9qdcXDVtן*#xWm4P(\xtO$@B䖛p|yie^?L&b<ՒG39z'v6s-|x1xc+ȀS1~1?ud_?IENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_zoomin_16.png000066400000000000000000000032561263013355200217710ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  0IDAT8mkSaK-B!W$"%BJ8.FP7.Pũ`@Kq@kḫ sz“~=_l˲*`8AClvEd#/:$N"@|GhAVP|YsVl_iJ:v>\L[jMHxva[BM'.8—¶m 89Si4 {dr<w9$f;j>ed29uĦv$L&19ЧҵvNzn8Sg_l6{\.u~~rKA:yR\ @cS|Ǭn^a'A6]^`Js; 'Mzc,*'W]#E)E7K *$'o6T2J&CO9G::^?KIENDB`itksnap-3.4.0/GUI/Qt/Resources/icons8_zoomout_16.png000066400000000000000000000032451263013355200221700ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  'IDAT8}?hSQ? :H`.-dѽ)ݡ lA."BD]hߋ x;|sol˲1Z|~%*tqVڲmہ OM&x` ?<`+H,5!NIalJJ1nok\.>L Nz~&ߨq.&B`߅lvS6Ah`oquy\'7p_9p]ܘ=HRt po0IS=.m^:kVp8m㖜}] DVU}lA QeN(܊OTgHd2y6t Gyfld V Kqp yKpl6W7N,Y J{&b?ykF PB&y5,ka#_JQ0!u&LBrE1/h2(>&*/MņQIENDB`itksnap-3.4.0/GUI/Qt/Resources/icontabwidget.css000066400000000000000000000020461263013355200215100ustar00rootroot00000000000000* { icon-size: 28px; text-align: center; } QTabBar { left: 0px; right: 0px; subcontrol-position: top left; subcontrol-origin: margin; } QTabBar::tab { min-width: 1ex; border-bottom-color: gray; border-top: 1px solid rgb(108, 108, 108); border-bottom: 0px; border-left: 1px solid rgb(201, 201, 201); border-right: 1px solid rgb(201, 201, 201); background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:0.0394089 rgba(245, 245, 245, 255), stop:0.487685 rgba(226, 226, 226, 255), stop:0.502463 rgba(215, 215, 215, 255), stop:1 rgba(245, 245, 245, 255)); width: 30px; } QTabBar::tab:hover, QTabBar::tab:selected { background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:0.0394089 rgba(214, 228, 245, 255), stop:0.487685 rgba(193, 205, 221, 255), stop:0.502463 rgba(182, 193, 207, 255), stop:1 rgba(215, 228, 245, 255)); } QTabWidget::pane { border-top: 1px solid rgb(108, 108, 108); border-bottom: 1px solid rgb(108, 108, 108); } itksnap-3.4.0/GUI/Qt/Resources/itkLogoSmallTransparentBackground.gif000066400000000000000000000062041263013355200254650ustar00rootroot00000000000000GIF89ad@111999BBBJJJRRRZZZccckkksss{{{ޭ֥kcZZRBB1{JsB{sZsc11)ƥ)ZJkcskJJ{1s)sc!)9)޽1έ)ֵ)1޽){sJskBs!ƭ1{!9)9)sc1{ks!))){R{Bkc1cZ!))!!s{s9sk1B9RJB޵ƽZZRRRJ{k))!RR9RR1!!))ƄBJJ!1)s{!9R{Bc!9cB9sZkJZ{BRsJcBZ)9ZR{9Z)Bs)R!B1c9{)Z!J9{1s)Z)kB91s{kc9RBk9c)J!9s!B1c9s1k)Z!J9)c!R!R!ZB1{JJ9B!Z)sk{Zk{s9Js1BkZ{Rs!1ZBc1R)J1Z!B)R)R1k!J9B){sskcRsJk9Z1R91s{Z{筽{s!1kckskRZޭ!,d@@ HMX@D$h@ C /`pCʗ0cʜIM |0)\)A)m5H"0*4)]ȹ]t9 &0޼p:t xٺvU=Z~K \ +k" I YNJCqfaJ\i1-X>6g㆔`N+4~询eF 6$( aۅNޜ5sۡp05/kwU35jwSHL@tլ3O M0 u`HvR"A8p@ p)I@N`@A.39 $epA̸ZteH0L,$  &<#K"c`0Q#}fqQ) >6} B@F#X 7 ´}iڜ)S5` rc!vM:,2˴%hP#iM V9:v>`AM J+=m1C_qҙsM 4Pv;BZ84iD0AXp S`" !$z/$A܀[9,` rŚ6@\ ,dx 4V &̸jK\7E`V_L,V"/QWhR>CSGm'لMD "DeDTqB6ٶn}L(G0PyjE4 WGJ17輮r՛R8jج.#H&6 3)x: YlC&_>ؿt, ҏ D ,0@Bp؂da @ZGu: 8":Hp gرL|!7!& =h&~rXcxPH%. %mi =1Mn򓍂>x 0kʔ Ǒ0), =G= vBD h'"E 8velcD&,M}D75fJ2O+yfJy\BP@ p\LoY2P@@P0*p>` 0@5 4`@*Atʀ#lApըF9> sCIsT a Sc:DuTe0| :DPL@" "qx02 $F`!D@G(KF!GR#%`\LE\ft޼Z,#0р2 [R)Tn4ܺ .&h[J8%qPJpuKڛa^M&`1i"P|Vn*,[ h ҭ Vy,q#ۮ/O@(Lbpj4a [0 DtODSz7%!J+K?R=O ^A hD@8ޱuc9/juBJt.zX@0/)nGI *~Z (yɣӗΤ/X7YuhlBD!< |hIAi%D9/C+5 sFp8`@j `T_{h∦yP0H$g =LT ZΘ np1ω42stڈ#F[$1P-R*d#̡v'E':96=#O hV?#kkKw?1s(Mc`e Xo +-: %t0Y, |`E-t! Na Wp3M'rP\v+ï3& 6[&AmV9*EӚ&n Z-l4&ô% ҝd*B V-03"Wa#5E9؂{\M9G X* o$f/EZ/!g>Y 0fZq}!c?a\$"xN/0  V)|O7Ԛx"e N  "!@8p4FM8gDN(RN1;itksnap-3.4.0/GUI/Qt/Resources/itksnap.css000066400000000000000000000013171263013355200203360ustar00rootroot00000000000000QMainWindow { background-color: rgb(248,248,248); } /* QGroupBox { border-image: url(:/root/fltkpanel.png) repeat; border-top-width: 8px; border-bottom-width: 8px; border-left-width: 2px; border-right-width: 2px; background-origin: content; padding-top: -3px; padding-bottom: -3px; padding-left: 3px; padding-right: 3px; margin-top: 2.5ex; font-weight: bold; } */ QGroupBox { background-origin: content; margin-top: 2.5ex; font-weight: bold; font-size: 12px; color: rgb(30,30,160); background-color: rgb(237,237,237); padding: 5px; border-radius: 4px; border: 1px solid rgb(130,130,130); } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; } itksnap-3.4.0/GUI/Qt/Resources/itksnap.qss000066400000000000000000000015061263013355200203540ustar00rootroot00000000000000QToolButton { border-image: url(:/root/fltkbutton.png) repeat; border-top-width: 8px; border-bottom-width: 8px; border-left-width: 3px; border-right-width: 3px; background-origin: content; padding-top: -8px; padding-bottom: -8px; padding-left: -3px; padding-right: -3px; } QToolButton:pressed, QToolButton:checked { border-image: url(:/root/fltkbutton_pressed.png) repeat; }; QGroupBox { border-image: url(:/root/fltkpanel.png) repeat; border-top-width: 8px; border-bottom-width: 8px; border-left-width: 2px; border-right-width: 2px; background-origin: content; padding-top: -3px; padding-bottom: -3px; padding-left: 3px; padding-right: 3px; margin-top: 2.5ex; font-weight: bold; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; }; itksnap-3.4.0/GUI/Qt/Resources/layer_Inspector_16.png000066400000000000000000000017351263013355200223350ustar00rootroot00000000000000PNG  IHDRagAMA7bKGDC pHYsHHFk> vpAg\ƭIDAT8˥1kA]5phu6^ťQăU*Zl BJ$X=HNݻp.>3#2!7?}>z0PSsU1w\5||ppË]n[+g2OuIa:>:3oiw_ݝn oi^7wA[/ÍFW.$+ZbBDD ̔{pGr@ $T ش_[ Q\ DE`la0|HfCJ {w@D@ Rj.׶`K& D@Qγ9PnlR"Ꮯín9s~S\k_Q=wdn K]K%tEXtcreate-date2009-11-16T22:18:16-07:00wu%tEXtdate:create2010-01-11T06:52:53-07:00c%tEXtdate:modify2010-01-11T06:52:53-07:00QbtEXtLicensehttp://creativecommons.org/licenses/by/3.0/ or http://creativecommons.org/licenses/by/2.5/F`DdJ<6.'w T @- m@n8P $ B2r22 t%[j;eOv$(S*@@&X`(ʑs`̔`)d` SGE3(xW\!Sd咔Tn!\]x87CP؄ ee3FvD9;:;8:|?E񋖴e /B_TBfgk+ m_ _׃  2r<[&q?.wL'bPGKĹi ˒$ IHk`~B[P. %w߂1w0hْ 4P6h>؀#;x̆P8XBHLC.,UP%BZF8-p<^0 o`A2DX6b"ֈ#Ef!~H0!H "ERd5R#U^9E.!==F~C>@٨jڡ\ Bh G h%ZBѳڋ>G03l0.Bx,c˱b6b#{";!0 $,",'̈́ Ba$nD>1B%+uc[!\H8Ri D:C!d6ٚA% ry;4:yBP)xR@\ RƩjTS5*.Qkmԫ8MfNEhhFyC+:nDw%JaEz=Ca1J~=+&ib3 z9c; _EBZY U| գWUGԨjfj<5rjjwY/i544D4i01VjYYlۜgK߱٣34545Ojr0qpns>Lћ=Ejkh4km8ndn4ר1͘klŸx$dI}S)4ti[3sf-fCZ||L OE57-I\t˝׬P+'Tj֨zu44ii50lmrlll9-/L6u}wϰ0ۡ7G+GcWLor ]3:B:;}rvq;7:$pesø܋DW'\߻9)܎n~}hLڙFY4xx>2yy z[zy~c#9[;vi{o?$L 10(pS_ȯvlvG#(2*IU<- 999-(yr`GryPGTԊ OR%y;mzh􉌘LJfbq4]ڑ#z-ںhT$Fg* Ki\˙S.7:hz4k]BX"\Ҿp骥}˼],OZ޾xEኁ+J_S}Ay1 W XPR$/}uuu맯߾sr}IERaofbC2]Ioot\<s--.zbFmmmMo*VOuw)y}׮zKv#swo}}9Fv~N~:],k@ Ç]FƽMpXy>t(h?8:V܌4/nmImmk9>x{{۱mDI͓eh OM?=vFvflŞ}> uzwq%K/s/\qu'u;w7_uzZ[̞S={M+=; wz˸~+?R{TXqϖ?7:zA?q)iŠ`Љak=x.{>>R/;^XW_FcG^_NVJ3^=~fm;wsw~08姶ANdNL%c3 cHRMz%u0`:o_FbKGD#2 pHYs   vpAg\ƭmIDAT(}1HpDEJ#'8Ă:894jdz{ob! "⿈a)vDVs*.~o{|oz1? 9V/" >'+7o y$ %pMr-I v|rJ&d\&eҞsA2B T8ΰH:Yf#l3%6]8e_ɾkt򃐿3I=QW)~K#6QnVߔ+:96qM:#_҄LtL-$4蠟;XF`DdJ<6.'w T @- m@n8P $ B2r22 t%[j;eOv$(S*@@&X`(ʑs`̔`)d` SGE3(xW\!Sd咔Tn!\]x87CP؄ ee3FvD9;:;8:|?E񋖴e /B_TBfgk+ m_ _׃  2r<[&q?.wL'bPGKĹi ˒$ IHk`~B[P. %w߂1w0hْ 4P6h>؀#;x̆P8XBHLC.,UP%BZF8-p<^0 o`A2DX6b"ֈ#Ef!~H0!H "ERd5R#U^9E.!==F~C>@٨jڡ\ Bh G h%ZBѳڋ>G03l0.Bx,c˱b6b#{";!0 $,",'̈́ Ba$nD>1B%+uc[!\H8Ri D:C!d6ٚA% ry;4:yBP)xR@\ RƩjTS5*.Qkmԫ8MfNEhhFyC+:nDw%JaEz=Ca1J~=+&ib3 z9c; _EBZY U| գWUGԨjfj<5rjjwY/i544D4i01VjYYlۜgK߱٣34545Ojr0qpns>Lћ=Ejkh4km8ndn4ר1͘klŸx$dI}S)4ti[3sf-fCZ||L OE57-I\t˝׬P+'Tj֨zu44ii50lmrlll9-/L6u}wϰ0ۡ7G+GcWLor ]3:B:;}rvq;7:$pesø܋DW'\߻9)܎n~}hLڙFY4xx>2yy z[zy~c#9[;vi{o?$L 10(pS_ȯvlvG#(2*IU<- 999-(yr`GryPGTԊ OR%y;mzh􉌘LJfbq4]ڑ#z-ںhT$Fg* Ki\˙S.7:hz4k]BX"\Ҿp骥}˼],OZ޾xEኁ+J_S}Ay1 W XPR$/}uuu맯߾sr}IERaofbC2]Ioot\<s--.zbFmmmMo*VOuw)y}׮zKv#swo}}9Fv~N~:],k@ Ç]FƽMpXy>t(h?8:V܌4/nmImmk9>x{{۱mDI͓eh OM?=vFvflŞ}> uzwq%K/s/\qu'u;w7_uzZ[̞S={M+=; wz˸~+?R{TXqϖ?7:zA?q)iŠ`Љak=x.{>>R/;^XW_FcG^_NVJ3^=~fm;wsw~08姶ANdNL%c3 cHRMz%u0`:o_FbKGDC pHYs   vpAg\ƭzIDAT8˝Kq?;jQF*t#ePXɦ]!!ٹ?`k!h͂NJUi0,BFh N]feS<~y/T*T(J;0'M?EL4WBEn.>@* . w(!`BG^-Ȳw說fkрZ'JŅYY{L, qhhhiUUsMw=eyX,j׾r>y+׮s:oH_p֦ F"ɖ0BѕYz@gsO9ēWH,KE 8TD)!|!fffj!)S }|7 XQ2@ilێJ"[51F>"V6dJJ%l0 &&&hiiݡ1^|,ӆޥxwm399I>Op?R{"Hwww2;;;86\zFU>@LӌW*Jx~~4 #TUw 6{j%tEXtcreate-date2009-11-28T17:18:28-07:001,%tEXtdate:create2010-02-22T15:47:37-07:004%tEXtdate:modify2010-01-11T08:42:49-07:00A{ݦ5tEXtLicensehttp://creativecommons.org/licenses/LGPL/2.1/;%tEXtmodify-date2009-11-28T14:31:30-07:00t$tEXtSourceCrystal Project'tEXtSource_URLhttp://everaldo.com/crystal/[IENDB`itksnap-3.4.0/GUI/Qt/Resources/layout_overlay_16.png000066400000000000000000000024271263013355200222500ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+IDAT8 EԍXfBk0V²Qhq} \>h1L)8~pYk#ocKIK 'C᧔DlB,pw@` 1P~*ݴ0 TvWp9n%-kV_.x|Ob][ .7:IENDB`itksnap-3.4.0/GUI/Qt/Resources/layout_thumb_16.png000066400000000000000000000023261263013355200217040ustar00rootroot00000000000000PNG  IHDRaiCCPICC Profile8UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- IcM^\~{IENDB`itksnap-3.4.0/GUI/Qt/Resources/layout_tile_16.png000066400000000000000000000024071263013355200215220ustar00rootroot00000000000000PNG  IHDRa$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+IDAT8SA _7/7jbԣMF589w_0{%`{ "Rb9gh h WR+`ed]&W4NS-ҫ[ELSp $춢*@Jyp8AbIENDB`itksnap-3.4.0/GUI/Qt/Resources/license.txt000066400000000000000000001045131263013355200203400ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . itksnap-3.4.0/GUI/Qt/Resources/lock_blue_16.png000066400000000000000000000034521263013355200211300ustar00rootroot00000000000000PNG  IHDRagAMA|Q$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDAT8}SNA=U=/!H/h41n7/ L\sALẉ{XbMjosO{Z[]]/?i-?+Fcx-a<x'Vk2IG4y^=$$8 F$*0 AD ^]}εXeU&gY !a-n`gME0j:fh?^^ 3et=H&>4M$ |N&nB?'80AUwջG c!`, ox_X?TOǘ|B>;Q 8_'xU[^(ڊU,cqBmBRkck-Ι* "yPEg4d`O ̥&] IhO*) DFʢeQHa۹aùS|; 4f;2 Lwy•I+5XeYniP@t#Y_43R*Bi8\~n\2%cݳΗlkvY4rYX:qm׆8?H6LJIENDB`itksnap-3.4.0/GUI/Qt/Resources/lock_gray_16.png000066400000000000000000000033021263013355200211350ustar00rootroot00000000000000PNG  IHDRagAMA|Q$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  4IDAT8}R˪`\$`. l_W'(tWPPRzTH ^Ѥ1vӁNZm?eْi򏳮ĦDQ_[7b񳕵>rR*.T^ Tçin~\އ> @Tɤpg"cZ,v[Oa gZ= Ü~>v\K j=)T*4䎹t\AQqtw v;z4 Dp>kt<IF~GOZ@z6 .g!HL$d6J%/ 2DN&& Lͦ Vb,(RxP/HY,R! @Y ;9`Ҹj` ypW>c~x/[qĵo9c6/Yba1v^DDa$B?g4t:]\j^@|s6}eX|V (BXNӞ8 9Z*m/IENDB`itksnap-3.4.0/GUI/Qt/Resources/logo.gif000066400000000000000000000265071263013355200176120ustar00rootroot00000000000000GIF89aNeJRP>oRcss5;l $2?IM))+#*-1A 31A91<6:EHNAy0z A:Ɨ)~@38T8D'UL<$"PNL,rihi B :D@81xxY?"@#aL4N@q XDa` EbGl) 2:0CD P b nl`1>xR݄%% H=€أ$eB@J RM8 45Bz5+ GPF)H :و !@'PPDR ]xl TB8iDN@M']t"RAHC`/("+q $"t}{l7`5Ab%(ݧ5fDj$PlsAp0c,H DCh ]% ļ,X(A<^BMB#1:C&6L̓# -B@JF|~J Hy"8b3( O ( (C(cLAdp |Db^3$j'= e` #$ jZ 젍%A% rϓ"@ H _`b8S!0&1Z`Z@(q*18ڂT9$#H< Ò jQE0$D/R ܀9ʂbIR|yl~@h&JS# AxrHBmA4E" 0KM,uD)A21@JTe`0șز,UJutASہ:KX``z` ] Gn8  NI1]M %/FBs5W #PԶR@E  q27 *<60!= ?C~b@nv@0I HMX HL;6$aҪg78úD8"JؑPYPD:*9OA ҴDvkj"8=p8H-Nd\;` fH Tg85 h`dT*pr-%$`] k9+7bpP8X!0?r|DeҶb+@sg JPDFDj"#Q&]a_J*y& Zv`@ [b'o (nw2gzҰ g@%&8Qu4X1aumsyNA XVHYxD2pSj"KY%iu2 0A&c!; =&!Q&7/iW7tAp::f`p@=<-:s .P:::,8F,G:.RGNh4k^ۭ !1yڕ/*m0 ==3qutPd0 F .WbbPb 5P9Z\-p TTN1; .pHH`NNN~: '0yy10s p09wQsd%u}{{?z@lPo@8\xbxkmiw'PZ^p^TM1S`V0670 m1LFpLFPH_FpF0PP@`P? -!_ y"g@0=`~}|@_t@q@_d  8^`'+! 5 QP5oSK0P0=1=K`JF0CPA0EPC0@PU; gҐ`@B@* l x^{dc@lp ꒏' $XArdH2J)QLY"1W5n&E!rCR)dH /OrK9 /gX:Q  $p@UYsbǎ0bwӇٳwS={CGjQ6x֬i308sn^Py0Jn;`\y)O@12Ž0QbIذUŽ)f̖Dp ;MPU>sq# (мn uȱcV؞;{ 3pΛ!C :pSŪ-S0H,,pe+娃1: Y|*2P#D% licfJ. x; F U$/:PANiy خSYo_TA+| _7{C%JTM@6 x ~U(y^(W"tNI@\e4͊M#ӲL I 3$Ԑ24L lG'8| _ ix8Ƞ 0cBP0(ʡ0hb'n.UK_;n iEia00VtpJzg t˺B9G&< =5`M@_xbI3jTȇ+jS3e:˒)Gˋt;dJYݨ0@#/vD:䱂;ԣp:C.0lZ/iAzb2OB 5ȅX6cqKYNW;,)OK৭>q I'|0]@  0FmLžʬZ *QxLd ]>^DAF4 -X%TUPZN H:PtTx :;(2VDd1bZAruoЅ6ʋ sޘrωvrY#jw:A sF9u\vpr/!]S.ׅdy :0) *88'8'@^%&.i3p r37ߨӉHh # $4o>_Y? Ⱦ۾nn195ʲ/BPHy*{>+p s'X'_7I)"@ Oh+7쒀  (:̊UȖm9d1 B =(B8x0'Y[ q.5I*-038 ӊ9،,9%P%Hn𶤓G!`A& N "# 4&C)fr(;j(!U:5 LU 8`s26H*E21.x9{e2:P9[ѸIA:Ó1k U(`Š|kd `&h)HjG(nP8h23x:;> 8P(h[E :.Kcc$XC+XFÁ8{@ܐtX=FH!ШAOL kЧw v`HxupQp.3p B=ȃ=(h(PB)KHx?*Lɐ->qŠuP3*,8V\SY'`â~̊V s:# Nt{<2$7P<~(\XPȖ01񖮜 =pnPPKjƒH^46K$&,02Ă6|m+Ȭ@p(ΔThG8MSJ$ZK> PqȅQ4Ko<LKT$taS(T?B/8T!,*̓%)I"%ɡ[COq#$ $ HU"Ѐ xn+RPZi x`1 0Sh4r`9Ё3:S:̉;T`DQG }@4ѴITr&ض3̜P:ɬ W.`r==bč/ SȜadh's(;5XAl5J DDI |ټY "dL7$8@zeI%T\\xZ^ @ʥtG1 @։݈9Q薸;S'k-*Ct9m%o-* W>WHpԹŞ~O'0R( {5.&jn e=|p=YD ͉QxT(8< <cc?h_% WDփ%݉j0m&)[1 hIFVx28 ےb2,uxQ4Ӓ ։9-p^ Ɉ^D,,i j9؁HOE#1> JIR%,42;z*xbQe:ʆy/kQhQe1o -Uh4<3&4@ќ(6?|"؍ZF<΂*9(}}Їz_ȇ_Ї99cCpnmG($7XwnP g}3gT;>J ``28.[=Qے% 5j V=\пc8мYcb jȁ&h'H؋L͕j-pg̀eiȆp(|rott k1HE>X2(nS]ߥ2 $5eEc΍˂^5s  }C˾G H6_]i Po-HE=xK]➃ !sAn3¦|EOɍ$@Tm$VdFX>bX?> dȈ^EE响|5l L]FQ nW#e(D]nny SJ>V/7@0 WKŊnƐc*@捐p6f_H%P&HӢP_9s<8ZQ.UepP^atI *i8,?LIW`X:$PUU$X?T7yoh_nuWr=` 2`i9P_o,xwxVe_v ix?x:ȵx/y 󬰹 h Rނ8?pow+W۬hNn0xV`QXQzh {hVXgoU`wІ6{i`PZS0WQ0U`-&zS]p\v\'}]5V,= /V|L}'8GXׯ}ۿ}J}}}}~~/~>{8~o~~~~w;itksnap-3.4.0/GUI/Qt/Resources/logo_new.gif000066400000000000000000000667411263013355200204670ustar00rootroot00000000000000GIF89a?KY_UO_&&hkwwg c%}2i&&j&5g44x((w(7y55k:U^^^zII}YYtNTuKqzYzpFkiii|||yXs]~~nbw-5?=z~lvxyuIF^WYK^Y[iihlrrzvhfvny{lghkzuzwmw  ('(()166%%88%4&(44((77'69F;P<[7HFGDUYYFFFUYY^jCbZfUyeecqswghwwk|HHHTWXIICZTSWxQkhhxyhhwwa~  #'(88%&6>,6 $$23''66.23E:F;F=@BBSSK[NiWtTlbIUBBWfXW^gwmsjs[]emkuqgwŅňԚȘҌāהş҅鄭눹ƪţд¡!, H*\ȰÇ#JHŋد߾} v ɓGrHaK/v)eL$o*2N)WIf̙4DgPIW2-Μ[~G4GUYZɳ©Y>kT#kTC:˱ѷpU mؔX*q 'Mܗ]ʈxe yz (g+_=sxavGڣom_qCmk+\;0޺Yݷr۟'v`O||G_Lt\53G1l 6Vh!mBxD!X^Hbs+na J(%8*[袉TSTc͋<6i?(#vM#Vӡ>ZnZ&\泏v%>/7ys_IY >cz֨`y2% !'Z}N&l2aj)gn(zVM*fD󪣖fx)>z]zʨj2*N 8fJ,. 磂"lnj*o &5ki&:+Ķv.˥ ` >D3M5+INO籪^)i˛i42aB </y"3)R?w)#-(|ʖJ]|ޛnS$pz1N͒" -_3 ?e>,{$Dk h;4oZiWS>uG'r\`VxGyFs^*l*xjO>f嫂^F>򐋨0\L{?'asy*s jzL u8nOꗾ rF0"f{S~5,Z~䗷mU3|S.(\ax2ڶB845;әNP28bK=r.TCθhCl wH+-k(Kk0(B9cß%P Qf _:P>̀1epn: }"*0cu/dU3,c"=̑pb5UE˼D$B-pYڠK.%+״ч18]L40FT( a!4 {6~icg%*P(D|j$ZmI#6a5RT4؏~U|2F4 rx&!-X4 N w(˧P!Q KXTx!80ܚ%j |z!UՋgSYz ԇ\#͓dfWVkrS tˠuo. M0b 0ZGz2 dՇ3P޳WJp/r!ЈjCV5yZdKZKT֪ĘKq MX`Amz[բJ?dK(m'֖ڰa*' !Z uOoƂ `Ml⺨-D"Z4r!PUZ\vqV"3ELԺE-`n"\ ƽ(FO4$kdIqRTEp2q]0Da*feLI:&k{*k{Z *R>ZxivfzP>?3-&o ruAoiшHָe'1M^1d"NwXD&KeΫK?Z\L|Iᩦ*m!JbM4 .~{ީ% a8uepЌ͓_n@A}Q GntJ[;/ȄczA,v%/V_ 3j mIF!9>-Ŧ|vڒ =RrTce!f(!FWi~#A+; ' ч3DÖq_I[z W :瞊T ܺUf˹ؿBwv>%wyj%F.*E]H#0ENKb[ԽzӪ,P! E40hcF sB \ATI, I_=JeXD[tbDQ]p0L)F[+){?o`:{-bi";Zb+ 0E16J67Oޒ?Juho"lzPC>SPS#g/:3(vH'XLBd{3Z7pfG%9 ΰk(փfm%foJ&pkfi05V w``t9BBDJPvi~lfU0JSi0=lb1Z%i }0(q>00'9ps1"6G5\pV/u^C*Y` rrp&@Dg;P[fth`iIfR_fE @&(PbP[p o@T\'{cjf `Po:8؆mȌHfSm<[(yeY0ӇiLw|*Hrl&.X!6.Qkf PoCi4sF,}PZE8w(Z$Cx EH:Sh)d`O8q bn @c P  @L8to& T,i[Έ~_lP 0{ 9BYaRGkؔOR9y`Fq$51zK7W_08<~V&d[2s2Ywՠ``\m@>''xRzP8;JYw~5-0SQ mTceB)U4X f^ia d@ S [lR p:pEn z\iw7 cl^9 o _"p[9ֆ @ٛ;9ٚMV0$0cR[2!KfN8W8 oXb @b0Oت&Ä2#gy%,}0`a*QPƠx'g7ĀӐ%5}pV KL$ܽ{+`p4`m u)>5~XH&.YZD}7g„zz3 6dUz&3QKL38ΰՀ^5@d񌉽[ ɹt+U8%Ԁ a҃Z50 ӐԠ׼{}d_PS  q Jc@r?TH`p&{r _ T҈8fٛT`Ki5A CژI BK1u(z /c7ti|+i>ːXzGӽC9^ŽfoV%0A4&[B I%-ݭ9̘[R3+-|Jڃ6f ;61ʃ%^3R8kNÀ7vkW9B'tHݒb0pUY^ g'SK 00<[,3a ~P9FQ躘P̊ 7.tSx* 0:<[!Lm% IR+дq{S X`]^NAJfD~^ @̚ЄB\C .3 @u݌o>㟟/}%>O̡.IX//0` NAcRzD:h7vs]zro2L2i/Â(Uy [.)؞$T6*SӴ%@O&~8o LpH7AThJ5l_pS0◕;'.~E79U_RO]l(0Mx@@RxwL*8bF~庙{8o ?{dZj +Yl?9>zB&bw2O_5֘ɑS玜7wY#Qĉ(^$8pč/GiђadǑ#Jv@ӷfnj"?^T6 ) y͊EJ\YGe 0$F|D#VjEBolQjQiWEVRc42Qжn*׏t:uɑA#j̰@M<1+H7Z0Z%,O>vј路_nŽ҂E 3g|`|-2⥒#.]o>Wzԓ@ Kъ -ܝ=;(wƧƳD.omb )H@,8i>+ 꺪LT+k)a'd &4Z/Җq2 0-xl(ԀJ )i*@DGDmG(@ F™QO&H/JSJْ0!3;Qb 0AT3) fͣ-׾ PH@a!eSUŽoH@*əc1qU$T/h-ƃ#2&F4˚dؕ#QF,pm%^G5 Ij8rƋO&摌d!%XDCz@@ ( {m%B]c7@a`j"=")՜BF5HEhF1 52CH?J[f(P3FsG*M27S(Œ)QZ%H.b ?-\SQ@r|PL @ ( IMh"n(H k _Teb"XûPvgԥ6U#>(ho\VW;}$z?1eX`hD+*Djx1AYP>*::f_ZS# CTҳIFNh#^2XT4Y1 IjW@&͚U Y)eRPw \2“s Gv8bN C$*ih1" a#W20NՈEbkx)yG,0L(nBIug! XS%Lt3dHF1MfD !xHL/pKy\D ݺpjw2'SFA ΀q`nt|2=sE @kFMp4Ql[:Dan閩 L+PopvQ~MpBr@|tl[VbۜUMZ+b OV$#u!h HMԅ<(;>/@ Ef}j B.bywG#ܜjnVwI~9hƗ1-;n%JDP_Getei0˗{Dcp@dj+%mOI4LI^GҖ{ b;Р8gPb:w[q$m6|8{r6͈33mj!*G>n!kI>%H&AZt&" (A3q)@7|؇CӠ =S(*Aٚ99Q.@n-ң@C1h#qpشS3h^AAXѰy@- [C$@[➒FY3((I<}s) 45A5PډGCɫEzȂi¢iAB=%)?@8=ّ>_/h)hN)/2*c-B{pg@AE9E*]A@j`B M#Zp63RC{;,9w8?8{«uH@ @ā,FXD9R-6= [Ť 8(-He qAQ;V2Ї)*ȔdpK:C+=IČe7.A`eڄ,).dEx\` /Jc>9DJ:EˮXH#O䪘8+8H !/B`܎{>"#Lɥ(ȀJKɈQ @&Eʊ EXO V@͸;ٷDNK ºd,4Вa5J k{DHx tRkOl? j4X2ER<Ǚ5RiQ(ZiK -)L8݄Q03QJM7nJ SK,EX3)f'-ϔHR,}H["@,6B(dRXT`%]QM 1RvR܈Ոd`] 32vhHͬηGǑTwB#7BT؅]p71; ;LUZ#fY*eJ\L^ 쨊י#0`OD$.Z"ZIkdH:a JANFN13CI{3?yBJ-L]T!LH(Kt K5R<άT\Y1:r, XqRJx[b]*Xu sH &iDFj[C1p7 ywA?K}NS ׂL\HOP!0FTU]@#6 LZXep 5j8L oqAұxn`)X1 ~.ryޚP6ݲ b^ 7ՠu!`CpNuɺ,ϊhX5%_~ C ԍ<-E-l^=^ cwEv&IT[ ņ 5 6|ۚٱ؁Szyfdбa4grdEwPׄ:ĐFJmSUh4 P! [yoS)xTO Lt V`2kcvhl3 M 5Oxtrrnhi;SF/Ns aqszWyp>0:Ub{oTQS/1P׷) )M͂+ :0XF1 @ B01diӦT'BԴI%K0) _jFi>vՒط>'Kf-dh(ED.>LTюm|5͇*O%CP}\G[i2Y5֎*sџKQړk>DM[';!%˫W&b$/*VȵLSkblyfqТ3&Ly"`1*BP n abgyuޝc[|1CF0\r8 $AL,|ѐCTmP,"#XD&mVTkrhގ)K.HYOk-MtOxIL`tRWT|{~gkUh z#I֌d5d@5VW?feɗDV&VbzWQzw*d\4t$ 0לSѤ&0ds, eeRDP9R&BTD9 Dmri^LN#WyHe`O25M2 ڥղ X}NM&MVzjaH&bXdJ4kH="<)&'fem}}|Bˎ!"d4Hx6꠽Axqe(%#5F%nVwFJđpDp<^y-5TOt5"GrH*51鵬qͣRNcf#Hkƫ`(H" -_L`G#VONMG ,@p`!"hwm^N`H!B1/dW ='=tUJ X6 T43r/ݳF} 3b%z)!L,SQdx2yg/"+3¦!!Mr$L#m{6 fK^bP;@a8y#jIɎ"fB>`Eb!}(QFÖGj;ՇB3!4?! R`2Q!cAՀB˕rX;VD<28yte"ȃQ*) İ- 0ұ|006/yTX0,yCMy젱 N4.s/ :b#Gc8O2`.O,e,V]<=Y20VA*ki)a(%C"/"PZˈG3)U#PCt4!'CQZ5  ء vT f(q$"~Y8$0`\ybAV'c5EY_$J>g4I|,B)iF>(EW+O(wX uԞ"m@ su#ap ؁@!ҷ:MІ0 t)ƪ*VW )2DNY#X9&,]+()C6hrD Ųp1' i݄v>٪qݦ9P8,jB}Bp@,nn6b 0{azSv `FB PO!XSA i&ͧeI&SiCte`DfAPWcW|PPձ7P! NRaP RI c:C;ŭʀ79Fb$Adc@/JXQ ܀s@ s*$&O Ʌ6g)[x[% 2̧D՗H2F3xe1ə0DamX@!J^46ODZq4?:/@A-Y(5H7Nf6( f ,+jn5JE&G&=C;Jvidc,4ƺjUChҀ8 tZ+㻆N^8C!PP$A5M,DVؼH ,DW&@ƘVͶ%ئj=E* Vc0& ]߭ d骆fN8`5)*ܷRLsA7JJ\JjnK$ 9Fi;0 pW\ekF=*r*vng5dh,b5Ȁt: ) -F0 HG\J_טI0A4yW~3FAp5BƁ We,NA˙kB+rFQďJ 8&I -G}D6P#w~ G 5tb 0&AO.8/iD/IyNdrPJJqڄX" g:^8ݖʕ^++/t@MWi)8O@'1 $d @)$ ͞`)uՈ:"N8m$G8r chAqCKXDrx'-)™5 $2fLJm[;{[=Ch}DM\$^9@IctΗQB9#w0 s㭔nCntL9H8eÓ rHNȰ:Ը:Lc| 4*LA2C %J6TZ[c&W $l5j{ҥ9vliW 7ZYډڎpw O#IMڼzٳG^=|u`1I QSL;U"Ͻ 7nIcR8Z7^ơVY/I u?C}c  FPFB HF{!t" 0ˆ#/,B i:`Bh!|q'ڃp&X"rJ01%zK6#laRh1b&jz6ꙇ~c'd40Mڋ 0̘Q*kEk̫i~KHsIr3plS« xd gX@ rFzY!`z(Ϙ*Ë$|TJr0J CƜ@k/Qw|̿54{IƗIIdE@ E3Cl1 (@7D( 9hdZܧlUH5kԩ{`+s kz B93pᳪq+ɭ/IK&хQ]86{ᆷ2L窏@[EujqvHb (:tmd Aqܡt68aV4zbt6E1>UBcOYG`YNk"a֮\4Zss|Ť}tIG]j&׋F4/&< \]qUg 7#3`EU}Ϋ,3by ;^_'D<vfʈM@cz*c- !+l uѼCLҗTl24ў3BqB#ĩ) K3nz>@zBC"(v(9~<80J4k Hyi*:Cr}t Ƈ KB8H^Q€Gr $F=fH!шBGP.rEhBw::|D#@"&t7Qjr[<(QI0IR 嶟 z:JL+㴨"kcKPly0)Pxu$SQʠ+x,(GSNs7Xuۋ5aEɆf?bbтM(be("%=SS~@8f:oC=R!PGB]c hPUJ4]c@m.[^$mNuzfIf;dv k[S_"yPq/ DL.GBz0J(1)0 u1AoaHҗt8_!iRt/}R>?z-{P 7o/ZY)a64;$O VNTv |0PdJ@Nx(f5.cShoONǘ tAz8_ȃV4ph{t I0v E䎤YxjR_ U1ݤ-g,EhXrbMLc@c &m6Y{V'HY/mnn1V4,}P̒$?'fwP)40i4Z(̽7!x3; }PFDj\yx%qFHa %t,$(x0Ut.b"8J:B;VbSr =mdC9&P:)HA1"%&~Lg@,bv<%"I8=뤡أr.N/c0ІX"gT2E6m4LТF XKF,zId=F0`~aza>gE[Έ?0! B~,*a %"fAFDDBNtJCzl,7t*BP |!4CCaP BTTɫH,=n,b#9aJBVæ*k5@e7No8IM̮=~nfT'=\-X4|OZ5MG#nQAaR zaDhFCr @ə.0HY8 91f/Fr? \B!4!*\D t` FJBuD} mi/ 6фVz!<EP`%yCh$Ld sR*f#XȂ/*!r)H.P(| pڃd',WPrhf|kam֣!a /-~P݁`a-n2cl@o .ޒb -!({@d5!h5j,P ^LY&%Qx*nD>o`'Z mܱk fTqТ'Jjf&.q0D+cBfuj0<ڃpkx0G&%f s4h@TĨ?؁4a6 Gj -A0  A%c"=D6bDJ8aJ((dN6 T60d&L"ˡtKdL*3 RFZ s.nB| Rz:| je#*@W PYv4<@,e,l~h#RATR)q&ƈL4beBRö4W$2LnNHma ,ά6tK!u4uTC@#@"*nj ?5+ {x&J+<2,"lJMf&Q3'J(5xa)4FEO΂+)KdV.M/Gb&&i#f1ktIA6Te`U6ᝂ,L-N&G@NvL!,`/Ȃ'J!(dBĀJ/65 RKABIy$ )Έg b#ϸ U3ӦPdLr'rNGq$oUZ1悯*'of0Njb=Sks~W++Ncb᳢g0UA7(:a2nlV X ƿn>s<9J ٣BS"Ur&Ǘb,QAXd7p"*q B& .^] ,͐M 0!Ә !-#l|T%f0Pyh(L(&ș04`X(P7e̘$0(k7T$QV$7<̼zO NhB2~ܣf=n4NPlwt!LVCx BL~&jv'[DAt9LB@.@ZvG$" AT#-At.>cQyܬb- }awmk[E6'zCeiP$G04fFS!m(Hk*0>n2Sxg*1!dOr#NM` U|k7{F6ÚwTajQaa}R|!!Ȣ0*Jhք̵t,?pAuSaR!{]$]hszAo ЖG`&a.Sj< KESXhi(~ܑ¥m6lEK2w4ݘQH#"%?PQJVIJ6 f0gb"+8˷+|e8N }wT&cv z'&GzY:cʾ |ყ\Ц]۾ D~a@ΩMb. 8=u#@4Xv0SnW77X>4L!~b9EA3ԣO&TL166‘QCL-CVUhfO0H1D&m9fR5깰&+6keT+ӗz]H`!@ A@ ^ RJypE5!@! %~ŜrbK:o:shReYx5SlGNb G";w#& ⡁4CmAnO V`0Is=,|BY>02O0S66PȢ0F(՚9£au)5TR%dDP"~ߒff'ᳬo>Ȧ<9',5uMW @  H$ ԅ;SAx( q7 w^(QʁSX#+\l)aî[!K9'aO5P#q|韻1X8]=w;v8w{8={+XhѤEWo{(aʕY+""E7o'NwY.bԈIqt*s;=d-zǒE!Zd8&;kN//p`~ /_36etɓHȒJ8)T5UbFJٳ6J}þ,\ΩB.j֤{y:"Kڛ"E4d {oTxF.e~P)AN==%:Ht"#/$C;TЅ$],2`S;51^R39ϱd;9T<(NRa HegSN .N5DY1CsWM>Fwmgn)%l&8"(p%h2Zhq%T' [mS[4O?ű)_#LZhM WJԬ%VNMĝǔPRwd<ܗ5QT\Ͷe&u][tpNv j'R1UMTO]G%_Eqw+xpҳ>sLxSH$N9.j"{U!C@9cH9dV[QLS<#Z$_vV~\FL}IOv`,妒3d,&I%b2#l蜠q%4#E֑fEll*s`i4:{;  GQ>KuGk= $_Ro$R[ȔQK %x mS].TY5Ӵ;!TY!N_3KAtM%8c÷8 KDOXÑ)CPDzGGQY9{[(vo2Sldlb;[ScL- 1B cCшK\&&jHM֠  Rn&6] ~*aps?yI k5( uE,g$Unh!U1 S9Z$V,<$4! b  !H%j V w&Sx5p';!qЭ%SE@ARu H$P`C_yE$s$C1߃_b4y| SىT6` ^`.3E(p,#FKM1L/a7Gor{ьl*6 &6~i, &2͊]2B4Yp;TCk`a@mO'f4ۭxsB[A t0h ,.b<%;IA4 qLnc5e S:B 'L'>mhcJ(%w vƿutt&k_3yN(X8#JJTl,%tdG}M\$G%AۑsBEL)k3 0dC L>rj|Jff4aI-1ꀨ:Ҕq]bo^qBKHzrc ySQl8{% U,bM#ʴ z}S=R!x٪Cq1㰉J!WaC9?s wBwA3+($1fBtζNٕl83Kj~a(li5m mppKSƀCt=4+AX|j8! R/z^acc}H7ق9$CUgNdqJ>xj1)__C3cHr92d pDS$M)^,̈́~1p>D C/7Wue6&l&'9%rb,UP@q@`g`Ropwhr~*W֐ ?~W&lAy9xpD20- HP#-E2ə;h9 Xە;c C^B127SSE59CdF8e-ݥJ3xd$#- m%\ҝ&!WE]U%^r2;&`2x6׷0(;(_upWtNm &tkkj)xh.?Y)Z:U&ӓ2J:$6tTa!9Zg e dlQ(R S,\6Tv.3"tZ &:#DT48uP#ӋRētyN)=)?&j Khְhq36aQe #NTV4( L"3)tU%״|}7֋홧"_pӱ& n׉o;Sr'RoatUơuU9hZ'|rgE5GZdPZ*Z9Srv"Or!SFdSfv|BVjwI_&z*N/l-*  K V}BV4D7dl'DƆ•z"kJ,wE,}"*W3;^Jho2(}%:Dm*Zr/&E/h"f+8ӵfۭ⑈3!|*J2 WzaiLڮ}Ӏ? C5Sc'@35 jakU;ɀ9: &Қ&5WE6\&X9xz!rDdžrg `N?k'7PB74Z'"kmKBL;Ur\ C!x)֢ea[WJr~C+{T(4r(R7~tT50Yd0"3)7} XL0,q7ST/z$Bwu|ySx7n˖ZQrUUab@ \Wr=&e9i p@Lǂ5|A|0#jBhogS s|*dgaxAĉ!쪐M zڳ[\d㪳)Kx&[5^,̖+  !Ztu/[A!?C9]z1z}(5{P*4it8b8]ry UO[ۍh:j mx!DЭhSAUZ}笭6qۂɉli'&meΙjFm!rэk.H|JZY_sݗݍ|Oq3b &zrF"nNJΑ$; )(^fJA ΋._IΊ71$ iE|&Nj9/u fkKo JΉY!ȋexq9%-T R/T<F6ػpt=PKE2XҴQOB:t%82^˸saBۓ/?x ړϿ_mݩO9= nka-o\J#!ʥϿI?%2v1k/ϩo3u oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  @IDATxy_u{ff`p@\Dɴ(QeQDR/Oq{Y|*U˯URb9[J(bI($"3 `qH;>ݷ=۷5ַcѹ333 B(/Ǜ7//ǙrrZ[M[n] :KD92|q:׮J, ###)z<g7_2:R ]9BNjEi29841m|1?PNjNrwKX W%\㫕 8R`ݺےN_IoI9.31;]KE&8cB/7޼ ӓSKqȡhUK:=ԮjmEcN8tٮj[V 8_RF^~ʇH2B\"]Ap% /9'g+;j"ʘ5q?Ľ_JtiX'r+D8󸳻9=Ǚ/э ZR`^gz|Бr\3+8?M3Jq9r S55Ѿ{WcTBbZ`1>O;ny2A#X-áq^d'4!gnyoF3ʞǧ')V1']- 2SRrtNLDw6_쳏Z^xB渜jx&8yFwipyXa4Ky. sqgt[n,$ywZkVX^^d>¤-Dopq8t8yߞI TAkMڴjrLs?PO^AHyv(^ufbYb r!ydh.dZf"y,j:nhkڜ8c· ҕ |_>D2129?p*9ߒ|kz{c݇?Q1[ǖ5662<C5WPܘYťA)(ӌA>3zl-[S_^w iHon<ߓ'tG\X&ʷ+O.pçqSN+F~}F/o` WX/Byt\$9=VfiUj{crb'&?4*Ę@ a6D~r>fL "Vi7;xToŽ B`X[>jig6}[&0@+˜мxE1o](Lْa(h V&Sʷ7x=V[-1>5cѥtߧ~)6k1ϕ+"[xNW|Lw^ }9ʒxj[ts F4~>y%fzvžOm05 /ZV]HR{=ǧEU))rV,,xNJl+PTS.+ IbQhRmlQr@i,.SPtVCQ[,I)…Twh%B#}&x;S]?r֨Bd\~`ۚ\hT7ߡD\UAȕ:!Je=~}OmKqOMCeZWC&MTMOONBŅ SyJn\k }!r@Xl|1֨FA ? Ei>nxiz_rڔ|571]Q,x+z@p>PP8zpRҦ_1޼>~6:~1[Z;c{wri/VT6_yw/-83 cdt㳍%7Ζrj8z 帶ާ4#T ߫[7T+ӋsG~p~ Qߋ{yql6r0{#jO0;wNKM?WVʨ0)+4,\kdZ֩t\0ƕ2 vS8e5☚PR_弿H4(`b[eTPRe2qu=if>U\+Uk4IM7$v_Z.ƽ~B[S[ `ˠ6_'6<5Uβmʦ'&PTb'ߺ'|'5ﻬ} ި8iWΧ+gjh"CM&L) jqT}%b2rRv,ycbb&bp F=luV^*Ǥ.ώRmZ+ 1qr݇Q) GP1_oㆁeǣi?NGf>zܵJ ( erXui<<9T}2oQ:,*\Gd0ɴ~>ڷO6296wvn>̭eI0] [ =:+WFm(YMe%Wqn(KLGɌ K.RqR߉(_!+I@{[SGL~{<84bn([5ނP4y1եBmj*yy8Vb۞4i/TȜ oa7 ĐEXC1Xy[o;ƩY-:͒)@9L \{ԴƆFW}#^?yea‚ ^3=3bJYm7 +iI0k왼;dYx)*=uH5q)MMЮn?3O ږ*LF |RF L< \ZeSX.O]e~T{ʮ0R\@?}*WG͢V_vUi+ɀ INC57>"QASاkA=E 4\a}jGLu? zB"n >.h刬To=lZQr%J},e4h$ksoҺc/ր^, }z}1%8z4B9?ߤ<SZFIqY_R/S2n7Ǔ.ZZ }^cZ걜/mkDn{ _t7Gk]>?.j|nl.-a_xFCrK;ѹRr1Fst{ѼF{h>VY.-*EϞ9-z0| -pxI#̹RqV"*JTIEZ6ރ!\+1ghp8vo>EŰH7B"(Wv 4˕#+pup \8wJ]Wo4xpRoUN Wukdj2 k5sť7ͻg}1?#>fW@C}Wf_j;o^{-ig<}$V=+joG^|5粲bW`h8>] ޷aRwےFdReYX>?71|]Ѽ~G\ҊO1Iy3*=jwztJd>dy x;\:@?3$ 0%`HJ*6( շrN/$|)J3u4Z&h9.WxbS qʤf,&c>W?p v_r>ryޏ617>lKv?̟{] ~;v H]cl:SKs1W# dRG4y6Vg2maX#}P"qV&dDfxܖ0oZ\h{D*ZSY>ud 2ŅbNpLUڍcKt3=}9 _uxڳZs"H=׶,yױz) \R},(|.Ι;ѥssH:Wb;MieӘ|k5V}ݡ'>Z] \=Li"`+iߪ>E'JȀEzɚ* (MlRNe#FiDB{z+Zm| v H< \ T.<|Y33DslRrHmǤ岚pj 7b'>[?%EbSsK`zʆ8n4kjOY隺 nH\ӱ<M"7mhsډt?PIV!D )Xd:cO3z&g|そkh=h(umFEB˙LН& +BN++ņذ,BߕLȂbdL6iX~[>HR/+*DEYW5:5<@0=Gqzߋ=D@ݳqSk4<3eeqQ!DJd)%=Vp(BaIGU=k|Gu\qudvR~2Jnubn2OgWUSbaQV;{2-٢G"/8+օew$~9)  X +1u3t({|+o?'ScC?/_bF_=hf,B]7!u0]:-4y,>Ư}S)h 0ԏ7x66m:Ҍ2./ /q h))*oSM2{<]IK1t4i6"!7PP9-xCZY74ޮU2oh4Q5Dž5غ ڜu =LxΟۜ[6w#sRqdsY{pk (^K>iX|=RBmA9ot <4vШy撅C*D7iǝy&, w1e44մ[6C;Ox+N^})Ft@hȨEeUl8rpwڡv]M1ўu\&<.eY 5zvGG g1zݬ6d%ᎉ~yr㲙dii!76] ӦG7X#З^Hҟpz6:r9yCB}gG>mk?YYR8,w(JMņ(6iWVRJJEy14q\1Мa [_RBhьw^]7ї3 4V*N~Ba%N}յG6ȁs9Q˿Lǚ2q+ZQ(]234jOP!BpE L+nӫwӜ&d@m Na.7=@@ ˬ:ģz-}$讄: ؿ7iQ]%eH蘔•if@vmg!1c^Е=p27@\VQ0ҳ[%_5Ztsrg=B-b~n>|+2'd<VTQ͕>+i*V F!j݃ GhUñ>O#!YS&,ѫ/%<7(68_p L-e0V68e٥"ǖiZ.cXEJaCn@p7ZNwyB2Z[Gu=ѿ.ĝVCarm "%o~U~`X)\F1N04dܩ6}\_ 7.wҏ&dup7"9O*o*+<#dŸKCr%ByV&tΔeEvtvdZ)sY8iܻ:AzI]ţ}On4ʳ2/?杹3lPzk)~umlTc۾Dv<_|LÏxgDcsB[imx|ws:7,Lx 3<(kn*Z:oVtҍ xtwjN&ʼno|#~M~w$-++*O;k/TT4c42n_Ï=cg!;$]by xzdyvힿx6qLp ._,iW4K_-$TG T%\V^F]lސ;`yXfe6ӞzpGY Ee2OEGAwj∶T\:DFxdl4ũǢÛXS̝<ެLT(Hs|~M㲉h5:?mO4`3ueS=+џ/Kxd~"^U f}Y;uSl+(U>8cnOt6Vyŗ blCL>)1BN˦CL7 \M9Ǵ\)ǭUjrOk8Ս kaOm̟,JAyn!2.|NS"c+PE-HݿئxQrx -!Ej<.뢶7?l˥P>ϳ1+%9N0MOkc撛ܙ1Slz[r~,4 -8%CTDŮخuA! +ޕphOvoI-MDG(Ug/iZQ{X:+r9MXTrPl70ra(ѯ`Cys,o7/=t\S>/WG(ҙr啸Y<O1+O4ǁCQXSӢ2[1iCJ{ %2 I>1nl0o/r2C9%H pa; w]py3Z87+p?+`i/-L-8UR-$2x ;:N}4zm6R/C6#mVwynئȜB,0 'm0餋05QYD2Y~DlpR ;tVdgP\5xXvI@% Z3ӋQfpe1yGc[x ?I%4 xR(a΁ݓ'1 &ɷukiȔH#}af4!+ Ľ}3޻)>o4QdcP#- eY 6ѩtta>}S"EMX7{[F$JίZ:R9/uq(8ȟq1i•diTg߳< i%߼݌w+BмUq}jo-ߪ` d;f3-٦8RyrhX^@o /U+22o{9YBIy7d?2¼ 7d>_KhVg]yX2( ?' C3Җ@tZLP, Gmم-;JiV&ԫ4j 'vpg>-n='1?Wfd-1 u b9yCź#-:tLhq#fuܐc*1UR $+ 8V8Nq|Gߧ4*oT%&ksʑb@u؛B>|ks'JE4ܴ]M,e4ksk左b2F\LU:[#4\jς*20\}_,[hnU,r||?o!1F=rE^\C;7#]ypi!#\G|>pH @gɦ,FʜӒbc~6pze*ݏތb71QLܦ}3E+9ّa\3IKv_:z,]j~ҸXWնdw2fs~h-Ef7D]@ȟ qδ< 'd Lx~dF;Ѻ5(S?׋7)Yr`XVod -{2?S_sa࠷:mUSŸ;gjw ވE&`EU`Z 2ոIDATJK %+P%Zm;=I9ڼ 8m<+#:lIʭ^*=2j9>V.O] pÞ{?qKҙ8k#o~OJdDwەn[G'n@u^YAsXaR "hp? ,_lj*6GqF^7n[{$2xR0m@< C%K=|Td(>^u/o՚no[GIH4, E,R{RN=2Ӕ*!\w+-]`u~/WAG^㹒9\xm/ >߀u]I'EH :$/ۼ꺢7=pzHP*zN'7r*FmZ_=_Q-.٣_DthD"A-+:y8iAFnu8,*ՙ f u.(V+2&5nӼusE7~Lhk6x&64]w?x'-DlT߼Qժm[IKZWWqjhnl+:׋O<AxvkvGOLn|y8\j}eKBo۵}{<.t~+].^P3i-*]([nR/Y*{g?`8#CZ|+N((?2 l[.ޏ,uUٮ8,Z Vx.tM78-R9D~p{5o$vdϞD"!{ɢ`Pc:hXx޾KptNϴ!FPeW+xy*4¡3TTsHgE|mnuJ +ZO 9E24q]7r:$o,q'?:l}ڤL1Zniוaem&ie^dPC V}嚎eES EX˲1EprK#]tB]NJ}-BXyC$2@nw=\^?p&3h 5(xʟӰZȢ*Ycp݄ԟjwH*2Xq q J+N:<(q<.){0O.ޒ[\ kFQ4düN|!p~飻˃ڃ*IƗ]%KHE \6IENDB`itksnap-3.4.0/GUI/Qt/Resources/logo_square_shadow.png000066400000000000000000001012221263013355200225420ustar00rootroot00000000000000PNG  IHDRQQ$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYsNNsjh@IDATxgfusOO gIE*P+}Z^^ūk%L $rzs=s1@C_-Q}sN]vӴfh WMs+(nen8~߫BZUߓ6ξg?oӻ |e>t~s+j O:HBe6fyoL_ 3Hv[>hne#­~ݪIvvIBT3vrOwrZ Wݤ^i¦vH{|6}U]W}}/]]{MҮzpU|nr* +w]^ML{滦jve{F2W˛42^힍Sݻ&^}7{qD3,xIKrKU{Os+te[UyO?;Vd8_nwg~?вPޫϙ5>[g{U^exOKsVt_|.[ƙ~nm hFW5 0CCCMǎmnni{{iggGvW#sUVznoo_OVrIsW>jfqֶ\wxٸzjɻV/yO:%<[3%@ܜζ ݵϋz80i2\>={ݲ?5߳2g-o~?J7{^a޻vј]{}ư߮U9=?8AT+ ;tP_r/Lr.𾵵EhM9F޽%uu~ukiWf،g4հ[ooo,..^wϼg3ZU/G3^sWr2p݅߿;;;===%Q G,ɼHo}_?}HT: ^5IX\(__D9adM nG,7E{j R!`7RuKF;GkmƆi7' ʹ43 h8Tݴ._@544y[8xƣ6g6uȑV&xtK%t;M.u#K `{*ǻ%5Y1>MHv [@%XneXQ{K\ +Ch{:}7_GOkg"Kw+/ׯg>?m8M[c󫝗epmIo=7 h?g0բ_X`Nc4PBHB0%h%@ڙaްוhpRߍOS#Җ2O4{lZ%/QG&٫Ke]ݴ&Mg#Ȗny.U63;}т+> W3-QTl27*w35;2W:{d%X 5j,`$0Vqk cśNMtAm./#G!oRi{}-5r}dV:YcY6 7My̛v{d,C_0jo|YI+2J٠,my(ǍhmkzCpv]ȫmpz-͒Wם˺lk pwM͊&}NTw0^Z|6|ϻaEG , ZB$N {^* ' '11Mgsc3峱+im~c-VF`cw=ODhR=hWc+%7V޵{,ƼVrdY8 _[ {əino)K%.̣yX3qe )y}bLh:ԩrË ]nK/ oRUef\Vf!$$v矏=f2q,s)ہq{Nё覲j] sV&[4>g=xlP3vY6ҏU4-puJ݈ʟc:6D W[ag=.lQwE B[ V[>c}{@wD> sQN0n@N{oFbyi#Vp/}_6N(b3.C.Yc??W&cx™jAH8DZSFWwTVKE\LhS$,Y.+TWAw˦`6L+tm;'S' S2/yJ}k:ITg]KR%gI{ZF`(`Ɉw:A@>[D\ }.Ѽw$V]*a?8 a5kt-Ho)"/@㹏ʸε}8}|Lwo?H#A+&d9tM;=+M'}8T1 ^n] r Vx}uZ!&B1PLdU[JdE%TUZeXi"3ɖcA$v >,Cǰ'%g%JDMqߗx2V8:9b5]ᯓ[KεPضڡJc@1{t\Z:v }Sq^UKh7Bޙ)1 ,y4濔S%F{m,I$W+;M "/ QzOFtR(@3s1g2A #- zWK\u%Rku7VL &^%\fF#e vHcRQ4;$RwI-1L )sւIXEJ&7C7M<+9EB4qM}6.v!>kmgdgJe L|VE/[˦00*}φi^kgƓ־8tp~8ƒO2F'4X(ڠAŊCP߭Pe{QcL:4>@`rLC:"Ԍɗkˑ)PFIE^.u8nQ}M{=k2PFjiW2{2ckYcx|֮(cEpt4'qvTg\T;f $Eo?idRM۞AP6C~&M)Oř'oJ=z,җb(B[ɉYȩD)$ "L-XO0귔Zf8%(ns}}%PR·;F "9Fz6-LMѐKHd oWH͞-xO%мyW꭪x__XxhBٸk!.<8r=p1[kP=x k.m⨽zr-}y "s #?v$:$'/\_3<"XF)#ʈi!mx"cX¯nB.TblAK ]_/`F3צ'&몓k?It}#1دh[ZJ<,R8[e#h4g/#@2 T7ҴH(.f{S(6tb(Q!cŪ jkopWbsT ]}>.Qz}.I$]R躔)#9͢"2B ׂh\X*M*%2ɃWqŷv]ԅ>fg޴ם;X`)cCq'}31C:C]T~`ۑF.\w~QzVnj]ig󮝣S_` ^dkBFDXZd": R]k3~O{O8ZF{޵dyi Պ4^= i߲Ѥ3S;}6zcq~PNFJ&5&ۂ%*{`1>m'BxKx-FaatwU`p3jJD.󬌦 NTΛT. 2a?N[W,߅/~ .Sqc]w1E*0X.BYN%H#I.vvUwdi`eʳn}o~C_8.'m˼h)&~c#+uέQ͗\w[<)M;Kfsĝ-z(UpeZ3h솈ft:o?.ݟK'>W3?φ-·lh `tغɲU p ~T:8& 0J#6ϳt?,X%Lݱp`Q ßXAN&z}c#N-k|b~?D<_q#]UƟI+^ */s@ 0Tk8]l\q+{p%Sܥn=a={Y9M?ji_n剉/)4X(; :Om .[&/ESې-83e&K"of1,NW6m*k 2`Sb=TOrq{"slB?Kc]3ܒ.(26[Ѿk-b%::ՄEl9ϞkSdt#zzde"[$$!wC^4.iCi'g4n9~6?uFn֯%mzfnoi2 e[M+EnjeRD jijoҝg{hfL|nE|9/ hlق+n!A8ݣe>"XMKP]*;^ QL\JNޒZ v @,VR մ7ۣYŋ:ck.\c "0d\+/Ї##p,APqE&0HBTF [fF ߍ#9bѻw/@bW=w;Fon #њz:NtfpV9Ϣr² EG$!;ҝFt ,}?V]G+fڥovhm%V 4ӴD+A`爴ʝ: j2 4Heog (C7W̳uT7x$nROng.{qu8@iT>s\_z!~C{}(z[H'7L$n> JdJ3]!^_9C2h*o2orKhs;w$gkFV9 N^m `p9בQ:+BkH16w-tX:tϾ}96H,{ʖdĩ*٭D89= E;}Ad?`]+z *T!i*[#[<єpp؏ƭ]^-bӝvum&]H 2@b,J W,sG?8}v"Q8+ ޞ^&W Gs*`t(1vϱa?s0qWGWvəPn޳]]rȴ+~vu{͈빹u&XU:b#Po,E ] [ބKΑtv=sctcG}^Km!qfɒLcr{씿Xp d&jG Pi3SA SHvtPK'uf@6#*{se5Vc ,dtni j\n.»y:@#+qFQߎC4X$44 XԏKԟ.uq0"L e~68}Pc1܃F;( Ymkf5,aU-4.L֍|sxG/\y2W7w+ԕv9Tg^5u* ʨQ7u섳F(mtMp)glQirRUuV˼vp^t1hڙhv3BNc4A8wqPl=HY]!Pj90n2{r4GKcשOc[{MՍ5 zsHp]WQC&`z<G0=#D@¬z0UwF9T2z.d ]34K O!#"N7mʅw]~Oq~F)`!i .j6nM>w9@Ɩt`~ pC!Cvi2hN{+6D#?TBh[{Ň,TcĬ`LSĎ@ v~N";hAT"*> R$,,/bF:n[YcPntq<ݤccAQkݣ2v2dŸ:9f]Y\8ʃrR,P^ZEe1WHty3& ALu˻#?,[Kը!ƒ Y:J pL#O]a4$% ϕT i+T]n:7s|Y]7D.:)hg|.quՊI;_|NV[muVLRmAdh_/Źmy̹Px*w k]EeEv&Z&~g_8Mp;A:@`(@UJA/夸@e\E4l(]$e^=+@-oUkeߪc\,y q`ẻъc,# ko4$Y:p)T>#hw4!5 Z-T-2. O᷽miiyO+I%pYYei|wͶķ̔-Hb̴jʀ>Rr|ܱ8q~2.;)8tT}A=A6>3q5N2P@t><m]!@Bcܧ#ZqENMTµ:rR2Q.8 Ll.t\FE2rh>qXlts.eOˌjyrKf؀7Ugu{gLCCX,,#䶹'~=4^_O_c}(AVJXs iЪJl,@[~1ÎkWۂ-3'QFD6aX,˃T`o_Y7_Fws+- Etl\_U @=r@M{Jm]ݻ b-Yhlhz+v.x)zyĆ1׮zN@-Z&ܕwl0`BI2W0́T3~qwFe /=T:?ha;݁Lz.vY_UjMÁf-Y8ؒZ'O':+z놓HPP] "x"iT5LArǠ!y9H7@WdQݤ R%&Ed)#Fr@q v pbF x2i^?/r46 U9E(t a!JhfnuA@HQEhfAB8j\쮖);WFG ΤeeFo}$=Aui43l,hy/t[,[ ,6,4 "F`/<#dhZjG s2QSKClK,gJςvQ ∋'5;PLIXG&:[cm9aOKoŢ>BZ˿ȟ{Tmuה2>0[ypȇiHIClTޫ4.i~ 6daUU f9pk3s%`8R˸{;T:a.S|\fk]bK(e_ Ü4iGDP ¿)`k agZ <ƕqng!DTPw {MnJⲉ5t]\ß^A&CVC]8Nm}c{8殽ĢL8 a{N-3 &=G`mv60Xi;13 c[*麸ѣw꾆DD?v'<`ǽ}^ѷ-Õ[!us#WXZY>K{ؘbYSg*SwioIw̖#TPIwݮ0M lmK}ڰŝZ2ȳQu)\*XՃ1-pS2cSTgqtum#}"xtpمZѱCvh\zcak6ӹ(ӉT5we9k{bX@ihC`/tՉJb9 8'Vhqq7eT7M3mTt~|9exw9pO?6ni0g}|i43\npSfJp 4rhd3:*;'4l7q6.WX߁TX0\4@P/ktȮ$TtCeHIC-[5tH?˚1@FyhHsz SM[cz)x?NYFr>w) rf&b6<_IA, C'IPo8O 7+4.Mwձv}?}-|r0^H1eʖ#Pnei'))wMO$kWٺvWt}sE-rJq#&*YxYbO M+ͫ8$q*ͱ7#o'r2ǝw o{ h-pQm 2ta5 YUCVyX`];74h S R,/ Xnrz dDs5>;)]7?;bXy+{Ku]w7Q9WP~?X*J#Ї87˜6_fB\?}̣_ݡ1 G Zǐ} 5btWYF/˹q*!t68YrQ1K]&VVD&wvxGhD'kkq0u}l;q%]/{EqhOCfFo-Dvy>',򪶪w_S'VYeӓ}ltzYgуcܟi5YQd }-&@V쾳#l]8B\ǵo{G~ foFҰzO>/~f&)\et}6N +PƟ%nF잚xGpCf_HYG36 Z3 ̼Ni'PyYpɸ|Ufr׸}ssY9 :KhFĖSrA0On:u*. { Π:rT"Ew":G;pëQ|ic jřӱ󿎞7uj?gRl >}  [NAv|]Ls +ER8g @.=N~Mw|$G%e7VNNr$gun,4ylCBQH4rR%o1A']e 4re&-Ƃ*8ATui''cvvê0:byE.攸q ~ؕXTLexs|XocYL\ˮgsG߃5tNઞ I<Nt!/e;~o @t}ʖ淅n>OJG94Q6D_<./41 f`b)mEuy~E*e.u}X 6/Ýj}$F \cng `B +Sj~k-|4N^G? pgeḃCb.+pz.P$ckqdx4Yg2>t"ڙeIDATh \r8.xbmGVFm;ğ7+r[3:EodM DZBDOimƉ>Dz⭯yItF)F)*pp}#VY9 Uҥm8\nh4|:SkN?&G>7ZFk` {cVEC{-ƣsY0QU'c[?XwbU/q?3l.m{&[h;a,t%j;rR r8++"p}`)hS1v J蜝g173 M:̧zNw]cA$z դGOQ[+Y+ N4?]!\_Izo2Z6~FL{bPfPt+˂U@fQcr>v7N7\c74W|Ի:h\kVk֚jp@U2WލSLT+>Pi{)µwIgnH%XbNkbWzcjjGQ8 BXظg=_gYR(W;nW)5teܱfn1+)XdC19npf"~:iihp.ׯ Ƽ`b$7BVbD[((~[ ]rlUS@`4*eNI+Nfh 1/{vLe#a%L{ ]tck3:܊>KP.5ȨgPx?gE^#O/ϻLZ]:=KgL&Mp6B|ihp><ǙpUL6vˤ=]!-)'sYIޕ֗$q+]̛o ˠ+XFmէ~{čuhYyU什oVl19UaL [.0+Q׆5JOjVtyO#Ǵ ;t0; hp+hu!1Qt.ozCN}-.;~Ǎ'm4 :;:芺0Y:OdOnFl@3G9K}NzûS. AxmC>f.κ=D.'yDlRҮGTv )ge*z뭘~rQW%ݤc#McF556Ol/2c$u + ^jT+ʴ'`Ƨ8ejK.(/; 'a\yMDtrɏGpȬO2~"*9mu㹈W#5]_؊G.@\7|_<}L'Yv^RL< i+7G/J..'͗ îU,`fkOF4DFÌCvdN `25ITn&p㳗a]nUcV#4oMnI_r[+*Otϳ3G/nfى>. P#xfm]&^4vov?f ˕=fV:']K7A~߽>osC̆ 0͸ٝu` ss*~;߳`aKܨwO.tWS^QWp-s놙MΠRYJ0ʒS &8׿n[~#Ta: 9v9S ^u`ڪr"#T\?|gti8!tWS%I)P9X#Dzƥy57hs ,ރ1_/^.1]5_\9P&ʼng.9w2\G54h)V u!`ɭtN#MpW0e%qjJUzzT*zzjyeJr@6&'xv&ljGŪǸ[ E1xnYsu09e-ר\*rh^m^y9{:eX gIׯ9T͉7no=KMи3޽#^+2Bҽހrmă>_Dѯ5PT}'ٝ:Dwg3:mڳܼ5lg4g6,Tt#4J@JՍ>F&0}&@xdt ׌mvG27$$'`XxV\wwf,D»׽,D$~pRV>|5WyxtǨvY6n/4]i37MT{:tل}Xʁ]г0=QKSbe{ !.6z31}rMNMj1Bbjlkid:7%Zy Ь:AoM^@*]}JHSZ21 @zu ^7*t39y5<[̅kb< *Z_nQ\,E+Yu>r k9[^=ݸe!R.LF1KS-QO uy. vن)+b_!ysL8Z -V%YbID/(϶Z~c!Nʵ t]n_[R\Np-֦!_?,vVjO%/ܧ_}5^NGQ BǴNrĩbWSAyJfd!y@7 =I_?櫔]NG?ʱ7r$5[F3U 29sO[!nm9gĹ ⩛f9 AKrJE<ͻ^QɝY- auAw["$wWML.^dȆP׀{S?w5rIzagr!jsL5`Ѐݠ![Yo p ;?1ȅ,y2|]Rq$ ӔACnFY ²ߔgqbJlVpLf̠Sd-/ZgFT6e\LyID+.+ʴ Y{y:Yя=+VٮU犛=3Kꂮk}OD44< 2+Ж+Jm9t?SA,8+W.o-ͻZM۴͓FwrJcwӧ_et!@kbBݮMwfo􍛌^,@ ]X`n'wۘX)lKe)`y4^5m^p;E 7p3}<~8ϳlf5]mlBd0qt*31oFeX=̀eC4*n5dDž;sٜ'rP&p?AfY-y|EN{Ndܛo}+N?÷zbr9yR~#pʨTn;@=w͎u9ܗfټ<&}񃘆͌8yf n'BPfJ$! A_M{_5J؅ֳ2 `ilmFK`E%3Yf6Ng|u6x?SJ%@eel3/v"{6ˊRcyž{=]9ݯmi{fz6fX]8Ur̻=UF8d~dbubPe]tE˩)[(8O|1Ii8Ьv _8-3TI([ƻ !!Ke|]<'щ8[[]&x_dXt=^ʸ8W6n3Oo(d[ hmOC&H<(ϰ5;O{ҽzֹ$GE}vП{Й[T%;Lvעtd['ZmQчl?XaEk<z $ŐLt2lj_;.iw`9 $Ro& գC9c/fl;1z睱ͮgk1Kq/;/Wy=g[~]PIt %Q$jǻHp (ƻ&~cOҦe[T;݄)8~J"7~2|`-43C b:"{r:/?U>Zk4#a͜ 4E,,IDs8EpT7~$ypMer??٨,oto8]OWtɣ>/ƅ+s,*'"_tKp>i64h3!Srr]4(D^Z)y=?£;*l)Z{Ev=Kڕ+SQ3qpj.9|j~Ѹʽt*^eFJeh V˜F-3fa%:#/єqtd%.5 r9d3gS%Bޱ7ί<&B&Hll{t! ͧv!ʵL{*򝕈_ @)CYM4#h apcwxl@YGZ/o $4I:%}Aj|V;w*#^k>ٳ11kcIr~%4h\YTs)q*xj$n| ˌط,^`4ěwDaڙlq?1pM@.AY07 е&_땇&XCi:j*`,*{~6@4ʇ2=Z4IPAV1N l`~VU]kXzxUdhjW3-$Nj'kOn}{ЮJ,v>{ZMQ_,წ< +V^}11w2B­ZxY βAX sB\`OWG(39 w9ODli,: ;\* tqЋ#j-f;h7\U" Ne6ؤ]g %K0g yL,YҞͺ4[eAuyf~/y-oe/`<mw(aXT#C1NQ 陳1Jc5itS򕅒{4I/i6Wd=Mϻ3L^I?WuPz:mi(2I3,hQ]Z}X^M)-xE $f a=<gx;ߎqYEDf Z@. 79LQgRxL6BN0}&jlFBo6N ]ry#ԻHnJuܹEQF+gٮ_,.rv Fe]ViF@Ln/M4ϻu9IWVUgZ7iIgwGSOsfPU`a-dd$~$K_DpG{Xx{ߍ7~auE7rG|:5%7aJ—,_~O^[,b) kL;Lh4ٵZ"vj@^ТNq?DC@ @ ZD%ȊZK6hfl};$@!<<<$y%y\0o]j,L ?9Oj'Y'ΆűS/IÝIw9hF;+3`R Hr[`M)gT%L-Ӟ `%[c#c[It[@+ 3.VPyJeTp5]z3?rBS鏒a_;-8 K`Ƕ@g!wo(Sn\\uüVݩΞb')|N@~݆ xY>?S~eKf~T<¢fa8_7 BKHy4{w3ڃHt- 0h#ʪ`߅cbf|9fʌ$Gy"p*tGҩt DUẁYn6#Z\ap?Em9[֣k:xNqk>J(NlT}.)9 b/q˪5g 6$ y;'|'(sٛ̋`,x󨬂lT0?v5[T8-G EƆ5^!}T{*Ze Ix_mAd*fR[a&қAM vr 0\E)( eItPP/w[uf~XzZgN%}]ږ:Ob(2-Yv51+R|+xugGQE>r+ew0 Ew@QMq*YwWl$£J, ?) 6rwmʼn=캅A~y?;p̴xX;UVQ0yD M!XK#piUX!,$(Ug*&#Q< _/^YMfKeE!g;}LLFx/[%IVk4J9vcwOZn^_(ޟYյ,ɏyT&BSvMQ+iv{(3ub*(f[>ڕ3~˜o Ң=*aRkL+T63h<GpN:FGuݾd|Cf=8 ߥo>-7o[WEMD;Fu7wޮj!b`ݵjؙM\Sе~HL[WKWzџzy"lwi*w5+7A ”n(ؕn#L~Yyj|W"UVӌl+V 0TfU2Q#iL~⵴|L$uP7b0?a]V~i}FҲ'l,S$5^[Z;ܣm*cnv|G5SE8AFa FWEc֌p|KwK>&_}PG|ぞM0."gz-whWfݲP"ӡ2m%TohD0SZZ2Y)x [",dJ~+$;U0 dmm.|sl?Qr(]48XO%/yll!]E9p),?¢aP[, O祷ߪ7r{wʍ5&B5 }Ia[Beba;O:xV>u9L8R]'k:K/ֿ*{ HLk䧜2VG%. MwN `h `@1[0FFc`Zc 7QM/JFn4>ʂ1mȤR(fVө }>Kwhh1:E8 '8&65 fP6y{Xvf`wI%sisxqƦQٹ]Ŵo̔\ek7s׾[֙nXE93ek:Gnf$vwOCbc |qhf>Vqo>憠&y!PW^)l:r91e^\*Ti_V`[*WBFkSQ4٩{AG&-`ABI I`M}M[ Nls[¢¨h݅1GaTN@z1B6xt#Ll{VRu?g p4թӲ̺ :ި3,lxxe -*o>:Vr\{u|2Փk_/_u92S(o܍vl{Q(y$?p"믟j'ɓ9mVm3_r8/)>+p$jEy~S$tB:zc/A-B;`$:~$ݨQft׌DnRԝ]! D}onkcB=L:ke7i:( o}cj_SESy1,}sR{Ȥ!lqa63b̩`kZ43+hE?~.+rY3;+[Ml̳r~D m@iUVi<S 0(d9q};l!kH/-Ko&_07)<3r7-U>o5 `$BpOn=CTT\{m'B^.ULd;0>T70Neg8O铧 BQd[[QuQ0VIx;]sB 80VMGN(='h7â2GVi>!Nv]n\ES%T̢5qYӲWQ(M|S8AzheE9  h f/i7z;vՀ->O3tG^*|b:b?&tՓn kιW rx:ܒL۵ktPqEt7QNNH{PoTZ ִjy5Z·?[Ͻz EqKΖ;}\.]|r9Ip+Mqt2A_![.Q.VIa2Bmx@*Z v %JHճ:¯\F3%v5'1[e2a21."DØZO\,׹Y83k%ܒ3Y ;O>]IBPyC^+ tD双AuEk ґqʡ5e)a-[s0Қ(NtxQ3h"DhˠVј\4^^RWGWkvy P(`df(H+8ڪs^ !Ku}7o,թUZ;{@ajO+|i0-Gw2߅}~ #H<4,Xa\OstpGL=RajY7BVq^K^pS[- V?;5›- 5V& 1=?~ 21 bfu=s=4fc$pDQILldG8ƭ2Ms|Lq`E[T]V_,Ъ"jWi5U.An.L3ޅMVXcD!sqAտE)8ץQ~ʢ 'O@:>m:zb;+l^"mmDm\|j.>?~&ei4qo (J h>C/O7cGCD2ZP_0v * QkoTE(A`R3%a87~'~7L뇳ߓQH[8M9jy݆O&_u;a;_^쀅 hh!yL?ZmԖJb L Tރ\JCM _ ]AbPHK]i amBF Ǖ8 Py̷vAS[-~,VF~K3ī;:hM LҭoBÇ%l%AU-O(~[\{! o"U8**R.ʥ;~QPqO D1ͷi f„Op܂3GR0$~Ow2%)्BiHBPCƕp52mc-e:fhZ@[0^82(F[DOZ3qi4 k?N$H0==`!b*OAN}-O-OnMc_ͶEJH}G|$4J0rO?!|[{JklCw!nj2?MaEO{sIeEC?-Mpڅ׌H%md-.<4 C3f4-a_.> S2=hڇ\{ ^ ֿV맽%0\F3eSh"{Iw;n@h=K n7iIQ„-t/tBIH[Y'>2;x7зf!įs}AAF8L - ~>m#(S0Sw>u൷ > nǝp- ->Ō0>u}9kﵾZ{oڛT%ҭ[ŋUb*,,TWN֮]={VQXKTZ@ZMU VɱR7}t5{lխ[7V}JIIƍw'PDDhт=J+W۷ӰaÈc5CA hf{lٲeeĸq㨼ܛ> :zt:ݿM{ǚ *[#q? Yz5B8]])ܹsۄ1G xsrrhŊИ14yr_[17\&UC4m4z-cү_ 7h&uYٿU||B];gϞeZE-\ЖÇ6e  ^R6jHWZe F78A?yDWUPcLzЫWOΝUR͖;bc[Shha :5P&۷oF {ՙGmڴSƍMO.\^=ƤgtǤoi> dVsrjڴs&}GH^|)))|YxMJrHaJ5k zקH*)){'IIIr߿n ƍTz(n\~Iƍ /_υ-p9N: +8J\\cޓ,F9TXX,} ҃vi׮}駁k;UhDˏ;#uq+zLHHGVVpom8իˁOM)V.mJP 4:q;Vc@_nE#Yկ_k{i iWJE5k_A5:%joMdarp *z0C !NEH=,(V)a{郮M9׷VSSV?#'-e>zȃ}UW#ZQ=iQ 7:$cL/qy =YMYY9>Gi̥&̎;ĿkҤǾ8SOj"~+qX cv0a>|(g +vbb8kzAMY޻vM %hÆL[^M J֥ #u13g8tPu0|@@rj-C-cΖ-[rO) .G'tul+VP-**&|P-++^cD&['P!Ѐ ¸`=awg5'6݌ӧ<sY֭[sjnD01N;)**Ο?m:A|ZMu޽i]%{jȑB3Ή#ͫVX1C:ucNnz!g n l`{K؋xajx%Q%L84ӼQhe^5-8N6ʕ+w@Y8"t0_J9;bpn8["##uM<{ħM+s4-k#C3`1lSL8, C߄(~Pf8%w%,G%F(;;g_)Hv̇g vdzwwm۶U|(-%|@L[ H67Ζg(֬Yc)2]\qf)pNKKS+Kaрo^xo%K؊s ]\Yuw28zhrmK16Y || :}ҥK8kWi~.883 oX* ?{u-`˫85hLVR&Mn8zrT"F)>>^_`.E8&E8acnǦs1A1h|)`!C8Rcs)|@:.V gT8sJLvڥ8VvS)`LbQzȴfV).&LÆa[̟??U㠴LzADER)::Oо g@GPR1<wIENDB`itksnap-3.4.0/GUI/Qt/Resources/media-playback-pause-4.png000066400000000000000000000014171263013355200230010ustar00rootroot00000000000000PNG  IHDRĴl;bKGDC pHYsHHFk> vpAgXGIDAT81N@Eww\uR/H.B8B) DDp *:Nm%oV5-ߊj=w`( |]`x\!7׷8Brfx5PEU<'F9i @Q<=?yU/{yQR,J\Eպ)ƘVp-qe25{Ĝe=k,XkE `q&HL$U$ ֹyr*:y;U~hVԧ@p88MGǃFϬj4\L #%#zXcM-uc%tEXtcreate-date2009-11-15T23:04:11-07:00<%tEXtdate:create2010-02-20T23:26:17-07:00M%tEXtdate:modify2010-01-11T09:09:33-07:00za4tEXtLicensehttp://creativecommons.org/licenses/GPL/2.0/lj%tEXtmodify-date2009-11-15T23:04:11-07:00HtEXtSourceGNOME Icon Theme&i tEXtSource_URLhttp://art.gnome.org/2yIENDB`itksnap-3.4.0/GUI/Qt/Resources/media-playback-singlestep.png000066400000000000000000000037311263013355200237010ustar00rootroot00000000000000PNG  IHDRĴl;iCCPICC Profile(UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- IcD+wHr s&+`ыP+Beϲ[a4Fcd͈ j^{?\K\jyO/U\Ap8|w4P7,4Ms`e(&1>++vjqq6MfoIVJyꚄJFTrIꚄRصS.}jv_,kt  i `'EwxһnD"5Mr h@uE3wy;}e][=HGzUTTܛH$"9%%%a\A[+Md||/|ICCyX[yi=yy۵+hjj TlȂ`JA۶m3`21]my;pVSbX ]4(**.׆1pH?u2ꇇ.M>~=r4M\paaa> l)%ҖH D4WBUns闟_:w~`3`qVdzRH)RbiH&ǟxGjI3 IeX,f-ò1-an-G#'>nNC`nn `qikא۶K C ! >_{eee+dey+l&踀N{=^mmE`La8e8H8J37>8gݣz>Ҭ  4}ߟH;wa|v;q%u]_hlocYWB˪ˑK.qV=6kN!pIENDB`itksnap-3.4.0/GUI/Qt/Resources/media-playback-start-10x-4.png000066400000000000000000000037721263013355200234350ustar00rootroot00000000000000PNG  IHDRĴl;$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  |IDAT8U[HQwg;F&)*!bkKy) _"KM%54D-/ay,t/ i*YFwqD#|s3gDVp1UH$rƾ G[, ƅ\pHjD>,R sU~%KRO,؞KRwoT؀YިViКy%4,tLW>nnn*-oqG!TV=#1sZ_RQݥnJ%e[ J`hh>x$P-ɤ GEXRRT;6J,@P8H`ddΈ2gы/8g."777. h443H`dA&&>̓\h/1v/Q[ 7H27? FJ O BC}#h4.~Pt1)Pu"h4vŸ뛊iPJVhjlE9ӳw~911M3lv+bA;XZZ@h~]vNuLʌq7'OHlSLQ T̲ E+\CvN6ZB]`(YXXXC,~@ Xd {xxح 6p@^Ey%[`h!|l6K0X,&Q455`zluvVCsKJ9UwdQSS+Q_mFK[[O //O^qoozZZJ``@e]m}+'kmm l ɦ\'+s)ʁ [ ָab7bVyAAAzqXܖ$ DUg&@|F{dnr n=czIENDB`itksnap-3.4.0/GUI/Qt/Resources/media-playback-start-1x-4.png000066400000000000000000000036321263013355200233500ustar00rootroot00000000000000PNG  IHDRĴl;$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDAT8KSasvζݔ)ʒX3N 5 2*T[mB"*Ǚ.bE(qAlEB.v/gg|B!P7 1Ǔý-CLàLy7F {7G_^v.[F=TEQT:l܋[c/21܆B+y~@u0s?]jmL0cb%-D"݋>{xVBKZ;sRw `$55FB1 r8^QcSS0E֟x h0'cQcx| *e l5L6>{8Ec@imt{z,{832`{OmmJnnNG'Aq:xh'&[b:,]p~C32\񣎂o=Mn39mzwU /i9ĴIENDB`itksnap-3.4.0/GUI/Qt/Resources/media-playback-start-4.png000066400000000000000000000022321263013355200230150ustar00rootroot00000000000000PNG  IHDRĴl;bKGDC pHYsHHFk> vpAgXIDAT8MHqo?6pn RѐuAE:[!9 u^n%rI 2 !f빜C Fy颚/Jvh4=8 e0 xj TTOef EU*Rp<!":0B# rE[eUsi !V*+f̲(vy?K⧎`  hlj5̎?GW q<$)_Ͷ5wJYs qo5XVv˟J2EEE\nC\B:ZtB7  &%tEXtcreate-date2009-11-15T23:04:11-07:00<%tEXtdate:create2010-02-20T23:26:17-07:00M%tEXtdate:modify2010-01-11T09:09:35-07:00;4tEXtLicensehttp://creativecommons.org/licenses/GPL/2.0/lj%tEXtmodify-date2009-11-15T23:04:11-07:00HtEXtSourceGNOME Icon Theme&i tEXtSource_URLhttp://art.gnome.org/2yIENDB`itksnap-3.4.0/GUI/Qt/Resources/media-playback-stop-4.png000066400000000000000000000013751263013355200226540ustar00rootroot00000000000000PNG  IHDRĴl;bKGDC pHYsHHFk> vpAgX5IDAT8=JAj7i իx\ MD<02t`zfvqVǛ?K6|Mkww 15뫛ؿIU`0`WP5UVU9 j3p2XB3\e Ap֡CՔeY.+pS:6Yì( ܽzsgf,!Pns*@T3C=S)eծiRK͌V9dH3``pzvB '?..Ô3""]w}p=\<%tEXtcreate-date2009-11-15T23:04:10-07:00Kv%tEXtdate:create2010-02-20T23:26:17-07:00M%tEXtdate:modify2010-01-11T09:09:29-07:004tEXtLicensehttp://creativecommons.org/licenses/GPL/2.0/lj%tEXtmodify-date2009-11-15T23:04:10-07:00BtEXtSourceGNOME Icon Theme&i tEXtSource_URLhttp://art.gnome.org/2yIENDB`itksnap-3.4.0/GUI/Qt/Resources/media-seek-backward-4.png000066400000000000000000000020041263013355200225740ustar00rootroot00000000000000PNG  IHDRĴl;bKGDC pHYsHHFk> vpAgXhj'[8F4= x$iip0:033#ůT,8k;,kqD"!qum-MӮ/D`؄KtE۹v˲"NX5vsq(.fg튢p;; $z;u]yBJst][{x<r iF(X[K|JMW~}}e^,ˢP(@ SL&'0 tuww3Jvq.x44,N0nٜ' 웦*D9"^yprjr(ܡiGc"b\yuF'~P P|djrP+?ο T%tEXtcreate-date2009-11-15T23:04:10-07:00Kv%tEXtdate:create2010-02-20T23:26:17-07:00M%tEXtdate:modify2010-01-11T09:09:31-07:00(4tEXtLicensehttp://creativecommons.org/licenses/GPL/2.0/lj%tEXtmodify-date2009-11-15T23:04:10-07:00BtEXtSourceGNOME Icon Theme&i tEXtSource_URLhttp://art.gnome.org/2yIENDB`itksnap-3.4.0/GUI/Qt/Resources/media-skip-forward-4.png000066400000000000000000000021631263013355200225070ustar00rootroot00000000000000PNG  IHDRĴl;bKGDC pHYsHHFk> vpAgXIDAT8MHa̺;8;3.l%1+"}!cE(KbbEYʂ,`VNgC=yyY B'` Kp2kh:;w3Scg]  ۹{ǫꚕkVzldF -mm__DzP(_qsk9Jjڵf>l9 OR<5M 3ڶbEC\Q0xv :ufuͪU1{ى ok~qɶOk:Li2dIFt<~<ѕ,O.%^m)H|fiilʪ*9s4MZlUr&sMՂa|p"Pd2]D^~immNNNz0LBzhjH&d۶RCuQBu=W{iw@*8T%~ #ǚgjk^!4U |>?6-qx<ݼ1wzov[Cc{#`8x!(, ㊯xOoR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  IDAT=PN@;JZZ*+衤&+ho5F D?0; !ZBaΜ9ZmUEQ 7h-lSJ!~ PAp:3Cb0 {rߺq߶mSfp} Q-}x<cpZ\4Mk \UU"Q ڶ}B*vÄGp%Ibi-M $!V@.sC;&c>w˲w.ĚF\ 82COs)f=Y0t]$R)IENDB`itksnap-3.4.0/GUI/Qt/Resources/menu-arrow_8.png000066400000000000000000000022051263013355200212010ustar00rootroot00000000000000PNG  IHDR~iCCPICC Profile8UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic vpAgXIDAT8ˍ_lSUǿ{ۮtܮtk D!7 8 }_|]b.1 2 mu=ח Q|/' xYp@pG1R9knXh݇뵙dbՁf {|04=|"$j$595H}6>lh켔Uch 2ţS uU`n3d%IZ롏?$PebZ@8{ÊQ1wE㑭D̘ D( ._}!^x2,[ZM/aK,ŃPNM22tS\~e׮ke-w8Uւe4Kee^d w񬳦jϿi:~:>vĉsuKjݾl6o -a:yV!x0&'#)F%031%tEXtdate:create2010-01-11T09:12:56-07:00`%tEXtdate:modify2010-01-11T09:12:56-07:00X4tEXtLicensehttp://creativecommons.org/licenses/GPL/2.0/ljtEXtSourceGNOME-ColorsD1tEXtSource_URLhttp://code.google.com/p/gnome-colors/PIENDB`itksnap-3.4.0/GUI/Qt/Resources/open_22.png000066400000000000000000000066501263013355200201320ustar00rootroot00000000000000PNG  IHDRĴl; AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs   IDAT8OQg ZpU6i19_4TD ,DP1%FN `BqoV/ 1ͼ̼yoИ{ʅXnc`I*,= C91tvR8rQsp^7ؔYl*))K4=:lTicė˳NRĜX2c^HQRZm=Na l _gffڱR{SR$XAY)T6M]S.i qZ13HZP 7bplr& _V~%郏(^W|N}Hy!> A ۣ'/Ȳ##5 gt:P/}@`ka/ɒiY8uEll6M8q7 0F bY#q]A6[|J{lP9s\q%@d-ڂ.bѺo+(9DG|yqVPN.[ !F7->?75WTΒi!+*ny$%\%j&U嵵}]劝5]C/T 1KD,*8X5L=CfLfZXt J:6s@bǀLoR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  FIDAT8c?% , hP# @WPIF0PتUc`ar vhh(3L5E`TR2SW2)sv۬,Oxpߡ?nmlld:p5ډMcaedzo_YZ 1\t]uv۷WSVVUW?tFFF~.LI PVĈyּ_pp2CP-ǃ{>}$޽@^_+7Ƭ꺴IiGc|;(4峗k;/aa 4@_ԧ0|GW/^8y|s&P 3g~ $5__ݯp5 Rئ&# (`Al4㚁;25GbJ?I4 j@3`@CqcZD.%1aK<pL*T?ȯ AŞk၈b lqvNIENDB`itksnap-3.4.0/GUI/Qt/Resources/outputintensity.gif000066400000000000000000000017621263013355200221550ustar00rootroot00000000000000GIF89a s!, sH*\ȰC08"D1f ǏCZPȑ1r2J0cl 1cG"k*|Y'Μ7]<8T&˘/zϓ:w2=sOPkʢ=֤9EZO&5h[+^EխQwa9WjȨt5[`Bz]\$Vv\qOiR}RYa̔-~!Λ׎~8h@;itksnap-3.4.0/GUI/Qt/Resources/paintbrush.gif000066400000000000000000000033461263013355200210250ustar00rootroot00000000000000GIF89a $$$+++666<<esES&fiFI(fplPL,ruRU"arEG&enUS&daDK.(Perfil RGB GenricoProfil gnrique RVBu( RGB r_icϏProfilo RGB genericoGenerisk RGB-profil| RGB \ |Allgemeines RGB-Profilfn RGB cϏeNN, RGB 000000Perfil RGB genricoAlgemeen RGB-profielYleinen RGB-profiiliUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfileGenerel RGB-beskrivelsetextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l,'dihl뾪fe&5eXu bP~@Q@&Gh6dDlI2I`dHY H/  c@  8 48  8 0  0  ƿ/ /  8R!;itksnap-3.4.0/GUI/Qt/Resources/poly.gif000066400000000000000000000036351263013355200176320ustar00rootroot00000000000000GIF89a  %%++%++%33+::3::3AA9@H9HHAQPAXXHXXH``P`hPhhXhhXqq`qq`qz`zyhqqyyyÝ͝Ýͦ! -! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxVcprt8wtptrXYZgXYZ0bXYZDrTRCXchadh,bTRCXgTRCXdescGeneric RGB ProfileGeneric RGB Profilemluc ptBR&frFU(zhTWBitIT(XnbNO&koKRdeDE,svSE&zhCNjaJPptPO&nlNL(>esES&fiFI(fplPL,ruRU"arEG&enUS&daDK.(Perfil RGB GenricoProfil gnrique RVBu( RGB r_icϏProfilo RGB genericoGenerisk RGB-profil| RGB \ |Allgemeines RGB-Profilfn RGB cϏeNN, RGB 000000Perfil RGB genricoAlgemeen RGB-profielYleinen RGB-profiiliUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfileGenerel RGB-beskrivelsetextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l,pH,H" lKF`{DАP%KɥL-S'JP dE&tNF$\(H! )I `$HNXFxND"#N$H$"F*L% 'eD+ iHC\C"cU+ }%!K%"Hp{FV` D8"$,8P(\N Yf9SG]q?+:u @B205ۯaQWkJ2#Xՙ{&TP`b%_>p&L:Y AjO܃{K֐W`ycKHģHD0;fہOnŔ=/]DždT-"#@eL(rr>vˢ37Euŕ7!qAAhF&Hs6/0[@? SU>`Kc0yt~o-As]0m6{}<"Q:➚k#iR".G3$Iq"j VVw~u*5M'6MĽ%tEXtcreate-date2009-12-08T13:16:04-07:00n%tEXtdate:create2010-02-20T23:26:15-07:00;\%tEXtdate:modify2010-01-26T17:54:16-07:00'_2tEXtLicensehttp://en.wikipedia.org/wiki/Public_domain?%tEXtmodify-date2009-12-08T13:16:04-07:00BZtEXtSoftwarewww.inkscape.org<tEXtSourceTango Icon LibraryT:tEXtSource_URLhttp://tango.freedesktop.org/Tango_Icon_LibraryȭIENDB`itksnap-3.4.0/GUI/Qt/Resources/popup_delete_16.png000066400000000000000000000020731263013355200216540ustar00rootroot00000000000000PNG  IHDRabKGDC pHYs: vpAg\ƭIDAT8˥?hq7?$ EQ$vN@!݋Peqv(m5IkO1%.\L{ޏǏc8l'JIonq٣W9. L$'^ F Hxٰ<ă+ӵe Ѐy~c=TDל/Tő {{lΩKfnj/sv0}bK5 Mdlj^5hȺS"yx$ gh 6̀c`nƞ" +`;p@g|m~|=TuC9<3 D?j m`Pm~7<|Ң2.2I` F~^H_vEi0(,%umLӅy9=/ (w\y{cn".QJ5 }+AR.RJ!B!s_x-1-ݣddN[UC'%k9:.Nty.& O)B=XǹUҚFX4p[ÏPgPy1tR).D819q宇)B7ZR1^,  S7d|SW[{GzsR^Uv @`NB!T[[rݪ|'`r{R#A9-Qg 6&hi@{# /}%tEXtcreate-date2009-11-15T17:02:34-07:00%tEXtdate:create2010-01-11T09:24:30-07:00FJQ%tEXtdate:modify2010-01-11T09:24:30-07:00gtEXtLicensehttp://creativecommons.org/licenses/by-sa/3.0/ or http://creativecommons.org/licenses/LGPL/2.1/[ vpAg\ƭpIDAT8mOHa? Pl`#pY1a[C6T.ۡ.PmeL_P!5.tlkLn;d/Y>}OWV8$P9` XLJuV~a宩FM*%I0|rFTU*%SbagQ΂iyy`$ t'%_b`a /^%,uXAXa]<0P. Zz'oa6a9DX11'*`AU@fm BĠ 0G¦ٜ ,YTpb&{Ƭ0YgQ&o"B{VkcV7^15k[ȗs=KFN ΂W (A*ir'UZt}Y'R?BƆ&ȗ뻖T:WHAĦ˞*ئ4,K+7ܭ;mŶ7|AdP vpAg\ƭIDAT8ˍ?hSQ//&%h?IDH ., .\B7ApN.:H:b'KEjim޻|ɋi{n$ s私O FT*V:z;z-/A@Xz晱R$a]$1sq HXidgAĖP8! \n#<𴃧 '">`bPw |?2MZrw 3R$$ 8m6K}!E(8nt:HY_ !ׁ9[PV^ Pp xR1.; ,;ہ!(>$D;x&)BSDVgwxvu \yv*)DQ4q6;;33;0Ezdk7%tEXtcreate-date2009-11-16T22:18:17-07:00[%tEXtdate:create2010-01-11T06:52:36-07:00Ϛ%tEXtdate:modify2010-01-11T06:52:36-07:00w&btEXtLicensehttp://creativecommons.org/licenses/by/3.0/ or http://creativecommons.org/licenses/by/2.5/+?;?q1h2Ǭ~Zh45&ed©CM&X>pޓ %yw|c؀^/terFXǧ+D}7w _"%tEXtcreate-date2009-11-15T17:02:34-07:00%tEXtdate:create2010-02-20T23:26:17-07:00M%tEXtdate:modify2010-01-11T09:24:34-07:00TgtEXtLicensehttp://creativecommons.org/licenses/by-sa/3.0/ or http://creativecommons.org/licenses/LGPL/2.1/[ ' gjƙ{6"Ψ-Dj3&R#EfUs!L؀mдc}#"@OvθZTuǧFY 1gaظc;w0JtBMo+-0n{ gDiB<2Dwl~$ղ y,fyŗ\N*&89i}1||/7 4 Jìi9 P,n0}7ӠAfpCA2xY]1ynѲ\3@Z8c$_MNq.9۴(>Q_eqS"l,h4Ppi$4SZTr3Ýo~NYt%F *ERk/ ܏xl\`r`xTI*!§k&,TiM$Ċ挡OwK7fDGIC\Of䂽yiDx~eY:ܼ6-띒8SO;%\ (3JDѕ%tEXtdate:create2010-02-20T23:24:39-07:00K%tEXtdate:modify2010-01-11T09:12:40-07:00x"4tEXtLicensehttp://creativecommons.org/licenses/GPL/2.0/ljtEXtSourceGNOME-ColorsD1tEXtSource_URLhttp://code.google.com/p/gnome-colors/PIENDB`itksnap-3.4.0/GUI/Qt/Resources/ra.gif000066400000000000000000000016171263013355200172470ustar00rootroot00000000000000GIF89a!!)!)!!1!)1)BJ9BR9BRBJcBRcJRkJZkRc{ZcZkcsk{ss{ޥƌƔΔޥޥ!,l=H*@0 (T9@Af@A# @pp@hn1 vPi CGIjʵk@;itksnap-3.4.0/GUI/Qt/Resources/rb.gif000066400000000000000000000016331263013355200172460ustar00rootroot00000000000000GIF89a!!)!)!!1!1B19J1BJ9BR9BRBJcBRcJRkJc{ZcZkcsk{ss{ޥƌƔΔޥޥ!,x=H*@0B (8(BA .PHx' &!Fvp=L@QT O1@ٳ;itksnap-3.4.0/GUI/Qt/Resources/rc.gif000066400000000000000000000016021263013355200172430ustar00rootroot00000000000000GIF89a!!)!)!!1!)1)1B1BJ9BRBJZBJcBRcJRkJZkRc{ZcZsk{ss{ޥƔΔޥޥ!,_9H*@0p‚ (P9Bp#$Ȃ2 )@ 9+1 iKn ;itksnap-3.4.0/GUI/Qt/Resources/redo_22.png000066400000000000000000000040531263013355200201150ustar00rootroot00000000000000PNG  IHDRĴl;$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs oyIDAT8]h\EMv7ٖim$ЦE " VDHE|AJ"*(5 j1M6vo;wf/놦\fs9sC8 fDTl/67H2ukj( >$㍵}Ù1vGp `ft)(a#ϾR6y4''Uø )>8@R5!}OtNEjY,96P1=BCa ^C9p'X浕/KDDe #jS n*Kc~Gy\g? <Ƙ` =Fo_gۥmTcxKHiQ(6{7.uB[sL#=ⵅ7ܳ+ŵfpRgjљH,Յ _]|7n#Ս h`8 [0.ص˒7Eei:ҩly{ B U,Tj2i}g?=Z-Rԩ4/wZat3A'w|w]-aIENDB`itksnap-3.4.0/GUI/Qt/Resources/revertaxis_16.png000066400000000000000000000026151263013355200213650ustar00rootroot00000000000000PNG  IHDRabKGDC pHYsIIE vpAg\ƭIDAT8M[heݙkvl[cIj&%ګX  JSKH7_P|P[T6B$ !11$Kvw}>J{:!Otغ9K7[(gR [פֿw;@1Ƙ i䇏cԥc^M \Jb粫]ͥ~!*l?sڑZܖMuR\HHRw733AೳShJdi: !л L5@g)UaZi TD\=2rVr듩+3̴ t>cC]J TRTjaM'TP8V3"N&V+:;n!g>7eͣ՛~*"Wk6~a_rĿNZo]I.S&bP=0E&T:%3׹  /B7.l8;l/~?t\l34섪\pDv` gNU@H(^O iF77=oՃ&0Ͱmhf-N^Z0`@¤& cS)V *o@{S+B7%tEXtcreate-date2009-12-08T13:16:04-07:00n%tEXtdate:create2010-02-20T23:26:18-07:00g=A%tEXtdate:modify2010-01-26T17:54:16-07:00'_2tEXtLicensehttp://en.wikipedia.org/wiki/Public_domain?%tEXtmodify-date2009-12-08T13:16:04-07:00BZtEXtSoftwarewww.inkscape.org<tEXtSourceTango Icon LibraryT:tEXtSource_URLhttp://tango.freedesktop.org/Tango_Icon_LibraryȭIENDB`itksnap-3.4.0/GUI/Qt/Resources/rotate3d.gif000066400000000000000000000046701263013355200203740ustar00rootroot00000000000000GIF89a %%++%++%33+33,:::@I3AA:HHAPPBXXI``Q`iYhhYqq`qzazyirrz{Ζßßͨ! '! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxlcprt8wtptrXYZ0gXYZDbXYZXrTRClchad|,bTRClgTRCldescGeneric RGB ProfileGeneric RGB Profilemluc skSK(xhrHR(caES$ptBR&ukUA*frFU(Vaeobecn RGB profilGeneri ki RGB profilPerfil RGB genricPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBu( RGB r_icϏProfilo RGB genericoGenerisk RGB-profil| RGB \ |Obecn RGB profil RGB Allgemeines RGB-Profilltalnos RGB profilfn RGB cϏeNN, RGB 000000Profil RGB generic  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfileGenerel RGB-beskrivelsetextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l,pH$Ȥt\:86+`, Ej@@( '4<'-kC` !vwk ~"oS   [B!   %BQ'e tFjĻȻ JEGe#S"!C N '"N$  - !Uo ÆO " a`@@ p!C t$e>WV ökV)`\0@)K;itksnap-3.4.0/GUI/Qt/Resources/rotate3dTiny.gif000066400000000000000000000017301263013355200212320ustar00rootroot00000000000000GIF89a!!!!!!!)!)))11)991991BB9JJBJJBRRJRRJZZRccZkkZssk{{ks{{ƥƥέέֵ֭!, 8LHD,d(pCAa  rH F 蘐 H @C*]4@HO@Ɂa OEL B0>#p 8(!Hp@a @<@! xHI%(S4X;itksnap-3.4.0/GUI/Qt/Resources/save_22.png000066400000000000000000000035331263013355200201240ustar00rootroot00000000000000PNG  IHDRĴl;$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYsvv}ՂIDAT8OAgxDxViH,PLh"jDz[ IDj D{ c? <ѐ`w{3ޙ(Xsw6rcd=i)|&}j$s^V_zrN۷f}S.]&gNXG/%(xߑͯ% ǘQ̴$p`:)k`!&I$A;` AGPD޼FvjTjţPl&5ҏqcj*33jP2+9yaF*ޕ(AW;r&CV89Pف 1e<<V+FЬEY՜ǫZX6 l -z:G1nb+N +V"<.,,HٌzeqqQlk/< 8 vvvd2NFO%r)Ieu|Ê5Z(9Ha eҀs?$v[a !xhhHZUc|"K Vu5 '!J\JTݜ˂v[$\S gKkoמLOO3` I¦5T U>Z X:0  >n$zeIENDB`itksnap-3.4.0/GUI/Qt/Resources/scalpel.gif000066400000000000000000000046271263013355200202740ustar00rootroot00000000000000GIF89a  $,%%%+++2:%33,::3::3AA:HHAPPAPYIXXI``QhhYhhYqqazyiirzrΟßͨ! (! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxlcprt8wtptrXYZ0gXYZDbXYZXrTRClchad|,bTRClgTRCldescGeneric RGB ProfileGeneric RGB Profilemluc skSK(xhrHR(caES$ptBR&ukUA*frFU(Vaeobecn RGB profilGeneri ki RGB profilPerfil RGB genricPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBu( RGB r_icϏProfilo RGB genericoGenerisk RGB-profil| RGB \ |Obecn RGB profil RGB Allgemeines RGB-Profilltalnos RGB profilfn RGB cϏeNN, RGB 000000Profil RGB generic  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfileGenerel RGB-beskrivelsetextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l,@pH,Ȥrl:QSi(Hc"HO2TMvH JS| XYO% !YU  S^  \^#  $N#]R`#RljBk(& L Я!S O #"  ( "@k%".lW& x#BJ4Q ,Vaeobecn RGB profilGeneri ki RGB profilPerfil RGB genricPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBu( RGB r_icϏProfilo RGB genericoGenerisk RGB-profil| RGB \ |Obecn RGB profil RGB Allgemeines RGB-Profilltalnos RGB profilfn RGB cϏeNN, RGB 000000Profil RGB generic  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfileGenerel RGB-beskrivelsetextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l,pH,d+LPs:l)*:Vf\K8DaҳG7[-(\-',/,hC0\13/h")/3T*3, '  Mt $  [I)%3& !F+uw'F+L B! !3)'F/L&3FqB $Mi PdAviV<%!5i)fɓZFP@ȏ$@3H S~//`9)3 H F@ 8x&-$0p`N~`آ!B @/ZxpԈ "C `73 :P3i@\-^xu;itksnap-3.4.0/GUI/Qt/Resources/snap_splash_172.png000066400000000000000000000574061263013355200215770ustar00rootroot00000000000000PNG  IHDR> AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs  @IDATxeUy^򶫪Qy H`F 1q0;~0]a @HȴZiLͬ>J5-ݨyq\8qDJ~(´fr9<Vz=嗨bv}~^T]J0F{ajZʳFʳe3|NX4Bhl/O# TVV|}&ҘY'ky3e>W&ylDR³\[[[*嘟 [X|3Noooޞhug:7߅FΤ krAfD": JqP g  ui5?'e+)T(e]Sof\^{dz-RsP }<+S)ӕwқ2E|023_XZwT/GυsFéVWWS;6VeśZCye/Oc-j[Sq֗1H'l˙<0a൰r"3I׹ed,\ 0P]_By(R`Z^Y*_!l|ZQgfʕ(-J2 "Ă ?љA'xV[=w.E+^jd_ Y2OKeBn1M*@Jw2& _d ǔrN´|-'*kM}qc~.&?|հ6Z_e 4[>5k,cW>ē}Ml|s*64fzsC)Lg{۔z|*Crdb(i-FGsgIV&<)?G=Z8jԩuJOǁOE@|{Xc>kkS,0˼s)[xayGzLJ⺰ZB1ti{c\,NL!Z뚬 !m4_g'cov)d' o|c va9Le"w'6N.Fo-Ht׼*=CX7!bp0ZwpZ6ک@*AYCO+㿏݆b97 x,濋JLA![т@QV;1Oɡbc_UO$(cGD,/pf]GQ -u8=:J}uCzy2)?xwQs?яƕ(a?vr@ nR\Ṯ@]4 ?>"= -h!M/uaB^U,7-,,$5_TQ0F}t„.2*YC8061Bie4'#ZXWYedY%O2lbғz W(eBuRq{x I+4e$~~#pI^ŦuS:3,$z$[ Bbcf?g*N@CLGwnQ]JpHX`+3FWIp)lg7k_k?sg@z;/!YA5ڲXR_>x:Q<+Qq?e,|~bؙ˟tK8[[Hmɑ2G}Lg<缬[m-$s -u.?N(`YGh Pyms+D7~3N-Ct-un<CX>3Բ$A-Ϝy~k WCX {&S_rSU>-ͤ u Pho\Z6ҪTvis ѵ##f^{fp_~*_+jJ4R7 휶7HmV'߉oEY=xq S,$pzQ6=, ?Mp:QtV˜v5ٶ|+u2fB{ @M5c&m@ UZ ?5̈́W*-C|8tr&_.6mU(72OR*谑I,Xzg4|9 'CMΔhn}i9V}(F6$\[hrܦ מ$.鳾%Ӌ}+O듺vųѶ8 bw1 tڣƝ, &0We5n+׮9:u:X|c8};#U{ ph9c=1atozᱜ KȵZ6H Q>w(p2Sn0epVX+XXa{ 2ַʕVV%B-M#I&qa~*cn;vvw| N:q?J'b!h4j * $ٲ<'9f^cci,0ځg hܒ0oڠLey2I}dgv\QFXps./P37hNqXݏ=1|K +q|/㖶Pqt}1ñ؇Vܑ(ퟔ~#eHg۴hs:##S Yd BZ?u}n#z"3R 62&qF_CUVCeFz)ҡJ)L6QenLPVⷥ,>@6 f+,o'II$bc3~.:=Z iްEey c1 ϔqZQɽLv1Ymed{h;r] Ų3p`5ow0n~K W\>V̚Nŕ\R')"GVjwgmTK3qȃ<k5>u&YI]~m 䵔;UY 9s+8+ȏ=6ZIv+<vّnHXgSrAFY"BWVhS!P7OGr%$ZyO%+yX4&ap#B2Q_rbVҳbdȸbcțr&hh=c>A mE3Li2l^F-B{FWd U',n3ѵz+etޱ{,:7V1ੴ38npy׎ڥw{.^">*өZsN9&=L~jQ8& x,SyLt196'=_S^ #7IAK\°s۝X=v"zqvuңxnӳߠю>|'@#G.9s뭱/b.^zG"CSq]WԱȡ+0ԅ=KuRܼ$NlInCcll~!b/rrօ)lwrol ϳZ.+rV \~V`7^rylPeZ,둕E8>~C.ڿ<1K4]yNe03CPc{L~M֗*[0thG#u Q,K7dž|8vpi3HSyV.cKX)b#_~0*c([9쎾^ve? erZ}}(yYKN/6mԘNM^Je71f_{'qxӝbTJP#>{6gZA<^>T\&7g'%ɼ-yQo8; ƐH;[X6:Znaxv6k\&uD4R6bꋊ[ڄeÓ,Bّf+,O8+%?4X/./YyR^C#̏|xǔ;ynv?†eMGcvcnc6_|}3S80ˀp}iۢJmثk!֢wŅWFm`WOǑm.6v6'2][%RqKai(,m\~{srCʠ{(YUx / /|Ű0M´) ldBX:-'K۰yU7몝)˴ޭ0 Yȃ=2?UIgPM9Oع1`nG=TZqo@(Gi3י,ϟ>Li}<)l;Ī3Dw ?Dƚ:$1L2dkC ?qck|+:b;z&FQhVy%bh/8τ^ ,K#OZ5#7=\vpqIg # ǺyV {X67h#C,sq/[.[Ǘi"l1m'Qⶾ0-OF+ 1W-ݲ* mƗoYFڔhvHxVM0ӍQft.I9)&kp0R>7ɚ}3ll>$׷h{(ē)"2>ggwzbNp\0䥟zeѲ0J4jp 7C0 5:=8oGW~ҍ7fGr+'3hȍ.|nfR0P|rH1вײh16z6 ;+gz,$|Ju-y&-ty26E4['</[d9yQ.) >Sk,k/e ;M^,̳tp Dkle7Ên?\ V=1Vo<4 k֯9F?qsSěxv\_O k#_;},BU[c'? e\BR[$iKAFJ;YX6SdZK_Mo}t=_| W>y>ob t"gW07BgXo#\8/r#̔)~|Bi=*6r9 cHyOKRύmqPH¶ɳ0%> \O8岩P\A⺻e|w[X~뛄o[O>WY`e\ѷߙ L-lƫxEn$qTe ^ٖ-kN.>PCM{ R,t;/p`Y ?~ˍ1vh=~{Goa=1k:X3o8w\=ƌ _XnkF,6ko-:X3$a6-rP0pm(RqԪ+h}1"/gl:[Q% ,[p$"ڦޓɄǑǣځXk CitZõߤp@2m1Wr1\*@I,$HmJ4R>o3C]qvFݨmkޘZ[|ס oHE G2XӍ iVrFFm .^>+IhCrw)pҡy&/~[AvR$ Ry\$p9t6U8qjx9Atm)rTIC 66ʱWWxq/ֶF?w&O= ;'̦F+:W76⑏n*1x緰6 7ow"/c`B9<,&_)*H؍5VsbU0&jklVo(Kq4%4SKz3B0XFOąJOCx=}4Y(}0ܟ\krzpB) Zf&V}1SEgb~ :W]cez5.ʑ ~jz8^2":5n-;l6Jq~"Z;8RZ ?Ëj9,F$4$L/{ ]W%ZgSLhatbqXSAG{i6b_b ϡl}l)/k4֠kuQOۍqK-?=|D3rmsqyѹxS_5ʳO0=q-{gěgޮ0F*+i5q>ŦybdeBʛ {b ?l`7;xJ>L-h%A^GYYŹƾ&.ej-<C  "/練 O}ޛS-}JXF,CY+]GZ%:8r3C1\=}*??;kcBd?/^v\5K.WPfXCWndEoaW'^:)˶Yk#qXar8FW\]PNRj5s-7}{f;+L`zX]=|r'fJj?u?ֶ=ꓼQ[Т,ß8IYw`ߊWi xF`g5[f2뎯`mVG;1Jj1{3imm ֢Fk,V-hO!2VsnD3H-}TW{}3Ԏע?t8k[dLkjij(B+KqgX9[ECM4cHߒb>ACuwדuݮu(ڃ_,qJ)c0%lcĥ-vҟ>F"::w˾3iKHcb/QrL?=oA6'bhh/F{%shJq1+"o/nEvH72"O̵hC̊(("^sYn Cw~f+SΖjt"q~XLMPv-dXtMlT??n F\),QSmv[e/&vSl.A(;n@oMw%eH'8$맕3L|$ :,ɗT!< dF잿?Z/}*s ~]7?J2!pL`.͌Wb/w  nyԦ"W/ـ^.(b5)[Wh,Ithva8KU&4':tJC/7ѹLV(Si*%NuvЈ2ťW| )|"F;ʶw*%F'x]7dAy- Y#jv)6[7bvR?/ZFvXebiJ ١CNHAXf$?X= Sye$srFo ?- BtKZ"*QHvU5 X DIk '|.i[me'}_7mуR.ւ{"ZX&eg Jq(Ⱦ}|-IJ3|>˲[}H t;Nlh ~ ,װ l`tߎw3r Y:QsD*"'Ĺ"UJITl]h4['R꜍C-3 ,!oMܬ ZgHhcnRbS k-1Un/,ő?MJ8n-X?.,z!A*V_e'P6j.U\g-&6!*&\Z+pjZebhnO{` 䞸z3o;L ;;rL/sX?|mn T] A٭jvMsG?;a,AH XFayKķδQB6-gbWsnkd R `hde2:41!;:gG@m'H殱ָ2E\<> jԍWI@7Qpݩ-La9}oQ'a?RRv*~fQ#uD(圌Չˢ&(X)\\ލ+7nVaD]ؽw_ꚡ:BS+_*u x!I*1r 7ގ;s(YUP6e70)-~o^hюf+qab:\zhp}?t2B,aˬs/ǘ߇>+sIҗd=4O@jhbq(5-izWǩƹ \VreHXT_ W&cxg4]%:Poi90R[+,ME(OJVxv+btݍ$s}xT啤ȁ%FzuQw]n\*ѰiLV7B2rU^LX,z@\RT”{9'멕iD4َnol O-RY/s1/N! FDg}̻YIuRZEѦ}"ءVI!p5 [Nq ۰C}X:iYM,* bPh`52l:Y-nN^c{.3[gMӇ䌴S<4ՙѐXƩ311G~:Y 썩l 133BV꠻Tbu\01O K1o8znx60*`F|rW(2Tq7p6:x[V!3ݎ:MV{G]Ũ;S7ñ#`;|Oc/ˣҡXQ}gtUh[2(2+khL uxh|q{)Le}mo7S֓va7p̧bvxY^QzΫK?~ @-3-LZ2Z=:{<͂u'N~B\uNqpⵃ@&(++Ȑ@]`]S~UKXPnup;B VDO&bo?[!XwΏ1[|_'X) `J$"~1"0ƥr{O 6O G4!3MQZ#})$ɩmh1H2~,{gu÷eKm& KyoqO'eZ$tNuA'c2 .+L/=տhgyng%e~X*)-EW;t.ye.+0o-3g2ye_IDAT /8SJz|?ٍ k| ͅ F@m[U> 7U5*\"QV9jRoOaSA:Q+,P8p.ןeG6\N8ડEl{:y~$}H|/z+XT^;f38> [: 2f1pN<=Ɩ <<,.I׮)K[m⣢7)n7`]+@Nw 5*&mA͟<\,A4)Oܷ: @|XgꅿCL̆y".I~(_ؗuk ( 3$-rKʳnH#=3nG #mQ\\4DttHw*46;{ڙRg9>B_`<:` @P(VA뵁U>rե8|}$n#H~5yV;Z=im&L\)"@YtV 2cTgW qs6CQhEeh jVW20Оq~NĞ(Y6:4 31,`:)u<>X89̨X,o: K[bi0ŜfƙY䷽va(Wc넮GSD1a&|usuw-W]pУ$ JGD >~<^w]1qs|R] >jQG[dKVrP3yȻ@h" %vd TnZ.moLyQPWOmĞsLM\Ge-.]x'l 3^}u,g,1-iC)L_ .0_F`]1qduLf,5S/u)K#KkwvNb3/5ѳ:AV+8 ޷BnnUX&o%^]_#[Wc.E?aG6 "6vyaO[=Ȩx(śIAtLkZYlWjxGbNO3,7Kqܦ(_f ځDXNLxnxjx*!qdObq 6GPnGh2o*m%Jջ;! lD|51ESuA"ta=-w|xbz} ȯʃ|~䮘<k߶OQ"2pvZ1ױ^Dz1r%BE9t2 9C-R2s|tѧR`{`T}fF_$6Q6u&9,0*,qA'6dƆR7s`גQ*%60 0!arO9+}>r8spB[ 9ŵi|m?i[WRFWU*NVDgGH2y =!Ln>,Yg8aճ4(B9&~GZZ~{? ~1R5%G۝ga#M?ãZaͤjz ,Z$ʇ(69!Lf҉:pd*DKzA$xpuv1.Vf;Zg)[qdpn=M/X*j!L 5 D1# &;bT#%:LNO]}|e֥ű=|!m !) ;mzPe+E6>F6lEruf7{hiǗv,22Y[m+:K|S4*,)H1]b5];$+Q"X~D#lTWܟGUTG*!?ɗ*WaxhG+}%&+ iU^}:&&9\:w{7?'7y$2g0쒰GDl nN&K !]19( E2w, 73$Xfq9bú,FPRƇc8]|! {&T8zGL={ٻʬIkoRn)T-a0KLdcx\{q5*OЈF :Y1;O+?{}k1_PO+b)\Jm?8߇SY8aeUVb9}Z}'dv$p˘t r_=*k.OjE?X_Ael D 3h9Y8dTa: !l~ q&1rx/_٘GVUpFo;u K·]M.34Vf*Q{6*'_S#uqջ8?0hJ Ґ t*M)WQK.yv5%D7?Ӈ0^ |1;,x- ژc1-rG\̷e+,|*2>{-}|fc={ [giEA ;z?ʋl|| {BALbȘtdLxe?O^3OwnZ|ɤb捇a1Xm]I˙ؿ~Uv\ay?tbߧb4:Ỏx̭v7$ΆLڛQ>Ʀ}LC&sb3&fJl~aVͳ)؁?e &d?W l9t K<+#:[>w~C!+N?bCv{ԵMhG݄F1dI O3\-ަmk`lNKL6Әi?xU mx]m_vM#2QǧZ[#b3KpoXZcfi|\H(fD$P]1٣7|m`=:7'ٿ49%ZzLU=(%E5Vy*ln4TĜ:CRp&ˋ_Dk<7mQقЎE`ӴDnq]RO0kngXO|-D)|/7YL;Muvs'72H?MU:߮{NpI&`b*#RCdOV wT[?88V=qa<oykַ8Shf!%-lR6PkcrcJyև [8 n @l !@6zp$yh SXc18%3O~+YDmEv<vLVL/Cr+>~e3/vb*x3liUiUMz.0]gYldC7UUL橰YzP{`S/|0ds/&yZfX]Wo_rL+un=}O<X#Ώ}oaJf&ePJILs3$&sIPLvdzkem|3<37 WED+lTxڡ5O +Fa^'%Ⱥ qϮ$]<βIhO)ޅ!ȁ-{~k8VT]K)wD#  Gk2f1OUeeUBω?ֹZg9gm뭡@|M˯w(Cن<ߊ 3OlbA$/Cz=$ QffL=I}YQ YGΔ, C8;Ôq;Sevϓ4f:7;nE.=L:MP7r^C]"/ӜaF_!Oz\L/Qءc1٪Q'o;iŁ|m$\2͹TV3̟u2nq^[r*(-]ψ*K;zm435_u )kQ a NZVԼVT=P[e 47wʫk>۞2*9\ Kk BȏLʟ $md2jR4ԍGCͨrHqkAQ3VO?KhFӐe@58e[&(B6@5?ګ;8{s{vUs!jTM>|_}u;&hJcMK<)cWjͺ#s/_VC7t:7kmހPc4~oQFkZ\"<;Ç#^5iǿ8bFmFDufmw pu-O i$(@nFӟ ~\w+p.|ɔ% (Ɨ#̄x V :د2<@n %x4^-pD.ifY3676ڋ|=1o}_$퐨mH&h.û:|8x[Bojۘ IG)[w> f^̤Qe+B RA%>X f?/jq.VR0k-*=ޜb[8`!Fc(IzR;9JC}Y$%Hh>eu ESt NT6n D90M p*L ޛ֕~*ao],G锢 G"qRWDÀ`<:X]۪!NaK?:-f[Uv߸'w-7L bUYmxizRiU6qyW/]ۚ_$Qz#=vN_ V+@-a,/v-WJAV= g]#'{߱a{}țV)FFuJH}mj(|C:IzPb^M(Ĉ;E&=B*r%;{術nw*2ID zz9Lf;+v W_vCV;tva()5:U cXPRRY6y"1 xףyIENDB`itksnap-3.4.0/GUI/Qt/Resources/snapres/RegionForcesExample.png000066400000000000000000000021271263013355200242350ustar00rootroot00000000000000PNG  IHDR((+tEXtCreation TimeWed 8 Oct 2003 16:47:58 -0500ZtIME 1k]D pHYs  ~gAMA aIDATx]OhVǿz&jSd"K4a12%:z v%J0u`t(3e#Z@I*LuEޓ%{}?InW bʙiLto lbIWg:#N~9<*; eAԌkC-R؝_Ip"VX;~@i K)ݡXoⶳ Bwʚ rǗ"RW$aH~z}p9q*qɧ-D3ܱ{9XW%ƩJ ,|a[mEQ8>.8\bDVg4wfd//Tmz?>p8DhƇn-8h5oń$N߆'YIMADIENDB`itksnap-3.4.0/GUI/Qt/Resources/snapres/SnakeParameterPreviewCurve.txt000066400000000000000000000006251263013355200256410ustar00rootroot00000000000000Points.ArraySize = 11 Points.Element[0] = 0.2875 0.53125 Points.Element[1] = 0.36875 0.75 Points.Element[2] = 0.5375 0.7625 Points.Element[3] = 0.89375 0.84375 Points.Element[4] = 0.5375 0.525 Points.Element[5] = 0.6875 0.2125 Points.Element[6] = 0.45625 0.31875 Points.Element[7] = 0.1625 0.06875 Points.Element[8] = 0.38125 0.13125 Points.Element[9] = 0.10625 0.125 Points.Element[10] = 0.19375 0.36875 itksnap-3.4.0/GUI/Qt/Resources/source/000077500000000000000000000000001263013355200174515ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/Resources/source/CVS/000077500000000000000000000000001263013355200201045ustar00rootroot00000000000000itksnap-3.4.0/GUI/Qt/Resources/source/CVS/Entries000066400000000000000000000000721263013355200214370ustar00rootroot00000000000000/snaplogo_gimp.xcf.gz/1.1/Sat Dec 2 04:22:26 2006/-kb/ D itksnap-3.4.0/GUI/Qt/Resources/source/CVS/Repository000066400000000000000000000000641263013355200222060ustar00rootroot00000000000000itksnap/UserInterface/MainComponents/Artwork/source itksnap-3.4.0/GUI/Qt/Resources/source/CVS/Root000066400000000000000000000001001263013355200207410ustar00rootroot00000000000000:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap-3.4.0/GUI/Qt/Resources/source/annotation_28.graffle000066400000000000000000000054211263013355200234660ustar00rootroot00000000000000]s8y+tyD&)iv;iMl׎gn%We[?do}7}vϟ>Eͳg9o[V.䃥jX.X :cEuhF-Բ)tSNh# 8x_WsW4N5d] \S+o8zR!ӿNO4$#/}"&qc8wEt]|ҶDUs6sq4 qodss) P 5g ij{ō]] ^Fv,?x!>[0Û^?9*hnCQӒW^a]q2HcH ?f+yaSmÂ% aF&5E 7(ܰ,ƍ=0t],Kr1)b&߈7+q~ur=œx*Tfý(L;bb#_ԚήioXJ|7<#i*C;r_{ #!ys\7q~/3¸B  .vBibY"g>;; Si=w}4ķw#ăv ]TPlօUs/ue/dK H0L3jrRX' ö3l-2TY2`Ma&DX90tiQN]9iG ~N>R>NE VWG:5tiΩ#+j\D%_m{#_`aVBdf!bMm51ՙirJ ea{Oc2eF M !Q D.{XǶˆ1l!.pH,X8f&N -K:j Dd7qr7QV5 Y*_s?Xᦎ e lZ̞fںI,X-ım*eXcqbY ò_y3a-X?V}ŗ)#!RM-j"?3P]Bڏ9*?T:} q"ʚ;C2ŜW £d\Mj}IaW8j0y-oKl:NOM l;_l?3jHr}N%btbd 7)oNֿ 1mO It-fٍan'm$f|"IjXp*\lyv#Ux5ok;\ En څ `'*fKF?a8Scclw7z 6G V'cll|NqzjӪmq"`VhUxM'o,uX2\)@.>DϮdf!suLjT5dSFbRV"Ç E!B熱agvgbi&e!Y 8YsF9oºap1[V̒@w)b]ӱ*^bCJɰu3UcF +mUljLXZMTIuMeqX 0|Z-ʽ嚌W! ET'Ts9ZtkA52QU]{ݢu[[9{NSR?_lgDq"y.u~|t npz4A T'E3ckEu"R㻉ms䖝3]4 %/clO ԦBdYqjgO+ kgb 8S '߹>X3W]bfə+1,@nXn$pS=9!z R9eZ+A;23ՕiU :zêhpX=t[vMN"p=?itksnap-3.4.0/GUI/Qt/Resources/source/snaplogo_gimp.xcf.gz000066400000000000000000002612241263013355200234370ustar00rootroot00000000000000T1Csnaplogo3.xcf{pUUݗ@x hDl@^mS=>m 3Ӝ.aH05 LPtBm  #J+Jn"HL $# "C!F Y9{{oS=ekk8?oa8T|~xK|&u {,aJuJmm%ƌ Q}\Tƕ; UA18IʣY4J;sj:q4٠+|X9#"MI+ܠ4ȹHt%7j:1riQk+V0<1KPkhH;_R}\SɹxhK1\5JG11/~ cxS:{Xr/<EqRfVө+c.m4>V vrL`et*9it⬫Fb]=Ldg]5$p2307zV*s4JiRζm;ì'ɶRi=1EI9vVY3л>U `ZHkķJ>4-R4&$utN(FL[NSr2Ƭ"?c&њ ;#IזK;>!;s3a){Fڙ@}9Ks^}\bsgi:Yqo΍9Yn8 ɦh5fS`OLDT$E+$NH<0쏕v2)me>A> Wuki:y6[Ou~ 5ÇiGΤu ~ z3/Etp%N\v h ?5^nę' O/T:UƼ*5&:%<Ǐ(kT,s?Qts/ +>^I(> -tV4V*:Nv91s)JG17ZIg y5s,úTLVaQK6en}e=+_}g |nlUHv骴cXm>3AחZc8l#ػgn)X3˫IEڣw~k~u,jOkiOӧ8JYW׉8r@cuq&S8&gMS8kԱ|q>\ 7 a2e4[NgKן.s:y KUdsZ}IL&sH :V"ќ|C'~ye.mgף72E˹xEy%\r#WcuOhcs.F4)Aku҇&'0EZ&ÃiJuY1F];hsoɳ'&(yj%ch]=Zɘp޽;[ *($u~ljw9ANcb7%:_3şQ+I%͒:tWkc\&v7gԱmmK-&|NnG,=HՃqB c;Hq%7QwkAaIֈ).W9m|Wh?yK17FؾZ=X)[pgfgiσ[]0=K>{>=P+AcȁMLTڋy?jZG j%)??Edp%V2mZaQ+AQ+dN>3bzy8r14V_sg˔+mр{)΀/PIYN:94PR ⎪Xݳx&VYGcgRn|ZI&J2'њ#IܘԖQ+Dwj9y hdQ_j0:TkǹNn\ M"F.mVhm%V:{],qndSԡV2bZL:9w:}JSΣ0 hz@οt~W.Ei4VuLj%+fYmRZi7]}؎~Ȝl)t/H:RZ^ZBk2֍R_gh~_1c;bcߠQ\DGq vs?ts?=WB1*w;)Znk^W!yC7(ԪxL&ct=9j/qJ^},K_}rw>7*k,v^&Vb'Jl;qy){KYt7DW\L\qh[.0֍iƺ)~e|/c[4>\M.4XWXot|}ډ{Q+p.SEd&>ycS9tt2ǰ[+q~W)dfO'xbN୒ ^gg\kzB0:MGe"UIP)Qq)A9Bז@P) ʴ4Ňx*US ) ,!*yd|V]UCUl֡RB#gtYh˥Mḥ6rݠ}7C^E1qX ǕK\Kb49#5q/h:b\tƕKntcfVay@cc}DB1 -LJ4xr.(cF`Q}\5V(>+j^XJI_:@{#܆{=K{|Nnurn0.wy]ۇ-K|JIU=*%6o ML\É)&n ۓ7VSRҍ?R\mVTJz7mĴ6o0RVۃ>ڿ;ꎽNNU6_j ^KCihƴ؝;S]|XnhJ:_';xӝaB6x$q9x֥h͸=(tv~qu48ʍ˨[IqhM`H#7&%}TJ&|Bv_!; 4ܷ|(::OQk5q>L+|3tnϔ. )nܚd1 S)v`⋷J2{ OPo I>*%)QQ\y֯Z_t +woA`"-q8<,4b]iyo^~R|E^.)TJh ?5|&cX2q\2]+- w*ǪA#J%<KbkT,s?Yts?VqW}I(>v_^W!ds(XӪtmα:픯Yw]rY_Dܒ7dYV% 5;gxscc%nR *%6iTJl;8llcػ7d~)fftiF_R&z+œ;tGWk::qFИb]ayf)NR6{ ^D1G ̓oeȍC5S@Y`6ζb6>- [=?gF̟yHOF_竣cjqa8{NޢQe.'lĒEyEoNJU͐Ӓ;o`|^Ƿg<ǰJ?J/3-L"$?5qG;iY㮰DϿN7BO0G Ҡ(hD~7 ٲP}$sIђ? i!8O#  Փʡ Qɥ̉Q6]_BiJY4bIm~vvaΈy2CE$?%EysXxnNhE @sYˆCLC:Yz./hH'Rs 4zy$iO0c#XH"=~vܺa}]nt9ZvB$1r-K={S;F"MM@#DY oZR܆$)]}OˠsFZO,r#ݾ#u)Hfj0kcE+罣V{>v#ۨ,leȯho&w;)y+r1KM1y=.r/}~F<ҁ>:}m"n7QO:EnQӧԻ _ue[h 2̔tI'iJ:ۉ٥ْl1K%ffV͌boεdN}^s1cQB.e@":^ G-aXS\!Q&t SJpnH C0)M͡SPr;d)3:%{L$(K &Y͐Biӆʑ= J&HZ %(jPcY$ɀ^NuZ$--rUJ2dw)b %b,dPr T%ׇ?pu Ԍ ZJ8]r} P9@ؙAə*ŐC5.K Cuw;Q6צ$2҃rQХ(1Ì=a0,P\RFڀMQ(R2 V>8k35) ,hkm^YRW&41%{M6K?u53 C06~|X@kP5 M S$ZP cliqÂkc C%rҀtt+I:B]Q%LPjMLj}9dNL‘K R#?Z`/̀G[ %TL#X5 ʈ\TL@)57 # j d#13I=H^FH5M0R F00_'M/}IPƱ).X=w=m 1=Xyl(-%[df(8z(C&k0Pag&c{T(hz+iթows`kaZ\ 3~57-/ 3#ՔBCh__&2-ȩ\2jDZ(jc5t}BP?]~ @2 pН,ZrC\,jo}v9Gcmz.Njo&:pM ]J|!5+Vg̉:UZN5p:_D&z/tJmj #4HS仓/%oU[uj*~CDZiU5H4%N,`fo}!nzQYW "@)}\,lRʰKaOW,}-w ԝ0a]Q 4TKcӞSǒ_ %HщǎK"cj [TVlW@ J'Ld6,6sF[>`+/QF!vW^[u>i>;wr-qP8Zx;(Ȼ_|XO,=|\0 Ơ; O*|J R0HCqC=MO|rfh61X &˔aȲbL&,s3fTڊ,=ÿN4V%wq}{N9 KXd͞x_Og!㐱Y$l>rr@(,$b\|r߱]Xf,9,O?|/Aw |O\rAW6 Lا<=t#_l!tf~~cO+NbkA$|9/Ϝr;{G:~ǟ[וg/>RgͰϜ=?:ui˃` E/Xn..`8>{8{0 Rsa\خlEg/^>}BVe`AQY,-Hy7OS\.CtIl-mpx׍JЈXNXUOh5+7|}=ݮzLeY쐸?/y5o^~||<=.,&8x  .]vzH@\׍ޞnGD\ZzfYQNJ  UsÃC}!!CiҢ _6\bx }/2,( NLKI)d#]1;U@!A$g.t:YyYAnr IxzFDGX:ervFzV~qIaf&x302? 8KNzRzNAqI>`)+,R9I$JINKO+SZƁ%(>%6.> M#bRS2 tc0ƸHEG'%$e)LSiԄؘE-q2c22),ӒR38qQ ˸䔄̷(,32_V̓e|BzVj,'ФNt\BbB|\[5̦ 8aQqaabp¡P_784*2.6$N] . nL7&[BLȌqqd1]`*8jae3Y k’l ˹1K,L0e%q`M/+C\ȇ3#.k0ktbKyrvvAq~VmP@M쌌7CP svr0$)BkK ңA1i6dBHQf $֕fF"hWX]WTs^323G%Dž@bjvscEAAQr0dҖQZVKLy~Ff<`,)*(LP0dr몊rJ+K `_CZYmC-72N2S:uCfIEumcyYyu!1醬ַT$+C^IqUm}}U9h0ݽs`ld:@+eV5uUprS?F c 51w״5V46pk2iR:w&Z1 D ܲjn}cO[C]CS=& £culהurkڛ ip}έ"/T7VUrښkum -[C4 M޺o 56(Z]kGw{c]CskYk><248v;# PT[VR\R`ĝo(~nWGMyYm[GKsc{wWKCCSS|grwpl{ J.0?$.Lrgb``toƖ7B=]7k#x!5^ws@.mXMM t҆5-h_:iޮfŲUZ;PXhFjol#m-k:[:F{}u-ʹe'mYYr 5C.QYOO+KٍBGhΞJڰުξ6 }]=%WV{Kgk{wooW%ZoԾmmk-+ں!}u z==}]Պj$ZǛicw@W{[{:ۡ%;Z"(,{;F;[;-]]ͭCn `7<:vrm}Mt7*f C0I}6v77utvsߪʁ477PJ*ʚ&nY2mXZWV_:kY$$U01C3I34uRRDfR%hFX4KP9I4aJB,ob*!q/w(yPRV,ȜD6@V9 1L>a$^̑?bX%Asb 긁#j(PW]9E?'g>PE(N UeV$+!Z@i3eC(,ڧ\( X\ׅBJ }#ĺ+\\$2J>2 2re9G%M"TdPd|( ɕ}` (}* '3sX;R!ASHf^IgИx4WE!%2ұ=AnE A}^"'2 |rdR>ƹ(AYK"eJgrf+fU>K񱋒[Ws3tl.|"n Itnפ0 M gii0cy2SJg+fr_ ,m>J-#' M(DX#`[( DrJ,B[2ĄPJleBNl)ќ:T{̲`,DBڶ.'X7lHYt" + ^/˥0Kd!Rٖ~dRîّH,ӆPĐԾb@-^>G99TR:Ri4v1L~3$3 c&Klohrz .IwF bNDA{JQAT&+SRm3H1 Ś4 D8%ԛDZ"3xRl LV㸦)RP5XbHLnobdDʋRB8ż&8aV, s$!33407847B-y6{F%tquswVmt%m Y|@#nl.a:3L\Udns*jjj* `xTUTWR*Ø\EUCKX_[SM0cH*ꚐgLU #O.]HWbpUu-}k>lؚ mL4!zXEM}nŏ޸z*bjYƩTؕҢki`3u?"/!tV׭XLm CTгiԆ*ֈ֟.%ڶLGCT]SSKK[KSSCjehK[Z,'v[lohh4bjiAu}AR\FP6.254272 szS]XO>ϙ|S-c:RݺX& -_;Zؑ,J׼ӂ6,0724=|qFdBݺ(rG S{b$tW0l&&P `}[ Kb|Ώ;rg(7?ػakX͗\{[*kAY}u]@.p&G;VZ+-~ob[KzJX-ްG0X,237@)lͻ`x_X[-ڰ moB+Krرs~{Wb {罏l_mgfj:F\z;wlY\_K[qź-۷ZBP7_`Ѳ˖؛kêgf ?VݰeK`6YXu LL-`WSQQ3sXpVF:,֚iڢ`ihclbfnn˲i p5:|`ҮԔii0ux:AW,.kCaZ0dc2ٚ5 gΰ<]ss5cc<߸H_99pۺ1wS=n_g+̳GO{Ʌ;r W\8F.~} O/ L~W0qb̰;z bh/g\rRY_y\sX&2aܹYW}c˞e\r8šsrV,|0woo/>uZ$d @_֫WB"3R23S/@ 1A~޾!70S"#Sғ3*s/Bh5LM]HpR22R K.m#bqcxPHTtxHhT(B'ӳR3RSs.7c+'<4*6:<8* KKJIMLOe1VG\Txt||Tp8D gBҲsS3r3Y`i@(Nbrlxp0>~//55(+wXW]]Y,LJħ$ńms eYEIi׮}i/NGgd_t;;M+5=%.>-;+9s<2&&<'50?9NZFFjrrFNJh0^F uLQ=-={ {'=;'=9)='=ܵh'9'/+-#77XRvbN3envNfVfvVfjRB|bV"f&eĹ#8P0OWW"vFIQqql(0Z*-?w@2K 2S .s#c8 yY!=]lVW_TTRf%%&&AggGktlV!1;)/7';' ?'#;;7'=)>.>²줨ؘtO+츬ܼ̌ܬ,̜Tn '!>&9< P#R23RS2ssҠbq]('(399!!-6"2.:9!"6?KyILLKDF̢1Qq1q)9YgHz᜴xNLbrZfr||j$=(-*/+*Ȉ󼒙~qIY~!I񜈨ԄTǮp0+;F }ԩ}ڍ7xc.*/(ȋc.HdrOAbIK?\n^P[R[fNFs 鏹4ciٹ\iA>zu<8 2JN=ZK+JKD('>.~F\VfnQvRZan:ΫS켜(9233a`&Azu&$;1.%;?+.Y5aHKIJC/%%g@,ᾗ M%5%9)"`t#.)1&>15;73.eQ2'Lv/=)1 /-eIDidWGScbRs {{'jR$8i^ ,91.s.RB׺6Kb1pɆIՍL, ˅ezq.j|kX^ wYy[͊UZ^ZZXQ22J+jeNFQ!^QsJju[Uʭk(Re%55 5\&䔗W5+WTNjiuIN~EcrEMʪl+k,W̔bnU jNF>[o jaAQ9 jYEYqeSSuuZU[]ZRS[4r꫹M mlioli*T n}-[\P m5uuEdAMiosgHrAmilrzAMlk韼?m!KdAls?,ɂ011?04<88ZTAԀ7ԁΦ:'jN0;Ƃ:3߉ j}w&zNJ_/=]medAm}kh歑"splq}C5ފu`pwhҍ\6tkbfo a%D=F'JՁA@yVutMOz+lookloo&kjPwWD{NKk; Ll Tm7oݽ}kbc}}7<1;|/kh}bI}n}HwWg39;psb <;Fn5ͯcc`OgGg=YQLJ{{&:Zڻ[zWԺAOBdE, N wwv*VԪށћ]3aumr4ezGnNNtCU57+;#=у-mX}c$¦ގ{p{qsdh*{2 ~q譆HR¢ƖVLn wcaIqs)jh^7A* ڪ*nueEq2YR'FY381_-=P#nM6_ WXR纇)F&Ɋ^=:>6T+j(4dYQ;{v B˯ð`'GoNt50:9>Q+j4zP3YQۛ;nN6z 3`_O#YQݲzOl{]Kݽ`?vkVtXQ{Ѫ^Q*zZ:;o,[GL֍Wmw&jxWVV0+)dAVVWpZʋ"o]PuՕrf_ÐUB)4G]DR/%SC"q/EW0-()O"G3+@(#/*B>:-;Ί$䢀7 Y\2+(p9KP&X<Oh$M ܪʊ XJKJ H)D$EBK% @#\&q $[Ƨ9T74+qa֜IIӄM:l_s^aq{^qePʛ B g_HIrlUJm-`db)4PQ(T;qF&(9,#hd%BlD$ȅA*%&+ɻ:DMHF b*,5-Ojұס[ʱ;ꄏ!HA2#{\ :hN]sBD,q#~/PWQ!bVXvesCҙ֬{뎽t$zWq)8f&5}_q]p1LQ et@ɤC`T)/ýjT'~=PɒT|b9%}4sZYl4%?ɗ Ps /$,όEFE7%fgrԜl KכrM%$k XgɎX@6e% E22@&LBK*%{^ Cd'mvX&R"g%b9aE8"!tVL{aq9&IJ*E{u?bT&ˤ-'%6lż;ѰCZзEsq f!-`Ф$™h\o5|hV1D, '\X8,N?;>a'9P23=c dg` ec"NTxHPbd,:W䫛@pVoA]dM{!#OZu5U&cCo{*"}=m|^XOK]!Os-[dkdfhSׯt4'O5Qß`Ŗz*L:m{ۜl4do9 ;;[@#n&}AEqX04r:(סwn^NƲ*SU2I1dLu0{kJ[CMEGZZZL->\ƽe CwnBCAC[G3161dV1SE,_زH[SKG w"v|G+S=Oo`yk'DOG~?/8-7P}Ô`n`h"x7,5 D pwŽsco纅 `϶ lmS0!ܴĂ`=k,X<ǁ_n[4ݵHQE} Ј۴Ԁpl;$nY`*PS6a0QTQ]`w+N MuAH4pͱH30+ܷkӪEfFF3Fz:ZȦ#ZU߾i>Ӗió05Ak|}?Mm۰jC\BRjf;S 67)}+ԟ[hPoV8Z}ieE6fEV%5s212oqr0,_sc==/0`'1;=v1Cn]a!OmNzZF$w&wB{L]ۚؐ?a PhnE֦fveJI> OgKp~&Ňb_b)=j6cUK헬11ɜTML޼}^p"vצՋ8mܹ;ٽk];vo?/fFT?dName-[nٶ7{ZO>%O{maNV9d|b˝Vs 6aG]؋~پqb;;9vWIl,Z|%˱||;>5M-LMFs&fVVXO߷{SX/wlZHO'O2uR#8Bm[wÔ0.4Ѡ.j* #+hu7*ȥۺDGEZ0I`2™?ߴiͦ`Ǘ0aTKTԵSןi%6L.p{+ plfɾv gNzT޿w_m,-:mܶs=o?>طikF ɪM[ngnoo[`nodtMnݶ}HOF>%+VXzmvn[B_KSq˝VtZf5.$1š4e+V,s0RWU74Pw_|lhdbfɴ|baRt:LFB#]J~u fVY 0f2tҜ ˸Ú0e0aIkZ 9ӡSl@Y Kǎ_f]9?fk:/GϜ:usgrǟn|ͩs'~ya@,*`C?x_O>s?Ï?'Lw~&C?Ͽ;~N8rϿ|8ٳg/ӷʪ{?z׃ɓN>{}!K_>n|ߊᇃN>}'o?ϻ}nu`!@/"o8q艓Ǐ,:{Wtqʹ ]\.ۃ=<ݎ=ܟ>r%r+FhT~R+.~>ftnlO?|wz~/<y tGx{zz?f#qlq>-yȄ谰͉ qZu7Wȅu <2*&93/;11\y?0?(":*zSGq?bGD+9O߄\{p%0?928? 4xʑP؁Q1Ju1:*60=%=x X[.C=bD()wjN^fZ6>r_  yǡ72FI2v\O=JBB"~l D)Ylh ><@̏A%!:H!@p"C!ըX_?yEȳz&7c 9j;~Apwu^ [, Ѓ}_'6V@D)|0+ 䁗H řLOGpb.[tB7ciп ky 8( 0͎ISI"'!ezqs^J 1֏Aۧ&* w,>`^f鳦N*5]qNR2$[z8$\XSi2%) q+o'c1KzP ӗ#2YnD#$XE[G$aɎ}"<Z@~g+P 9<ӗr ]IEs24H\;CtbO#"Kĺˀw.]MKM4.m]5gz>9а4NXeX\1))J^D l3.AY1IpÕ;"vtbBRRJ23Q i=}׍ +\<`FN(2_K32`1]ElƫLWۅ6e͸p{7ww\.04!h7`,U#ylXSjTgX5ei3 QĨ7B2srӓ|03¢$膉3 I *OJK+P-.̇NojzF& !eDk z?07;917 ??<Ը4>șgkԧ+鳠҂̼’‚~i4qjB Pi ' L\%EjN6VԔyGkk>Ґ[X_{Nfj^vZf͓DZ;{HPY$+5)5_\ZR^T|FqdΎT,mTt()e'fee$$+E8J9O:ij&5[_XZ^Y ;4,++̓Sjz;J|Fh+/*.-Iܢx%FL)gw 4zTv4UVVU֤x%g ;.tR98ޖKv}w[]UEE)דLIKR33 0um-LʊꖖRE%|:o2PUQmhm  V3jF;:=YD85&]m4HlRl2Z&zz{S֗oh,AjEkm\XS;ysop@j>.moj. wAji{{7zswAjinhi)/ȏ AjYgQ7vgw`AWQohhW*.w*y9;CCTDs]cSk@߼DeY;A䳮Vu LL !J(@щ75'*}J-U2wٮxOΞ-SW)f;ʃ05PKSsSskGwO-r^؝Բ~%'oc;! LHljljnns(hY)?kFw S#RJ:{ze) \L:VH@4LeG5p*hZի5ST %Let6Ut%0[Ij%05s`R Sw+Wtwd(`jPF`gBk S],0aW FLD\TRڡ!%է>e=Sg:Oh12T@UJB"QjR? JUj[7҈*bꂹ=]So*e.Kh%KTG^4%#i:q^bV- SJnO-qMD"YxM#w(Wi$(կVhoO [2 Vn%QKB}Pv0c4t7חޘǨ}}JQ^O+ɸuhtg.=Ju=: PaipVUʹ6PZ$ I:W"^PͷfB`fjգ5e%a[B/HUxr x"@( &Hr9T +lErX$&QB0 NLP /?K%d3\T5RRc&OPEC )-J,7Dž8V IQ/tHPBP\"J V&WQB*m&|:L|^7TZAv'\K\&ObR .E]A+YWX,QA9><^)BQW8SD"^H҂4c7YTIT{NjR|8C;˞y*+$8 O( #}=^u _Ir B*K$l#R2)\T(0=lč2*Ss17ID1dUE$ OIJ|X8O /eB--I _Wx(/=Ev;\3Ў}RtB\GDs&y?S J(|qܯ@Ɣvkf%KxBK)?PN!N D^|>M`GX>' sDڊ`"DF>IԖp=b!$LVq={$3Uܔ[( B2*BFIR1V)!7aVe&̆ ȰJ+.HcW2Ruë$$4}D\sQ5+ EW(M\In3QWeHrC\ GGȟ 0JXRԬ*ov)%IӇ|4,sK^(SS:G0)Π⊌OGXQT]]Q4Wyr(\#EBp#:,ٜPFAO4 Ӹfz$LFPDB^_/Bb@JjBӴCSdDb_EHoYMɸ@uYZz+T G?MAVX(E%x%|BX #M\&))l "\Ք-#9 #7^G4/} J:xI8 4dΓs8^!.L)y){ϲشHNL\\h5!XŹᑜظؘ謌yF@pU!v%$=|E^r j=Md6d*ijhA^%WlzHK30dNji/(SsLu m=Cc3Kk-zĀ+u342T(5MG{ۗ,\`omefA k YUuM=#Wˠl"{+S}-5}r[]uyeM X[V.s 8mucʖ9Z*-ZhZ=l(o9,+eHbwmduLN}W,tXHB_l_VS߲|/v[bg/۲f:.<~$B鞪=[W-^ʘtkG<?Cʷ\E/L(ǎx{`/st98lv77r6޻l߶}wE]ڵ~)%cd9};{mڰa޼TOl(I 6o\f՚wl\_[טZG{wlZz՚u֯[ իmdKtXlظ~+bW @`oxjU?!|{ P]?@v?m^L_G3< ibGf_ܴf!7~Z66 xNuK3TХdHj[,\;1`ummA Դmo;-Zo*}L|U =N[vV@G֯XUy$¯< J"߷}ݪ_/L9u#E7}J)ku_CKY ^i5^j=GDݽe7 3-`/Z~Ν{ǎ"S2I~g8w?UG*3V]aV睻?%ٽE޾;:,^rʕk֮ݸeǮ}w{wַ;\23Zv/]b͆Cၽ0Gn۴yˎ由,Eelw޸zNAk}~B>UNkAjidd0brM:;,ZzKKEl_b5[ܬ-k-^[h;{`̞m88._hxZsN|GVo߷G޺jK][v!?)%L{!6;t0rXbr!{wۉ"8Ɩ ׵P ({%7/33.h`vMaa=C-|ǀOfSkh ʞڲnӗX][Wiin{Oqnzg!.g'{#mUU{y̅|V-^8ߺXKd"̛>}v /@ U&jztnZ,;VrlS'L~3/n޾i T;;VӔr-s+mvmx^{gu+ZYOpmH3xմ`Y]|F{q-hW9owo۲~b;K#-M=Ev_-m \,\ ۶c];wڱ{RGk3muM;Cۻ:]d+-c-pZn{6nXz2XSMt5aчʽuvgmΛ,%O cf`2w\ts9:XhD5-} HaJ''e mR p=U@:L-,-M `EZ45VN`eaBS5`e((?mh E o捍 ^6 ˨k05:`DD<00:,sK>F䙟1KP ,m@]F^guue5Xju&!Otb|dxt>8nGǫW}#B<WdsINMNNOuS<|wtU_ l/"£pcӓ"ȶW֣K>׃9!>/*YȉMHyG_7b"U#S센ؤDV ˱ 踘?+hg|/IK*MxOxhpPHt|R/ߣ4;18759( n@jIhTBji4 Y.DE<987=4x.#"OҒ. Ƞ.g.xyWvF^IIyeiNv~U+(JpbY,# dוN|| '* ӹkVzB\|\ '. >  '4 (.nqHa=M?f\\ɐqL+kjJ3 cI9Y9ȓ9N!]7XˋK+uuL7y2 +7?/+u$|y,?7$}R|YUeE5qr6p+(H/:97O- xUWWV5TϩtH47+<|/t\|1u[ ~Y9ȭPaMCɤ8)[SW_!^+Ssӓr/Px!i.\ZUí,=éCiܜ줘S8kMrtH"rYXuTY]IN.-ΈILK`S!\HWJMJ8S\R^ZQV#7#4'!.55#I!0gee$D`gd.zSljZ~a~ZTRjq7Ngl(p )JIK枓+( xNAU܈ Sã sscO\S2rrs裌^RZ~qDt;Ņƾy*-9:K{lzzJB|RZVN}FP/i([ v.)59PJf9bVK+F'ĔW_N^Q QkiFc₣f+MO M)NIIF&$%sB9i8›~N""K!1YQ5@F*HKKCccÂB“"qw]rVZbwg&GE͟ᔘVVY: M & RbC32"vM()/JOf]bf=8H419򸧘b7I ,ֱܜPA1z1r)b93Wd&$! ?Ę`oQqI<ќxX[c"""QHATbYsƉ E"x3tj,s4^WQvAMt,Q:Ut #*>.'p8}᪸T]Dge#Q(t?0(zTȵФTpJD|D.HD,~>.ׯ⮹8Nt 3F #w XHkt ' tŁT.8 x( 0N\! p9L8Ww_I=_Jύبk1%*N zC(ǩԬT|X  +8B- xŠ`Za00 LE؊Vqel0gen,4CE nϔ%Հ&D `ЉUga}eFUY`SXJ˪UU-%,RrklZZ=M*J+HrǦ`Q5Ms[*_GV75wtwvWcSvEGus3_cӪ֎6V<6nh~}~UJjV8h\W|Žjnk|}UVJZN%8q+*^1ȘQSnO{s{o_wg<:-67&(y.4ԬmojoxNٹuܚf7MɏuU uK[@ok }u”Ȥ tZ;<:?S&: mlnotQ" 9.4:9?:\􂤊ZZZ:sJid,48}0Tz7uv ]RicSU6 ߹vuwu 6{ic{Segܾ}[utwԻ\o(xuEn46uޓC 6 Edn cce0Ët٣c7rIB̀Y\RM;~飻oԭyro`i< sb ѝ@Gx_70219302<ГOyd@ M?!qP@ћ}#!ϳ 'nTxφݹ;ih34<6?4:MCyd j[n};<񠡩_<%"4m}i{=}]c hV7ρ`Jri靸u[wthrhgpWKw+)Df熁wnC~ovh48rp;F;ד#M *en.ܡɛnOvдxpDhnkioYHw|z$sRBcMj'olSbӊގ;nc}=]}#cT꓍NN4uU{4b`w{k@Ogh3A/\+Pl`NK{g<860T>:1:82 Idl>P7~[~kw@Og[m##F]~ Oޚhmin3ޚ=4TK)h5vo?lh`OGKSI݁.fƺN^YCKhWc`c&awxjnl\2P%,ohoi?.g֭vV`WS=~=}uu5RLB##GutMNN w\R'Ƈ!.7uv4u O,?216cxTO55^[^U}]A`ycUysO}zǰ`j(/#pttªPVȭŀ˭k#J(Pg*]^O{kScԏ+f2ZS=tXظ44n¬FCS5q3r+z֣`Ze}SsKP_+#{p+f2@Z%"SAPihj%ԧ[[{'SUI#@T"Ό"rj mr42kllkiTԳcrxVq"LZ:^ 2Jj[{QҮ.T ȡLZaqEmm6ɕj*f$NLKjL-DuYPV^gP3I!R xi֗sp,O.=V@UqPFNI2 WUy2\q%) #G"s.D {PHpUR)$GB% jRbrDP.{>HG(J@*ds8L"/z}zH"AJ:7 Ǥ!=DG!7L.m PIK(yh@kԴHhjX.6]Wru4h}W4CE%JTtk9Rh;DVI[SL)n xI"xG|z!v=ђH3wp _@"y*,a]`œažKg^D/>ȩG<oR.%CR!E%H/fSP*%/v│B)PH)>!ȥmW,2(*zź Q-(Bc*'jY,gϒ=YC)?@65d P G$^,V&_#Y6] VyD s/.4 #L@ɞx0gN(& 1sADI#XY|DB0A徨JI2K+C@HbVք>pIog<%qͽd J|$ҟ3RJƛɻ3`h ԕ?w32jcjH_G_P(^49Jj$~͑I/IU47'gya`I(G-FƬ6 a2/[oQ0fHx\N'Hh5<H*Q_#ũhFH\,?E3U)iP"yB&qzz n[{L,BF΂#dC2@*C头OX}L)S&b+.`"ίI)9Di?7KL&!e^D+8GI6NS`/uL*$H$S*y}Z'x8:a dr#dc@(s)\Ux"t@"G=bE2+1)L1Ea2vUd`X& gAu$ɥP]0=!"36Wac244*;<ЎGDP١]0:;a󉤳3<:JvY  sS.4;Ɇ<Zɛy/aFES͹D0N_D-dQFbv .NLpY92).DV{nnLwoqj%lPBƖX>"_+x Gn+HׇW< Kx/b,Y%=%|@8ͿN YxD +I<"`mG*bdB1SJMO!? fn$a*rh# ޼:|9:Yl3 3ϗSUa;F&CD10R3* ZZ.y:Z(6xL" DbHW&S!Qo^=p( u4RMK7}_H5/5'bUzcRQ/4PjUM=3zr<?*ISBUKLuVӮ+r*@C%xaMkvIdn=;7;O-Ķoߵm-"2_S9PoNd```$9u(ʋ;ݻk']ehh(?Cò߿{ǎm[nټe=>Kclt6!o32_dj*'+W9ۼǑ)8(J]V.vG6DSwt~ A [b$2^=DG6U^m`af|wSPmOxSKm󉾹Ê Pvڻg~y;p`;ggl]bK&Q#kw? 헮ްjy6gm[7oڴe];osޱv7RO2^j=,A&g` Rxd:=\te+VX[qڧkl!uM=c3+kԀq˖ؙ!_!d%d^0X`d`jF&fDYWBuMm=&k(TP(0u"=?= 0  [BB=c1ˈ!gX2d0ըLL=Cqd<<;PzJu}Oobm<=_zӻMF3u)ILpb4_~~BˆYq!tUAAaQ1 yrRt]m񨱞_%R)vQi~}CC>$2&1χ~f*UsNҥ` aG\Rlj pbYE'(vփxGE8 9 x8F{ 8⵴ar b.G ĜdNPTtxTB\kIq>91 eHb}OqvPdtd4޻k'FE(aL;A{qh29i1ʃٞ.t☄K,|b>[ƸHNkÂ$ilO72 )$;$,:>.ZpŐ(ӂ8*.v*يg.{tD.d;:INHDDx_ MHK `]z!d$EG-LNJ u!$scs ̫KI.)!!7zK ,39*-Z_斘(^MAFV~iy}[!$1:,((,.ieB"k#M!mTZrwUeUMo#ⶪ▗W6D8ƂxK/S5VUU5RR;ҒK !\o7%ښƶCi9 m%e9Jp(56u;B,LKϫm/ɭ*GD+ S[TQ-& {-kknj({!Uw4WqKZZm_SVRR-V P]W{{[S]YYRB_S( ս5-=%!W*TF:Fō-M ]e4BP M +|!7 IF~MJ8[Zоw+ K87q{$P"=uMܦo%B'K -Z^>;BTBJ/"Ce;:ICh2=Qݫ\GcCs`ɪkJWB#2rC=-M-%Bo7Z;;ۻ_+M v(L}s赀ͱ)Bg?M#ΡrFF>4BjmRϊl|:z;&ڹ--Mub/!7BWGO FCmw*kwFM#}}#+ӛ@m}P~M .ct*l`wE:Q͏99=<:=Т@D-MViV VJ#'FFVk#uX_Wpo!Вl P3>qfG@! QzJ#ɑzBPFhopOrWe+BC:UjMn]c@__/Wpշw`hdKUܚ’*ں:TJk**t*xk db e<2B)30 Xr 79:"h9ᾈP4HwaB(MF+h.wnT½u /ł=u rzq=X`.&2B>~YnAoCS SY* b k8)ԏ%^-d"XNitDbܻ& #mEIJaN#F%ٮSAy82GW 4IcCUs&@B=BחD/Sl,RB.#+IuwOPhh/fa>Is$\B&y(œXG(3B%N<sV.K"shP (lr:n|!. BhOɼ , q B(&2#E"N#eU!Ǎ0y¹>,V@硢q&ME$'V%ILthp  Wbbch9W6UQԏc㩓pMb rܹWQUUSBd.dTn~hϟԒkkh5M1*Z"74R7R5Ct  r@0dMemT^!Fښjd2*n?|\3ڵ}xF>CJ\jZV1LDZ܍:DB!,i:tS{FT{+Q;7,҃N0ؿcn¦a6jj4Ctn"#ErMw^`AHR]P.u۶n*&)¶Ҡ/H!tΫm heByڰ@[G!;/vou3#\+^fj:DkX`=AȕYC*ޱa);vmqr4C:k`nl@zu^K XY@#hsڥƺFo_ `Mi5KlMu5m3T4h.[7ZdkM@f 0?}G9o;;oZj.~0H}rGŴ?|6N׮^ ObY wn^x"4|{5+WXPWًO/ZE~M+8X[D*{x % ڱi d<o֕VƆ:Z! ޶i%`^۱vN(T;[֭l[hB9؀b^N(Zc۶|K[+i CiJD.Ǽ`/` )%owSahS?<3m%}0^%~_q* &ogwAݿw{vmٴuw=9=IMu`e<ۼum;vܶye+Woغ} %mq޶e }d2$V:9b]j6Kpb:ۙ@;F){;kh#c k<5iǴvg@>: g90h.<RUMM]>RS pd\1b~cei0~nLU R2W*9!֛,!"~EL*՘QQ#5y%2UYgnތa@߿ITȉ™oc*4S@K \)VaiUH Ωa+8CŨ6S΀OUq =: m:7/ɑ*UXdJ _PL4^Gu[>cR1Q|?#0'ׂ90X>jࣥڊu߸w7q1%|#L]1p߲ "mӶ2vO̭aD`"z;Wx5ʫϪ5.ׯ{]qqS5xyex '(9#7qKpQNxamkoKeeueqWǹki%5jRڦ\"*1xN5سi[}umi 1 驑1)U5\c̐ O{+˹Gũy aaq)%\P+rkTjxZLE;Y1=+)KNJ ZRWS[u)s\ߠw +psjx3/[C2<G'$䔔G&Ba\sb8pu}K>9ywwB=\ I/-)lM*V;vС>ӏtrN,, J ={% ?o᭎`@[9FxtxXc?}?i'.˹|sZdvi~NAUemHDw~5l";%)[wĿΎM=yΟ*sc*{/٦kru ((H/kj b)̭<W^Ld|7CqJ3 *jZ'>|P?B"_L$ZK"L.ۿ\HFQMGkinuwO;piolS61#y55×RDt8+8/껷:{?ʚޖܽ9><83T\$Oψ+*2ѳΆ૾>cݑ9q~u1$`O?ϼ_tp_@MtRJ.=oh̺Q^Xr\΄~7rsbl/_̾z%$oR2ɫ':{[ƛ"r\u'N|}:~7EbT& ܪ,|S&M=7:51(s~Vq/7:|nF(e2\\ͯ#rؒSSLy*(ɽ:rʎᱻ/©}/!9u'Je\neD.Ͼxbjy^N^zgJI䳧MIDO&_MHP[4+XQ8/H%H,~.@Hiѓpoω&nf'o߻sԬ@"{` Wb/2UaIENi̙#?Mȯmnx$L俺90<15xٙ[wazUPyyÈ~]jnL953djwhtbsɫm0 Uu}| 2@a7A'zt=~pɣ{#w&ۊsK-÷u4?lhr rgRW1_>7PZwzFugW-T蝛}Ó5w인`eWN/tWqS";LKwyѝ[w&j: jq4%vb2;664SRpjsݧF{kq'0#upWJN)U BmsŔ ~kbthOn֔'s2R8g/xĄqո\;/6jܡfy2F<*O ?~<_4dr̴݉A\{1m, r %(OѸ6H,j:_|UE|l!*aڳ9h}w95T\r#2/Y|SG>t'H~.x[iK t 1ǿuƾ[1g[ܴP̮bNTMɰʸ?%ǯ1OfySzܑxR2'zBK^-3/,7=v'hZx/١!ݳO+y\H-|06![ۿ5TT`+ +^ H8sXvlG}}7dkm}U] [zqy5??{|z="to teT>9ڛ7^UEFG>O:U -Hء#Eyf@fvUD'JE-sqgD:(OJSFXo~ :CG* H/A1ʝϺ~L AuM-䧆'8y.UPVa5sM3ZWI,UeW/bj_!I8"5rj9jdDd`p.W+.r*aeI`J 0 AFZQ (KT]uhQʈ+I/\I/Cb•VlS,%$f\iY`9^a%#~lYC\QJrǕ,DUWcxuչCCZSՇpqwYYȟ~.xR! ]sJ$w%9 LD5.`#- "QxbWVS[^'nJkrO7a5XUŭ XwẰ ?:nP@b56cl} dVPo*R5—p7tQ ate-AS܂5ڦVpK`5VU u !YTEJW wsjͯQߚ::ImvFuN3Ԇ9:.\`wH5|0XRU_W ⯭M;O? u5^ݯZ6:SY9G^}\אBG t~6eYjB9empGkb 6j+OS! Sp&:nnfzÊmM5[v +vO= _M~ⅶFXQlu5M}#}]#jn2'Pՠ_PPa,mmCUFigdldhd?&/Cc2  ňv*>672tX\glnan``h^ko'7ijiY8֝22wX%DbBcS5WЄ;};ں&uUMWZqV~ڻ@fSUCWGMEC@OWG[SKGĬtGE6m[lb|C_d[an}m]|%+BWWϰqs [ܘj`bjcokmeix NZo޽m]λ[V7awZiC`2UNcSKDž樽6v56?ow_fkae/CB{%֮ڰyqhӎwYPW~u|i -jZZ ۛ::,\u vlܴskumUvLͬ.[je;AY[um.750]b-Vlؾe՚\nW/]ODst[jV03FTM!˖8.upXrJm+)z+ׯYt*'Ge69o߾e 7ọV:-pZbohfanf`ӺŖЉaq_ڼy͊;vڻ&8U1^Xhohbk`ji`9,ZѰZ`g`M7x[l6TcB3744672MSJ_ǀкjz 8m߲y[9j몙:ZC#@14531r\`82k1t;Vmڰb򍫭-̘ U =}+mMLHaBU5]dcH] VF/Km͛-u5mW.Rk٘ZXiYPVg(k_QUW6\ kYm- 4T4L5od촟ru0Vם3ZwͲuN9٘4P Z*s9Y'%sAy Z/\bmf*KS8sK|( >٥k,`Ӷ_@[`_n1' 㺳4Q)qu-T:+]_ѷ44I}QwF{eƚ; =?*> qtӇ@DOMMIW~9%] kks=DeM q N:C ?)^$h=\lŵg4Ԡ᲎$d~ 4o&CX0cbnk Z~_0Oi?1tەٟ[8i`8$# ˞`n2J#ܚҿjjàӆ9OhmOGUϜ;USfJ^Ŭ`~Z&46qX*o4k1t~xoXzbM0`:w#_{;޶w9ib5.36ergkO-d:Ш6 m,ZnhmmKiuW`d"QidSVJ|5D%5%eHΤ(*+^&DTeJv ՂpDaD7hm9\xCDגo -CDK?H[Hc1o Rg$?]DFZ:.:{rDmv\TO?B3V?D/`"QӅѧ5H%Du->.mG8# x_#@t釫c D!?F+?fkN}?^x D?[K_̭ F  B`#D&3W7ʑΦ@TPMWKuu㵀Bє&hǘz,P}  @T% 6p/RHT5bE7AÄkU~Y68FU7KF_Ҍk 6k[[[o="Tei;76q_CRZ Djwߌp\auMp{G Xi04Qkgk-$g uX :55ՈFTՑ Cjp-}Q--[h/VO(>4]I4T#^%x/OBTRށ @:ZB HS}?BCR6OcCF1(#j5ÔrbXkKfC0DÉh]XB)&c7%g鶳FAI<wr],{䩜Z!ڔdYj-Pb˧`=Q;vvKH*@L$ӓb$ϗQ0sؼ)\(k&_T;).l2d(xFR%Ő ӗ TꤔjPg9Öhw\<>hdU(L}T (,VP6?vS 4)KûmNd3/rJf:eHR U{[3J$4bXOw'$44OTvp ^%B?5@ O4PTnkѠL|n׭uP,^Ҟb𹛭fp׮{Qٔi]\PKs^iºt"4u8Ԙa_eV9,OOJ+?zh:XF kܲ|]'N앦.H7V4tD}MP=W-旵qܩ3I]*`^lܺYyDq=RȾjVz!̆y|ζk $TPQst͑W_l7&ẖk{vlc݆C)Ҫ9|xm͡sZEܐakon*KRTs'O_R4Z|ך^Օm_CY74J`u/_|ΝSU7Tn5d/_!B޵=[xCWO9dV݁t.)bk4J>}WZs 6tCś#/u,9x[_UVnn~\u׾vUOɢUu͹U+N>Hq M+ дu{h $;z2K._tڵBmLݩʋw/H,GJ嗛gj:yf W/]8LXEHe~UZYslv\$µ+~RsqFvwo?scǤ +"ۿ̍%9,l޶2銫t /ܾn'iXx:2]Q$:~ƝlI9}erӉ-UƓ;lYQt6mau|B\*rc, {rk;,(ܳzD^ĦoxsJsbJ:Y|]E@ ,{W+ݾ?;~WU^b-;JsWo]L |\qzB!Xjۚi9'Sv'A:rk&tF>^{ef$ű˿ JJNI*[31v[OĤ_Hzgrԯq{pED|jN -9>&b㦥єˌN;rl;~Xuf%yi[bbB݇jٞ*i^Z"LJKؔEmINJ _lq* vQ!.W^g:"&;?%%'ޓ^Z ~js?x@m{+72oKd֮xܗ*!b߼%+ģYDq/hjcW񨩻ܬml߱cَ)>"fWU'N ☈*pWST!$f::;z06cqvtvv>~Hcf?1c֓O=9gʔyMno)GJ(ff3a 3͞>ˁy1WDb1nka3aμ>sO gᰕC}.b.CLĸ#pFSٹ &ڳ:tZHbdb;.xs 0k3J`"D,[xO'{z3m`7#"/% jck 03G|F d"Qy.o}y1'M8a%K \&AToj`g.6s=qbѢѪk#.z]lK>;ToI,m,"k~ޮ.n.f ǣ^'6*QgM-c{ȾJg+^W2dvF"3kk{;G{{;$"zmT;ނYx8:Y[Z )+*FffT WZԈpmcx%fa) O]lime`}pJTw##G7k^;] %Pa:X0cΧ>Ovic( k K`cl7se˸yW!ΣA$91S.DF0puszF5~9Zә:Fywr[!ۯbF03Q1wS]"#.RK~RHD$t zt1mNc˘ )tQq*HնQ<KccssS k*ފO2hgƠX c3;| ԯr#B[%103NMkqx(t|ۡI/>BkژO~juҒ]ǿYGh?c[k ZAk'uZo|YNlAkO)p8Z?^(hNAk;.>wWڮ8tgeS'Q[} Tm+ՇG΁j[;Gow8E׵T5/)n 2=ޖp>)ncE>Qs m>~h˿MQTyQfXw\bPZf*N10 ϘƊ06d cKG2Ɖ`pƅ`x1DSA7cSllj{n74tK'o_m"6ĝjC쉛0iqJW8W{iij%ΐwNFp[K3o8ApKā#(bnj&Cl_]M]]E>  ?{ҝn%P:Np{^m^҄9cL2k* ڮkP5sh{7;4pbX\ǝPY\F Az QnwBnC/qnc87Fw&&Ltw4` f@*qiL}nƌ?)IC,Qp z͊-_%R4rHvYr{q,bM+Yu5B29g1" JRpQ?ʃV]&QZqV$2)"wGl˪<|j*3ep06Ŗ(b*XVt 5b(RjV}&cK#lƏ ZrL/DmSpYk}퍷zUpĽ[m}n*T ed5wo) sv K wUVƍNWŹ n$Nͧ|*ȭ<~Z'Wvb4+ shuZ!Wf="N$\otWq5&:-X+Gl_#6>^qC:[ҢkJKSzKjSp=7QDVx_ZZVqKj:@<?ǪlpS]{ןh4.U[k=TRak]ގ6S4Di}.By"Ҥ ׮]:VVXU~b}KsGiA+7{ظ6qwRxh=UCz$3&*1w