itksnap/0000755000076500000240000000000011560342171011544 5ustar paulystaffitksnap/CMake/0000755000076500000240000000000011560342170012523 5ustar paulystaffitksnap/CMake/CustomBuildSettings.cmake0000644000076500000240000000211210735760673017513 0ustar paulystaffSITE_NAME(SNAP_BUILD_MACHINE) SET(INSTALL_LIBS lib/snap-${SNAP_VERSION_FULL}) IF(SNAP_BUILD_MACHINE STREQUAL "pauly-laptop" AND NOT WIN32) INSTALL( FILES /usr/lib/libstdc++.so.6.0.9 DESTINATION ${INSTALL_LIBS} COMPONENT RUNTIME RENAME libstdc++.so.6) INSTALL( FILES /lib/libgcc_s.so.1 DESTINATION ${INSTALL_LIBS} COMPONENT RUNTIME) INSTALL( FILES /usr/lib/libGLU.so.1.3.070001 DESTINATION ${INSTALL_LIBS} RENAME libGLU.so.1 COMPONENT RUNTIME) ENDIF(SNAP_BUILD_MACHINE STREQUAL "pauly-laptop" AND NOT WIN32) IF(SNAP_BUILD_MACHINE STREQUAL "mingus.uphs.upenn.edu" AND NOT WIN32) INSTALL( FILES /usr/lib/libstdc++.so.5.0.3 DESTINATION ${INSTALL_LIBS} COMPONENT RUNTIME RENAME libstdc++.so.5) INSTALL( FILES /lib/libgcc_s-3.2.2-20030225.so.1 DESTINATION ${INSTALL_LIBS} RENAME libgcc_s.so.1 COMPONENT RUNTIME) INSTALL( FILES /usr/X11R6/lib/libGLU.so.1.3 DESTINATION ${INSTALL_LIBS} RENAME libGLU.so.1 COMPONENT RUNTIME) ENDIF(SNAP_BUILD_MACHINE STREQUAL "mingus.uphs.upenn.edu" AND NOT WIN32) itksnap/CMake/CVS/0000755000076500000240000000000011560342170013156 5ustar paulystaffitksnap/CMake/CVS/Entries0000644000076500000240000000024111560342170014507 0ustar paulystaff/CustomBuildSettings.cmake/1.2/Sun Dec 30 18:21:47 2007// /find_fltk_13.cmake/1.6/Wed May 4 15:01:47 2011// /standalone.cmake/1.12/Sat Oct 23 10:26:41 2010// D itksnap/CMake/CVS/Repository0000644000076500000240000000001611560342170015255 0ustar paulystaffitksnap/CMake itksnap/CMake/CVS/Root0000644000076500000240000000010011560342170014013 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/CMake/find_fltk_13.cmake0000644000076500000240000001345311560265133016001 0ustar paulystaff# This script sets up FLTK 1.3 because FindFLTK does not work very well # The name of the FLTK library will vary depending on the system # Options are libfltk_forms.a fltkforms.lib and fltkformsd.lib IF(MSVC) SET(PREF "") IF(CMAKE_BUILD_TYPE MATCHES "Debug") SET(SUFF "d") ELSE(CMAKE_BUILD_TYPE MATCHES "Debug") SET(SUFF "") ENDIF(CMAKE_BUILD_TYPE MATCHES "Debug") ELSE(MSVC) SET(PREF "_") SET(SUFF "") ENDIF(MSVC) # What is the target name of the fltk library SET(BASETARG "fltk${SUFF}") # Search for the base library FIND_LIBRARY(FLTK_BASE_LIBRARY NAMES ${BASETARG} PATHS "/usr/lib" "/usr/local/lib") IF(FLTK_BASE_LIBRARY) MESSAGE(STATUS "FLTK base: ${FLTK_BASE_LIBRARY}") # Search for the other libraries GET_FILENAME_COMPONENT(FLTK_LIB_DIR ${FLTK_BASE_LIBRARY} PATH) MESSAGE(STATUS "FLTK lib dir: ${FLTK_LIB_DIR}") MESSAGE(STATUS "FLTK target: fltk${PREF}forms${SUFF}") FIND_LIBRARY(FLTK_FORMS_LIBRARY NAMES "fltk${PREF}forms${SUFF}" PATHS ${FLTK_LIB_DIR}) FIND_LIBRARY(FLTK_GL_LIBRARY NAMES "fltk${PREF}gl${SUFF}" PATHS ${FLTK_LIB_DIR}) FIND_LIBRARY(FLTK_IMAGES_LIBRARY NAMES "fltk${PREF}images${SUFF}" PATHS ${FLTK_LIB_DIR}) # Search for the fluid executable. It can be in a couple of places. # 1. In the bin directory, same level as lib # 2. In the fluid directiry, same level as lib (if built 'in-place') GET_FILENAME_COMPONENT(FLTK_ROOT_DIR ${FLTK_LIB_DIR} PATH) FIND_PROGRAM(FLTK_FLUID_EXECUTABLE NAMES "fluid${SUFF}" PATHS "${FLTK_ROOT_DIR}/bin" "${FLTK_ROOT_DIR}/fluid") # Search for the FLTK include directory. Again, two places to look # 1. In the include directory # 2. In the root directory FIND_PATH(FLTK_INCLUDE_DIR NAMES "FL/Fl.H" PATHS "${FLTK_ROOT_DIR}/include" "${FLTK_ROOT_DIR}") MESSAGE(STATUS "FLTK include directory: ${FLTK_INCLUDE_DIR}") # For now, we let the user decide about the additional image libraries # TODO: figure this out from fltk-config file OPTION(SNAP_USE_FLTK_PNG "Should we link with the FLTK png library?" OFF) OPTION(SNAP_USE_FLTK_JPEG "Should we link with the FLTK jpeg library?" OFF) OPTION(SNAP_USE_FLTK_ZLIB "Should we link with the FLTK zlib library?" OFF) # Look for these libs if specified IF(SNAP_USE_FLTK_PNG) FIND_LIBRARY(FLTK_PNG_LIBRARY NAMES "fltk${PREF}png${SUFF}" PATHS ${FLTK_LIB_DIR}) SET(FLTK_IMAGES_LIBS ${FLTK_IMAGES_LIBS} ${FLTK_PNG_LIBRARY}) IF(FLTK_PNG_LIBRARY) SET(FLTK_PNG_LIBRARY_OK ON) ELSE(FLTK_PNG_LIBRARY) SET(FLTK_PNG_LIBRARY_OK OFF) ENDIF(FLTK_PNG_LIBRARY) ELSE(SNAP_USE_FLTK_PNG) FIND_PACKAGE(PNG REQUIRED) IF(PNG_FOUND) SET(FLTK_PNG_LIBRARY_OK ON) SET(FLTK_IMAGES_LIBS ${FLTK_IMAGES_LIBS} ${PNG_LIBRARY}) ELSE(PNG_FOUND) SET(FLTK_PNG_LIBRARY_OK OFF) ENDIF(PNG_FOUND) ENDIF(SNAP_USE_FLTK_PNG) IF(SNAP_USE_FLTK_JPEG) FIND_LIBRARY(FLTK_JPEG_LIBRARY NAMES "fltk${PREF}jpeg${SUFF}" PATHS ${FLTK_LIB_DIR}) SET(FLTK_IMAGES_LIBS ${FLTK_IMAGES_LIBS} ${FLTK_JPEG_LIBRARY}) IF(FLTK_JPEG_LIBRARY) SET(FLTK_JPEG_LIBRARY_OK ON) ELSE(FLTK_JPEG_LIBRARY) SET(FLTK_JPEG_LIBRARY_OK OFF) ENDIF(FLTK_JPEG_LIBRARY) ELSE(SNAP_USE_FLTK_JPEG) FIND_PACKAGE(JPEG REQUIRED) IF(JPEG_FOUND) SET(FLTK_JPEG_LIBRARY_OK ON) SET(FLTK_IMAGES_LIBS ${FLTK_IMAGES_LIBS} ${JPEG_LIBRARY}) ELSE(JPEG_FOUND) SET(FLTK_JPEG_LIBRARY_OK OFF) ENDIF(JPEG_FOUND) ENDIF(SNAP_USE_FLTK_JPEG) IF(SNAP_USE_FLTK_ZLIB) FIND_LIBRARY(FLTK_ZLIB_LIBRARY NAMES "fltk${PREF}z${SUFF}" "zlib${SUFF}" PATHS ${FLTK_LIB_DIR}) SET(FLTK_IMAGES_LIBS ${FLTK_IMAGES_LIBS} ${FLTK_ZLIB_LIBRARY}) IF(FLTK_ZLIB_LIBRARY) SET(FLTK_ZLIB_LIBRARY_OK ON) ELSE(FLTK_ZLIB_LIBRARY) SET(FLTK_ZLIB_LIBRARY_OK OFF) ENDIF(FLTK_ZLIB_LIBRARY) ELSE(SNAP_USE_FLTK_ZLIB) FIND_PACKAGE(ZLIB REQUIRED) IF(ZLIB_FOUND) SET(FLTK_ZLIB_LIBRARY_OK ON) SET(FLTK_IMAGES_LIBS ${FLTK_IMAGES_LIBS} ${ZLIB_LIBRARY}) ELSE(ZLIB_FOUND) SET(FLTK_ZLIB_LIBRARY_OK OFF) ENDIF(ZLIB_FOUND) ENDIF(SNAP_USE_FLTK_ZLIB) ENDIF(FLTK_BASE_LIBRARY) # Check if all components have been found IF(FLTK_BASE_LIBRARY AND FLTK_FORMS_LIBRARY AND FLTK_GL_LIBRARY AND FLTK_IMAGES_LIBRARY AND FLTK_INCLUDE_DIR AND FLTK_FLUID_EXECUTABLE AND FLTK_PNG_LIBRARY_OK AND FLTK_ZLIB_LIBRARY_OK AND FLTK_JPEG_LIBRARY_OK) SET(FLTK_FOUND ON) ELSE(FLTK_BASE_LIBRARY AND FLTK_FORMS_LIBRARY AND FLTK_GL_LIBRARY AND FLTK_IMAGES_LIBRARY AND FLTK_INCLUDE_DIR AND FLTK_FLUID_EXECUTABLE AND FLTK_PNG_LIBRARY_OK AND FLTK_ZLIB_LIBRARY_OK AND FLTK_JPEG_LIBRARY_OK) SET(FLTK_FOUND OFF) ENDIF(FLTK_BASE_LIBRARY AND FLTK_FORMS_LIBRARY AND FLTK_GL_LIBRARY AND FLTK_IMAGES_LIBRARY AND FLTK_INCLUDE_DIR AND FLTK_FLUID_EXECUTABLE AND FLTK_PNG_LIBRARY_OK AND FLTK_ZLIB_LIBRARY_OK AND FLTK_JPEG_LIBRARY_OK) # Taken from findfltk.cmake # Platform dependent libraries required by FLTK IF(WIN32) IF(NOT CYGWIN) IF(BORLAND) SET( FLTK_PLATFORM_DEPENDENT_LIBS import32 ) ELSE(BORLAND) SET( FLTK_PLATFORM_DEPENDENT_LIBS wsock32 comctl32 ) ENDIF(BORLAND) ENDIF(NOT CYGWIN) ENDIF(WIN32) IF(UNIX) INCLUDE(${CMAKE_ROOT}/Modules/FindX11.cmake) FIND_LIBRARY(FLTK_MATH_LIBRARY m) SET( FLTK_PLATFORM_DEPENDENT_LIBS ${X11_LIBRARIES} ${X11_Xft_LIB} ${X11_Xinerama_LIB} ${FLTK_MATH_LIBRARY}) ENDIF(UNIX) IF(APPLE) SET( FLTK_PLATFORM_DEPENDENT_LIBS "-framework Carbon -framework Cocoa -framework ApplicationServices -framework AudioToolbox -lz") ENDIF(APPLE) IF(CYGWIN) FIND_LIBRARY(FLTK_MATH_LIBRARY m) SET( FLTK_PLATFORM_DEPENDENT_LIBS ole32 uuid comctl32 wsock32 supc++ ${FLTK_MATH_LIBRARY} -lgdi32) ENDIF(CYGWIN) # Set the FLTK libraries SET(FLTK_LIBRARIES ${FLTK_BASE_LIBRARY} ${FLTK_FORMS_LIBRARY} ${FLTK_IMAGES_LIBRARY} ${FLTK_GL_LIBRARY} ${FLTK_IMAGES_LIBS} ${FLTK_PLATFORM_DEPENDENT_LIBS}) SET(FLTK_INCLUDE_PATH ${FLTK_INCLUDE_DIR}) itksnap/CMake/standalone.cmake0000644000076500000240000000360211460534141015656 0ustar paulystaff############################################# # REQUIRE ITK 3.20 OR LATER # ############################################# FIND_PACKAGE(ITK 3.20 REQUIRED) INCLUDE(${ITK_USE_FILE}) ############################################# # REQUIRE VTK # ############################################# FIND_PACKAGE(VTK 5.6 REQUIRED) INCLUDE (${VTK_USE_FILE}) ############################################# # REQUIRE FLTK # ############################################# INCLUDE(${SNAP_SOURCE_DIR}/CMake/find_fltk_13.cmake) # Allow FLTK 1.1 for older systems. This is an optional flag OPTION(SNAP_USE_FLTK_1_1 OFF "Build with older FLTK 1.1") MARK_AS_ADVANCED(SNAP_USE_FLTK_1_1) IF(SNAP_USE_FLTK_1_1) SUBDIRS(Utilities/FLTK/Fl_Table) SUBDIRS(Utilities/FLTK/Fl_Native_File_Chooser) SET(FLTK_LIBRARIES fltk_table fltk_native_file_chooser ${FLTK_LIBRARIES}) SET(FLTK_INCLUDE_PATH ${FLTK_INCLUDE_PATH} ${SNAP_SOURCE_DIR}/Utilities/FLTK/Fl_Table ${SNAP_SOURCE_DIR}/Utilities/FLTK/Fl_Native_File_Chooser) ENDIF(SNAP_USE_FLTK_1_1) # Look for OpenGL. FIND_PACKAGE(OpenGL REQUIRED) # The fluid-generated fltk sources have many warnings. This macro # will disable warnings for the generated files on some compilers. MACRO(ITK_DISABLE_FLTK_GENERATED_WARNINGS files) IF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 1.6) IF(CMAKE_COMPILER_IS_GNUCXX) FOREACH(f ${files}) STRING(REGEX REPLACE "\\.fl$" ".cxx" SRC "${f}") STRING(REGEX REPLACE ".*/([^/]*)$" "\\1" SRC "${SRC}") SET_SOURCE_FILES_PROPERTIES(${SRC} PROPERTIES COMPILE_FLAGS -w) ENDFOREACH(f) ENDIF(CMAKE_COMPILER_IS_GNUCXX) ENDIF(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 1.6) ENDMACRO(ITK_DISABLE_FLTK_GENERATED_WARNINGS) # Link libraries from the parent CMAKE file LINK_LIBRARIES(ITKAlgorithms ITKCommon ITKBasicFilters) itksnap/CMakeLists.txt0000644000076500000240000006466311560060540014320 0ustar paulystaff############################################# # CMAKE PRELIMINARIES # ############################################# CMAKE_MINIMUM_REQUIRED(VERSION 2.6.2) IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0003 NEW) ENDIF(COMMAND CMAKE_POLICY) ############################################# # PROJECT: SNAP # ############################################# PROJECT(SNAP) ############################################# # 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. # # Important: # - The qualifier is just a descriptor. No two version should have the same # major/minor/patch and different qualifiers. So it's completely wrong to # release version 1.7.3-beta and then version 1.7.3. The right way to do it # is to have 1.7.3-beta followed by 1.7.5-rc1 followed by 1.8.0 and so on SET(SNAP_VERSION_MAJOR 2) SET(SNAP_VERSION_MINOR 2) SET(SNAP_VERSION_PATCH 0) SET(SNAP_VERSION_QUALIFIER "") SET(SNAP_VERSION_FULL "${SNAP_VERSION_MAJOR}.${SNAP_VERSION_MINOR}.${SNAP_VERSION_PATCH}${SNAP_VERSION_QUALIFIER}") SET(SNAP_VERSION_RELEASE_DATE "20110504") SET(SNAP_VERSION_LAST_COMPATIBLE_RELEASE_DATE "20090731") SET(SNAP_VERSION_RELEASE_DATE_FORMATTED "May 4, 2011") # Shamelessly stolen from ParaView 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) STRING(REPLACE ";" "-" ARCHBIT ${CMAKE_OSX_ARCHITECTURES}) SET(CPACK_SYSTEM_NAME "MacOS-${ARCHBIT}") ENDIF(CMAKE_SYSTEM_NAME MATCHES Darwin) IF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}") ENDIF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) ######################################################### # FIND PACKAGES IF BUILDING OUTSIDE INSIGHTAPPLICATIONS # ######################################################### 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 ) ############################################# # 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) # 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/CommandLineArgumentParser.cxx Common/IRISException.cxx Common/Registry.cxx Common/SystemInterface.cxx Common/ITKExtras/itkVoxBoCUBImageIO.cxx Common/ITKExtras/itkVoxBoCUBImageIOFactory.cxx Logic/Common/ColorMap.cxx Logic/Common/ColorLabelTable.cxx Logic/Common/ImageCoordinateGeometry.cxx Logic/Common/ImageCoordinateTransform.cxx Logic/Common/SegmentationStatistics.cxx Logic/Common/SNAPRegistryIO.cxx Logic/Common/SNAPSegmentationROISettings.cxx Logic/Framework/GenericImageData.cxx Logic/Framework/GlobalState.cxx Logic/Framework/IRISApplication.cxx Logic/Framework/IRISImageData.cxx Logic/Framework/SNAPImageData.cxx Logic/Framework/UndoDataManager_LabelType.cxx Logic/ImageWrapper/GreyImageWrapper.cxx Logic/ImageWrapper/GuidedNativeImageIO.cxx Logic/ImageWrapper/LabelImageWrapper.cxx Logic/ImageWrapper/LevelSetImageWrapper.cxx Logic/ImageWrapper/RGBImageWrapper.cxx Logic/ImageWrapper/SpeedColorMap.cxx Logic/ImageWrapper/SpeedImageWrapper.cxx Logic/LevelSet/SnakeParameters.cxx Logic/Mesh/AllPurposeProgressAccumulator.cxx Logic/Mesh/GuidedMeshIO.cxx Logic/Mesh/IRISMeshPipeline.cxx Logic/Mesh/LevelSetMeshPipeline.cxx Logic/Mesh/MeshObject.cxx Logic/Mesh/MeshOptions.cxx Logic/Mesh/VTKMeshPipeline.cxx Logic/Preprocessing/EdgePreprocessingSettings.cxx Logic/Preprocessing/ThresholdSettings.cxx Logic/Slicing/IntensityCurveVTK.cxx ) # The headers for the Logic code SET(LOGIC_HEADERS Common/CommandLineArgumentParser.h Common/Credits.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/Registry.h Common/SNAPBorlandDummyTypes.h Common/SNAPCommon.h Common/SNAPOpenGL.h Common/SystemInterface.h Logic/Common/ColorLabel.h Logic/Common/ColorLabelTable.h Logic/Common/ColorMap.h Logic/Common/ImageCoordinateGeometry.h Logic/Common/ImageCoordinateTransform.h Logic/Common/SegmentationStatistics.h Logic/Common/ImageRayIntersectionFinder.h Logic/Common/ImageRayIntersectionFinder.txx Logic/Common/SNAPRegistryIO.h Logic/Common/SNAPSegmentationROISettings.h Logic/Framework/GenericImageData.h Logic/Framework/GlobalState.h Logic/Framework/IRISApplication.h Logic/Framework/IRISImageData.h Logic/Framework/SNAPImageData.h Logic/Framework/UndoDataManager.h Logic/Framework/UndoDataManager.txx Logic/ImageWrapper/GreyImageWrapper.h Logic/ImageWrapper/ImageIORoutines.h Logic/ImageWrapper/ImageWrapper.h Logic/ImageWrapper/ImageWrapper.txx Logic/ImageWrapper/LabelImageWrapper.h Logic/ImageWrapper/LabelToRGBAFilter.h Logic/ImageWrapper/LevelSetImageWrapper.h Logic/ImageWrapper/RGBImageWrapper.h Logic/ImageWrapper/ScalarImageWrapper.h Logic/ImageWrapper/ScalarImageWrapper.txx Logic/ImageWrapper/SpeedColorMap.h Logic/ImageWrapper/SpeedImageWrapper.h Logic/ImageWrapper/VectorImageWrapper.h Logic/ImageWrapper/VectorImageWrapper.txx Logic/LevelSet/LevelSetExtensionFilter.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/IRISMeshPipeline.h Logic/Mesh/LevelSetMeshPipeline.h Logic/Mesh/MeshObject.h Logic/Mesh/MeshOptions.h Logic/Mesh/VTKMeshPipeline.h Logic/Preprocessing/EdgePreprocessingImageFilter.h Logic/Preprocessing/EdgePreprocessingImageFilter.txx Logic/Preprocessing/EdgePreprocessingSettings.h Logic/Preprocessing/SmoothBinaryThresholdImageFilter.h Logic/Preprocessing/SmoothBinaryThresholdImageFilter.txx Logic/Preprocessing/ThresholdSettings.h Logic/Slicing/IRISSlicer.h Logic/Slicing/IRISSlicer.txx Logic/Slicing/IntensityCurveInterface.h Logic/Slicing/IntensityCurveVTK.h Logic/Slicing/UnaryFunctorCache.h Logic/Slicing/UnaryFunctorCache.txx ) # These files contain the user interface source code SET(UI_CXX UserInterface/BasicComponents/ColorMapBox.cxx UserInterface/BasicComponents/ColorMapWidget.cxx UserInterface/BasicComponents/FLTKCanvas.cxx UserInterface/BasicComponents/FunctionPlot2DBox.cxx UserInterface/BasicComponents/FunctionPlot2D.cxx UserInterface/BasicComponents/IntensityCurveBox.cxx UserInterface/BasicComponents/InteractionModeClient.cxx UserInterface/BasicComponents/MetaDataTable.cxx UserInterface/BasicComponents/RecursiveInteractionMode.cxx UserInterface/BasicComponents/SnakeParametersPreviewBox.cxx UserInterface/BasicComponents/SnakeParametersPreviewPipeline.cxx UserInterface/BasicComponents/StatisticsTable.cxx UserInterface/Common/SNAPAppearanceSettings.cxx UserInterface/ImageIOWizard/ImageIOWizardLogic.cxx UserInterface/ImageIOWizard/RestrictedImageIOWizardLogic.cxx UserInterface/MainComponents/AppearanceDialogUILogic.cxx UserInterface/MainComponents/HelpViewerLogic.cxx UserInterface/MainComponents/LabelEditorUILogic.cxx UserInterface/MainComponents/LayerInspectorUILogic.cxx UserInterface/MainComponents/PreprocessingUILogic.cxx UserInterface/MainComponents/ReorientImageUILogic.cxx UserInterface/MainComponents/ResizeRegionDialogLogic.cxx UserInterface/MainComponents/RestoreSettingsDialogLogic.cxx UserInterface/MainComponents/SimpleFileDialogLogic.cxx UserInterface/MainComponents/SnakeParametersUILogic.cxx UserInterface/MainComponents/UserInterfaceLogic.cxx UserInterface/MeshIOWizard/MeshIOWizardUILogic.cxx UserInterface/SliceWindow/AnnotationInteractionMode.cxx UserInterface/SliceWindow/BubblesInteractionMode.cxx UserInterface/SliceWindow/CrosshairsInteractionMode.cxx UserInterface/SliceWindow/GenericSliceWindow.cxx UserInterface/SliceWindow/IRISSliceWindow.cxx UserInterface/SliceWindow/GLToPNG.cxx UserInterface/SliceWindow/OpenGLSliceTexture.cxx UserInterface/SliceWindow/PaintbrushInteractionMode.cxx UserInterface/SliceWindow/PolygonDrawing.cxx UserInterface/SliceWindow/PolygonInteractionMode.cxx UserInterface/SliceWindow/PolygonScanConvert.cxx UserInterface/SliceWindow/PopupButtonInteractionMode.cxx UserInterface/SliceWindow/RegionInteractionMode.cxx UserInterface/SliceWindow/SNAPSliceWindow.cxx UserInterface/SliceWindow/SliceWindowCoordinator.cxx UserInterface/SliceWindow/ThumbnailInteractionMode.cxx UserInterface/Window3D/Trackball.cxx UserInterface/Window3D/Window3D.cxx ) # The header files for the UI project SET(UI_HEADERS UserInterface/BasicComponents/ColorMapBox.h UserInterface/BasicComponents/ColorMapWidget.h UserInterface/BasicComponents/FLTKCanvas.h UserInterface/BasicComponents/FLTKEvent.h UserInterface/BasicComponents/FLTKWidgetActivationManager.h UserInterface/BasicComponents/FunctionPlot2D.h UserInterface/BasicComponents/FunctionPlot2DBox.h UserInterface/BasicComponents/IntensityCurveBox.h UserInterface/BasicComponents/InteractionMode.h UserInterface/BasicComponents/InteractionModeClient.h UserInterface/BasicComponents/MetaDataTable.h UserInterface/BasicComponents/RecursiveInteractionMode.h UserInterface/BasicComponents/SnakeParametersPreviewBox.h UserInterface/BasicComponents/SnakeParametersPreviewPipeline.h UserInterface/BasicComponents/StatisticsTable.h UserInterface/Common/SNAPAppearanceSettings.h UserInterface/Common/SNAPCommonUI.h UserInterface/ImageIOWizard/ImageIOWizardBase.h UserInterface/ImageIOWizard/ImageIOWizardLogic.h UserInterface/ImageIOWizard/RestrictedImageIOWizardLogic.h UserInterface/MainComponents/AppearanceDialogUIBase.h UserInterface/MainComponents/AppearanceDialogUILogic.h UserInterface/MainComponents/HelpViewerBase.h UserInterface/MainComponents/HelpViewerLogic.h UserInterface/MainComponents/LabelEditorUIBase.h UserInterface/MainComponents/LabelEditorUILogic.h UserInterface/MainComponents/LayerInspectorUIBase.h UserInterface/MainComponents/LayerInspectorUILogic.h UserInterface/MainComponents/PreprocessingUIBase.h UserInterface/MainComponents/PreprocessingUILogic.h UserInterface/MainComponents/ReorientImageUIBase.h UserInterface/MainComponents/ReorientImageUILogic.h UserInterface/MainComponents/ResizeRegionDialogBase.h UserInterface/MainComponents/ResizeRegionDialogLogic.h UserInterface/MainComponents/RestoreSettingsDialogBase.h UserInterface/MainComponents/RestoreSettingsDialogLogic.h UserInterface/MainComponents/SimpleFileDialogBase.h UserInterface/MainComponents/SimpleFileDialogLogic.h UserInterface/MainComponents/SnakeParametersUIBase.h UserInterface/MainComponents/SnakeParametersUILogic.h UserInterface/MainComponents/UserInterfaceBase.h UserInterface/MainComponents/UserInterfaceLogic.h UserInterface/MeshIOWizard/MeshExportSettings.h UserInterface/MeshIOWizard/MeshIOWizardUIBase.h UserInterface/MeshIOWizard/MeshIOWizardUILogic.h UserInterface/SliceWindow/AnnotationInteractionMode.h UserInterface/SliceWindow/BubblesInteractionMode.h UserInterface/SliceWindow/CrosshairsInteractionMode.h UserInterface/SliceWindow/GLToPNG.h UserInterface/SliceWindow/GenericSliceWindow.h UserInterface/SliceWindow/IRISSliceWindow.h UserInterface/SliceWindow/OpenGLSliceTexture.h UserInterface/SliceWindow/PaintbrushInteractionMode.h UserInterface/SliceWindow/PolygonDrawing.h UserInterface/SliceWindow/PolygonInteractionMode.h UserInterface/SliceWindow/PolygonScanConvert.h UserInterface/SliceWindow/PopupButtonInteractionMode.h UserInterface/SliceWindow/RegionInteractionMode.h UserInterface/SliceWindow/SNAPSliceWindow.h UserInterface/SliceWindow/SliceWindowCoordinator.h UserInterface/SliceWindow/ThumbnailInteractionMode.h UserInterface/Window3D/Trackball.h UserInterface/Window3D/Window3D.h ) # The source code for SNAP testing project SET(TESTING_CXX Testing/TestMain.cxx Testing/SNAPTestDriver.cxx ) # The source code for the tutorial test SET(TESTING_TUTORIAL_CXX Testing/TutorialTest.cxx ) # The headers for the testing code SET(TESTING_HEADERS Testing/SNAPTestDriver.h Testing/TestBase.h Testing/TestCompareLevelSets.h Testing/TestImageWrapper.h ) # The FL files for SNAP SET(APPLICATION_FLUIDS UserInterface/ImageIOWizard/ImageIOWizard.fl UserInterface/MeshIOWizard/MeshIOWizardUI.fl UserInterface/MainComponents/AppearanceDialogUI.fl UserInterface/MainComponents/HelpViewer.fl UserInterface/MainComponents/LabelEditorUI.fl UserInterface/MainComponents/LayerInspectorUI.fl UserInterface/MainComponents/PreprocessingUI.fl UserInterface/MainComponents/ReorientImageUI.fl UserInterface/MainComponents/ResizeRegionDialog.fl UserInterface/MainComponents/RestoreSettingsDialog.fl UserInterface/MainComponents/SimpleFileDialog.fl UserInterface/MainComponents/SnakeParametersUI.fl UserInterface/MainComponents/UserInterface.fl ) ############################################# # LIBRARIES AND EXTERNAL CODE # ############################################# # 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 ${SNAP_BINARY_DIR}/UserInterface/ImageIOWizard ${SNAP_BINARY_DIR}/UserInterface/MainComponents ${FLTK_INCLUDE_PATH} ${OPENGL_INCLUDE_PATH} ) 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/Slicing ${SNAP_SOURCE_DIR}/Testing ${SNAP_SOURCE_DIR}/UserInterface/BasicComponents ${SNAP_SOURCE_DIR}/UserInterface/Common ${SNAP_SOURCE_DIR}/UserInterface/ImageIOWizard ${SNAP_SOURCE_DIR}/UserInterface/MeshIOWizard ${SNAP_SOURCE_DIR}/UserInterface/MainComponents ${SNAP_SOURCE_DIR}/UserInterface/MainComponents/Artwork ${SNAP_SOURCE_DIR}/UserInterface/SliceWindow ${SNAP_SOURCE_DIR}/UserInterface/Window3D ${SNAP_BINARY_DIR}/UserInterface/ImageIOWizard ${SNAP_BINARY_DIR}/UserInterface/MainComponents ${FLTK_INCLUDE_PATH} ${OPENGL_INCLUDE_PATH} ) ENDIF( CMAKE_GENERATOR MATCHES "Visual Studio 6" ) # Get rid of this ridiculous warning in VS8 IF( CMAKE_GENERATOR MATCHES "Visual Studio 8" OR CMAKE_GENERATOR MATCHES "Visual Studio 9") ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE) ENDIF( CMAKE_GENERATOR MATCHES "Visual Studio 8" OR CMAKE_GENERATOR MATCHES "Visual Studio 9") # ---------------------------------------------------------------- # Define External Libraries # ---------------------------------------------------------------- # ITK Libraries SET(SNAP_ITK_LIBS ITKIO ) # Core VTK libraries SET(SNAP_VTK_CORE_LIBS vtkCommon vtkRendering vtkFiltering vtkGraphics vtkImaging vtkIO ) # VTK Libraries with possible inclusion of patented code IF(VTK_USE_PATENTED) SET(SNAP_VTK_LIBS vtkPatented ${SNAP_VTK_CORE_LIBS}) ELSE(VTK_USE_PATENTED) SET(SNAP_VTK_LIBS ${SNAP_VTK_CORE_LIBS}) ENDIF(VTK_USE_PATENTED) # FLTK Related libraries SET(SNAP_FLTK_LIBS ${FLTK_LIBRARIES}) # 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_FLTK_LIBS} ${SNAP_ITK_LIBS} ${SNAP_VTK_LIBS} ${SNAP_SYSTEM_LIBS}) # ***************************************************** # SNAP Logic and UI Libraries # ***************************************************** # Wrap the .fl files FLTK_WRAP_UI(itksnapui ${APPLICATION_FLUIDS}) # The SNAP logic library ADD_LIBRARY(itksnaplogic ${LOGIC_CXX} ${LOGIC_HEADERS}) # The user interface code library ADD_LIBRARY(itksnapui ${UI_CXX} ${UI_HEADERS} ${itksnapui_FLTK_UI_SRCS}) # 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 itksnaplogic ${SNAP_EXTERNAL_LIBS}) # Designate the SNAP internal libraries SET(SNAP_INTERNAL_LIBS itksnapui itksnaplogic) # ***************************************************** # Define SNAP Executables # ***************************************************** SET(SNAP_EXE InsightSNAP) # Disable FLTK warnings #ITK_DISABLE_FLTK_GENERATED_WARNINGS("${APPLICATION_FLUIDS}") # Define the main SNAP executable ADD_EXECUTABLE(${SNAP_EXE} WIN32 UserInterface/SNAPMain.cxx) TARGET_LINK_LIBRARIES(${SNAP_EXE} ${SNAP_INTERNAL_LIBS} ${SNAP_EXTERNAL_LIBS} ) # Define the testing EXE ADD_EXECUTABLE(snaptest ${TESTING_CXX}) TARGET_LINK_LIBRARIES(snaptest ${SNAP_INTERNAL_LIBS} ${SNAP_EXTERNAL_LIBS}) # ---------------------------------------------------------------- # Miscelaneous tasks (not related to link and compilation) # ---------------------------------------------------------------- # All program files - use recursive globbing FILE(GLOB_RECURSE PROGRAM_DATA_FILES ProgramData "*.txt" "*.html" "*.gif" "*.png" "*.img.gz" "*.hdr") # Copy documentation from the source tree to the build tree FOREACH(DATAFILE ${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 with CPack # ######################################################### INCLUDE(CMake/CustomBuildSettings.cmake) # Generate forward shared executable SUBDIRS(Utilities/Forwarding) # Install the SNAP executable in the appropriate place. # Windows (Microsoft Visual Studio) IF(WIN32 AND NOT UNIX) INSTALL(TARGETS ${SNAP_EXE} RUNTIME DESTINATION bin) SET(SNAP_DATA_INSTALL_DIR ".") ENDIF(WIN32 AND NOT UNIX) # Apple IF(APPLE) # Copy executable into the bundle SET(SNAP_MAIN_INSTALL_DIR ITK-SNAP.app/Contents/MacOS) SET(SNAP_DATA_INSTALL_DIR ${SNAP_MAIN_INSTALL_DIR}) INSTALL(TARGETS ${SNAP_EXE} RUNTIME DESTINATION ${SNAP_MAIN_INSTALL_DIR}) # Configure the XML file CONFIGURE_FILE( ${SNAP_SOURCE_DIR}/Utilities/MacOS/BundleResources/Info.plist ${SNAP_BINARY_DIR}/Utilities/MacOS/BundleResources/Info.plist) INSTALL(FILES ${SNAP_BINARY_DIR}/Utilities/MacOS/BundleResources/Info.plist DESTINATION ITK-SNAP.app/Contents) INSTALL(FILES ${SNAP_SOURCE_DIR}/Utilities/MacOS/BundleResources/itksnap.icns DESTINATION ITK-SNAP.app/Contents/Resources) ENDIF(APPLE) # Other UNIX IF(UNIX AND NOT APPLE) SET(SNAP_MAIN_INSTALL_DIR lib/snap-${SNAP_VERSION_FULL}) SET(SNAP_DATA_INSTALL_DIR ${SNAP_MAIN_INSTALL_DIR}) INSTALL(TARGETS ${SNAP_EXE} RUNTIME DESTINATION ${SNAP_MAIN_INSTALL_DIR}) ENDIF(UNIX AND NOT APPLE) # Install the Program Data files INSTALL(DIRECTORY ${SNAP_BINARY_DIR}/ProgramData DESTINATION ${SNAP_DATA_INSTALL_DIR}) # On Win32, we must include the redistributable IF(MSVC80 OR MSVC90) 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}\\\" /q:a'") ENDIF(VCREDIST_EXE) ENDIF(MSVC80 OR MSVC90) # Allow package generation SET(CPACK_PACKAGE_NAME "itksnap") SET(CPACK_PACKAGE_CONTACT "Paul A. Yushkevich") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ITK-SNAP 3D Image Segmentation Tool") SET(CPACK_PACKAGE_VENDOR "itksnap.org") SET(CPACK_PACKAGE_VERSION_MAJOR "${SNAP_VERSION_MAJOR}") SET(CPACK_PACKAGE_VERSION_MINOR "${SNAP_VERSION_MINOR}") SET(CPACK_PACKAGE_VERSION_PATCH "${SNAP_VERSION_PATCH}") SET(CPACK_PACKAGE_INSTALL_DIRECTORY "itksnap-${SNAP_VERSION_FULL}") # Show GPL license SET(CPACK_RESOURCE_FILE_LICENSE "${SNAP_SOURCE_DIR}/COPYING") IF(WIN32 AND NOT UNIX) # There is a bug in NSI that does not handle full unix paths properly. Make # sure there is at least one set of four (4) backlasshes. SET(CPACK_GENERATOR "NSIS") SET(CPACK_NSIS_INSTALLED_ICON_NAME "InsightSNAP.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) # 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 InsightSNAP "ITK-SNAP") ELSE(WIN32 AND NOT UNIX) # Set the generator to either STGZ or Apple IF(NOT APPLE) SET(CPACK_GENERATOR "TGZ") ELSE(NOT APPLE) SET(CPACK_GENERATOR "ZIP") ENDIF(NOT APPLE) # Executable is the forward sharing exe SET(CPACK_PACKAGE_EXECUTABLES "itksnap" "ITK-SNAP") ENDIF(WIN32 AND NOT UNIX) # Figure out the extension of the binary MESSAGE(STATUS "Generator: ${CPACK_GENERATOR}") IF(CPACK_GENERATOR STREQUAL "NSIS") SET(CPACK_EXTENSION "exe") ELSEIF(CPACK_GENERATOR STREQUAL "ZIP") SET(CPACK_EXTENSION "zip") ELSEIF(CPACK_GENERATOR STREQUAL "TGZ") SET(CPACK_EXTENSION "tar.gz") ENDIF(CPACK_GENERATOR STREQUAL "NSIS") # The filename of the installable package SET(CPACK_TARGET_FILENAME ${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}.${CPACK_EXTENSION}) SET(CPACK_TARGET ${SNAP_BINARY_DIR}/${CPACK_TARGET_FILENAME}) # Call CPACK INCLUDE(CPack) INCLUDE(CTest) ######################################################### # Automatic Uploading of Nightly Packages # ######################################################### 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) SET(NIGHTLY_TARGET "itksnap-nightly-${CPACK_SYSTEM_NAME}.${CPACK_EXTENSION}") SET(SCP_ROOT "frs.sourceforge.net:/home/frs/project/i/it/itk-snap/itk-snap") ADD_CUSTOM_TARGET(upload_nightly VERBATIM COMMAND "${SCP_PROGRAM}" ${SCP_ARGUMENTS} ${CPACK_TARGET_FILENAME} ${SCP_USERNAME},itk-snap@${SCP_ROOT}/Nightly/${NIGHTLY_TARGET} DEPENDS ${CPACK_TARGET} WORKING_DIRECTORY ${SNAP_BINARY_DIR} COMMENT "Uploading package ${CPACK_TARGET} to SourceForge.net as ${NIGHTLY_TARGET}") ADD_CUSTOM_TARGET(upload_experimental VERBATIM COMMAND "${SCP_PROGRAM}" ${SCP_ARGUMENTS} ${CPACK_TARGET_FILENAME} ${SCP_USERNAME},itk-snap@${SCP_ROOT}/Experimental DEPENDS ${CPACK_TARGET} WORKING_DIRECTORY ${SNAP_BINARY_DIR} COMMENT "Uploading package ${CPACK_TARGET} to SourceForge.net to Experimental directory") itksnap/Common/0000755000076500000240000000000011560342170012773 5ustar paulystaffitksnap/Common/CommandLineArgumentParser.cxx0000644000076500000240000001100411115053275020562 0ustar paulystaff/*========================================================================= 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(argc_out+nParameters >= argc) { // Too few parameters cerr << "Too few parameters to command line option '" << arg << "'" << endl; return false; } // 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); private: typedef std::vector< std::string > ParameterArrayType; typedef std::map< std::string, ParameterArrayType> OptionMapType; void Clear(); 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/Common/Credits.h0000644000076500000240000001050610735614367014560 0ustar paulystaff/*========================================================================= 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/Common/CVS/0000755000076500000240000000000011560342170013426 5ustar paulystaffitksnap/Common/CVS/Entries0000644000076500000240000000146411560342170014767 0ustar paulystaff/CommandLineArgumentParser.cxx/1.4/Mon Dec 1 21:27:25 2008// /CommandLineArgumentParser.h/1.4/Thu Nov 20 05:10:39 2008// /Credits.h/1.2/Sun Dec 30 04:05:11 2007// /IRISException.cxx/1.4/Mon Oct 18 08:30:46 2010// /IRISException.h/1.4/Wed Sep 16 20:03:13 2009// /IRISVectorTypes.h/1.3/Sat Nov 1 11:32:00 2008// /IRISVectorTypes.txx/1.3/Fri Jan 23 20:09:38 2009// /IRISVectorTypesToITKConversion.h/1.2/Sun Dec 30 04:05:12 2007// /Registry.cxx/1.2/Sun Dec 30 04:05:12 2007// /Registry.h/1.2/Sun Dec 30 04:05:12 2007// /SNAPBorlandDummyTypes.h/1.1/Sat Dec 2 04:22:09 2006// /SNAPCommon.cxx.in/1.4/Tue Feb 3 20:28:14 2009// /SNAPCommon.h/1.13/Thu Jul 1 21:40:24 2010// /SNAPOpenGL.h/1.3/Mon Dec 31 13:12:04 2007// /SystemInterface.cxx/1.23/Fri Apr 16 05:14:38 2010// /SystemInterface.h/1.11/Wed Apr 14 10:06:23 2010// D itksnap/Common/CVS/Entries.Log0000644000076500000240000000002211560342170015474 0ustar paulystaffA D/ITKExtras//// itksnap/Common/CVS/Repository0000644000076500000240000000001711560342170015526 0ustar paulystaffitksnap/Common itksnap/Common/CVS/Root0000644000076500000240000000010011560342170014263 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Common/IRISException.cxx0000644000076500000240000000347611457002666016166 0ustar paulystaff/*========================================================================= 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 IRISException ::operator const char *() { return m_SimpleMessage.c_str(); } IRISException ::IRISException() { m_SimpleMessage = "Unspecified IRIS exception"; } IRISException ::IRISException(const char *message, ...) { char buffer[1024]; va_list args; va_start(args, message); vsprintf(buffer,message,args); va_end (args); m_SimpleMessage = buffer; } IRISException ::~IRISException() { } itksnap/Common/IRISException.h0000644000076500000240000000416211254242201015566 0ustar paulystaff/*========================================================================= 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" using std::string; /** * \class IRISException * \brief Sets up a family of SNAP/IRIS exceptions */ class IRISException { protected: string m_SimpleMessage; public: IRISException(); IRISException(const char *message, ...); virtual ~IRISException(); const char * what() { return m_SimpleMessage.c_str(); } operator const char *(); }; /** * 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() {} \ }; irisExceptionMacro(IRISExceptionIO,IRISException) #endif itksnap/Common/IRISVectorTypes.h0000644000076500000240000001057111103037060016116 0ustar paulystaff/*========================================================================= 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 /** * \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; // Construct an uninitialized n-vector iris_vector_fixed() : Parent() {} // 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) {} /** * 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; // 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; // A matrix definition typedef vnl_matrix_fixed Matrix3d; #ifndef ITK_MANUAL_INSTANTIATION #include "IRISVectorTypes.txx" #endif #endif // __IRISVectorTypes_h_ itksnap/Common/IRISVectorTypes.txx0000644000076500000240000001450211136422002016510 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/0000755000076500000240000000000011560342170014611 5ustar paulystaffitksnap/Common/ITKExtras/CVS/0000755000076500000240000000000011560342170015244 5ustar paulystaffitksnap/Common/ITKExtras/CVS/Entries0000644000076500000240000000204511560342170016601 0ustar paulystaff/README.txt/1.1/Sat Dec 2 04:22:10 2006// /itkBSplineScatteredDataPointSetToImageFilter.h/1.1/Fri Oct 24 12:52:08 2008// /itkBSplineScatteredDataPointSetToImageFilter.txx/1.1/Fri Oct 24 12:52:08 2008// /itkBinaryDiamondStructuringElement.h/1.1/Wed Nov 26 03:10:15 2008// /itkBinaryDiamondStructuringElement.txx/1.2/Wed Dec 10 18:42:15 2008// /itkCoxDeBoorBSplineKernelFunction.h/1.1/Fri Oct 24 12:52:08 2008// /itkCoxDeBoorBSplineKernelFunction.txx/1.1/Fri Oct 24 12:52:08 2008// /itkParallelSparseFieldLevelSetImageFilterBugFix.h/1.1/Fri Oct 24 12:52:08 2008// /itkParallelSparseFieldLevelSetImageFilterBugFix.txx/1.1/Fri Oct 24 12:52:08 2008// /itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.h/1.2/Wed Dec 3 10:16:06 2008// /itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.txx/1.6/Fri Jan 16 21:31:41 2009// /itkVoxBoCUBImageIO.cxx/1.4/Thu Nov 20 04:23:28 2008// /itkVoxBoCUBImageIO.h/1.2/Sun Dec 30 04:05:12 2007// /itkVoxBoCUBImageIOFactory.cxx/1.2/Sun Dec 30 04:05:12 2007// /itkVoxBoCUBImageIOFactory.h/1.2/Sun Dec 30 04:05:12 2007// D itksnap/Common/ITKExtras/CVS/Repository0000644000076500000240000000003111560342170017340 0ustar paulystaffitksnap/Common/ITKExtras itksnap/Common/ITKExtras/CVS/Root0000644000076500000240000000010011560342170016101 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Common/ITKExtras/itkBinaryDiamondStructuringElement.h0000644000076500000240000000740011113137027023774 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkBinaryDiamondStructuringElement.txx0000644000076500000240000000350511120006607024367 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.h0000644000076500000240000002410311100342370025551 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkBSplineScatteredDataPointSetToImageFilter.txx0000644000076500000240000010162411100342370026151 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkCoxDeBoorBSplineKernelFunction.h0000644000076500000240000001267311100342370023443 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkCoxDeBoorBSplineKernelFunction.txx0000644000076500000240000001221311100342370024025 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.h0000644000076500000240000010032111100342370026217 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkParallelSparseFieldLevelSetImageFilterBugFix.txx0000644000076500000240000027243111100342370026627 0ustar paulystaff/*====================================================================== 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/Common/ITKExtras/itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.h0000644000076500000240000001014411115456146030205 0ustar paulystaff#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/Common/ITKExtras/itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.txx0000644000076500000240000004741311134176275030615 0ustar paulystaff#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/Common/ITKExtras/itkVoxBoCUBImageIO.cxx0000644000076500000240000005636711111163100020632 0ustar paulystaff/*========================================================================= 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 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()) { itk::MetaDataDictionary &dic =this->GetMetaDataDictionary(); EncapsulateMetaData( dic, ITK_CoordinateOrientation, it->second); } } 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 MetaDataDictionary &dic = GetMetaDataDictionary(); OrientationFlags oflag = SpatialOrientation::ITK_COORDINATE_ORIENTATION_INVALID; if(ExposeMetaData(dic, ITK_CoordinateOrientation, oflag)) { 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/Common/ITKExtras/itkVoxBoCUBImageIO.h0000644000076500000240000001123510735614370020265 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkVoxBoCUBImageIOFactory.cxx0000644000076500000240000000423110735614370022166 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/itkVoxBoCUBImageIOFactory.h0000644000076500000240000000541310735614370021616 0ustar paulystaff/*========================================================================= 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/Common/ITKExtras/README.txt0000644000076500000240000000014310534177562016320 0ustar paulystaffThis directory contains extra code that may be eventually added to ITK but is needed by ITK-SNAP. itksnap/Common/Registry.cxx0000644000076500000240000002524110735614370015342 0ustar paulystaff/*========================================================================= 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 #include #include #include #include #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif using namespace std; 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(); } 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 ::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 ::Encode(const StringType &input) { IRISOStringStream oss; for(unsigned int i=0; i < input.length() ; i++) { if(!isprint(input[i]) || input[i]=='%' || isspace(input[i])) { // Replace character by a escape string oss << '%'; oss << setw(2) << setfill('0') << hex << (int)input[i]; } else { // Just copy the character oss << input[i]; } } // 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; } /** * 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,""); } /** * Read folder from file */ void Registry ::ReadFromFile(const char *pathname) { // Create an error stream IRISOStringStream serr; // Create output stream ifstream sin(pathname,std::ios::in); if(!sin.good()) throw IOException("Unable to open the Registry file"); 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()); } itksnap/Common/Registry.h0000644000076500000240000002763210735614370014775 0ustar paulystaff/*========================================================================= 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" 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; } 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); /** 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 */ template void PutEnum( RegistryEnumMap &rem,TEnum value) { (*this) << rem.m_EnumToStringMap[value];; } /** Get an enum from this entry */ template TEnum GetEnum( RegistryEnumMap &rem,TEnum defaultValue) { if(rem.m_StringToEnumMap.find(m_String) == rem.m_StringToEnumMap.end()) return defaultValue; else return rem.m_StringToEnumMap[m_String]; } 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(); /** 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); /** 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); /** * Update the registry with keys from another registry */ void Update(const Registry ®); /** Read this Registry from a file */ void ReadFromFile(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); /** 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); }; #endif itksnap/Common/SNAPBorlandDummyTypes.h0000644000076500000240000000735710534177561017276 0ustar paulystaff/*========================================================================= 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/Common/SNAPCommon.cxx.in0000644000076500000240000000317411142124336016041 0ustar paulystaff/*========================================================================= 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@"; // 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@"; itksnap/Common/SNAPCommon.h0000644000076500000240000001257711413205510015063 0ustar paulystaff/*========================================================================= 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 itk::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[]; // 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; #define MAX_COLOR_LABELS 0xffff /** A structure used to map intensity from gray to 'native' intensity for images that are not of short datatype */ struct GreyTypeToNativeFunctor { double scale; double shift; double operator() (GreyType g) const { return g * scale + shift; } GreyTypeToNativeFunctor() : scale(1.0), shift(0.0) {} GreyTypeToNativeFunctor(double a, double b) : scale(a), shift(b) {} }; /** A structure used to map 'native' intensity to gray intensity for images that are not of short datatype */ struct NativeToGreyTypeFunctor { double scale; double shift; GreyType operator() (double g) const { return GreyType(g * scale + shift); } NativeToGreyTypeFunctor() : scale(1.0), shift(0.0) {} NativeToGreyTypeFunctor(double a, double b) : scale(a), shift(b) {} NativeToGreyTypeFunctor(GreyTypeToNativeFunctor g2n) : scale(1.0/g2n.scale), shift(-g2n.shift/g2n.scale) {} }; /************************************************************************/ /* 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; \ } /** * Set macro for strings */ #define irisSetStringMacro(name) \ virtual void Set##name (const char *_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(); \ } /** * A get macro for boolean variables, IsXXX instead of GetXXX */ #define irisIsMacro(name) \ virtual bool Is##name () const { \ return this->m_##name; \ } /** * A rip off from the ITK not used macro to eliminate 'parameter not used' * warnings */ #define irisNotUsed(x) #endif // __SNAPCommonIO_h_ itksnap/Common/SNAPOpenGL.h0000644000076500000240000000700510736165244014765 0ustar paulystaff/*========================================================================= 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 #ifdef __APPLE__ #include #include #else #include #include #endif #ifndef _WIN32 #ifndef GLU_VERSION_1_2 #define GLU_VERSION_1_2 #endif #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]); } #endif // __SNAPOpenGL_h_ itksnap/Common/SystemInterface.cxx0000644000076500000240000006500611361771076016645 0ustar paulystaff/*========================================================================= 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. =========================================================================*/ #include "SystemInterface.h" #include "IRISApplication.h" #include "IRISException.h" #include "GlobalState.h" #include "SNAPRegistryIO.h" #include "FL/Fl_Preferences.H" #include "FL/filename.H" #include #include #include "itkVoxBoCUBImageIOFactory.h" #include #include #include #include #include #ifdef WIN32 #include #include #else #include #include #include #include #endif using namespace std; using namespace itksys; SystemInterface ::SystemInterface() { // Initialize the registry m_RegistryIO = new SNAPRegistryIO; // Register the Image IO factories that are not part of ITK itk::ObjectFactoryBase::RegisterFactory( itk::VoxBoCUBImageIOFactory::New() ); // Create a preferences object Fl_Preferences test(Fl_Preferences::USER,"itk.org","SNAP"); // Use it to get a path for user data char userDataPath[1024]; test.getUserdataPath(userDataPath,1024); // Construct a valid path m_UserPreferenceFile = string(userDataPath) + "/" + "UserPreferences.txt"; // Get the process ID #ifdef WIN32 m_ProcessID = _getpid(); #else m_ProcessID = getpid(); #endif // Set the message ID and last sender/message id values m_LastReceivedMessageID = -1; m_LastSender = -1; m_MessageID = 0; // Attach to the shared memory IPCAttach(); } SystemInterface ::~SystemInterface() { delete m_RegistryIO; // Detach shared memory IPCClose(); } void SystemInterface ::SaveUserPreferences() { WriteToFile(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 ReadFromFile(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; } /** * A method that checks whether the SNAP system directory can be found and * if it can't, prompts the user for the directory. The path parameter is the * location of the executable, i.e., argv[0] */ bool SystemInterface ::FindDataDirectory(const char *pathToExe) { // Get the directory where the SNAP executable was launched using namespace itksys; typedef std::string StringType; // This is the directory we're trying to set StringType sRootDir = ""; // This is a key that we will use to represent the executable location StringType sCodedExePath; // First of all, find the executable file. Since the program may have been // in the $PATH variable, we don't know for sure where the data is // Create a vector of paths that will be searched for // the file SNAPProgramDataDirectory.txt StringType sExeFullPath = SystemTools::FindProgram(pathToExe); if(sExeFullPath.length()) { // Encode the path to the executable so that we can search for an associated // program data path sCodedExePath = EncodeFilename(sExeFullPath); } else { // Use a dummy token, so that next time the user runs without an executable, // we can find a data directory sCodedExePath = "00000000"; } // Check if there is a path associated with the code StringType sAssociationKey = Key("System.ProgramDataDirectory.Element[%s]",sCodedExePath.c_str()); StringType sAssociatedPath = Entry(sAssociationKey)[""]; // If the associated path exists, prepemd the path to the search list if(sAssociatedPath.length()) { // Check that the associated path is a real path containing the required // file StringType sSearchName = sAssociatedPath + "/" + GetProgramDataDirectoryTokenFileName(); // Perform a sanity check on the directory if(SystemTools::FileIsDirectory(sAssociatedPath.c_str()) && SystemTools::FileExists(sSearchName.c_str())) { // We've found the path sRootDir = sAssociatedPath; } } // If the associated path check failed, but the executable has been found, // which should be the case the first time the program is run, look around // the executable to find the path if(!sRootDir.length() && sExeFullPath.length()) { // Create a search list for the filename vector vPathList; // Look at the directory where the exe sits vPathList.push_back( SystemTools::GetFilenamePath(sExeFullPath) + "/ProgramData"); // Look one directory up from that vPathList.push_back( SystemTools::GetFilenamePath( SystemTools::GetFilenamePath(sExeFullPath)) + "/ProgramData"); // Also, for UNIX installations, look for ${INSTALL_PATH}/share/snap/ProgramData vPathList.push_back( SystemTools::GetFilenamePath( SystemTools::GetFilenamePath(sExeFullPath)) + "/share/snap/ProgramData"); // Search for the token file in the path list StringType sFoundFile = SystemTools::FindFile( GetProgramDataDirectoryTokenFileName(),vPathList); if(sFoundFile.length()) sRootDir = SystemTools::GetFilenamePath(sFoundFile); } // If we still don't have a root path, there's no home if(!sRootDir.length()) return false; // Store the property, so the next time we don't have to search at all Entry(sAssociationKey) << sRootDir; // Set the root directory and relative paths m_DataDirectory = sRootDir; // Append the paths to get the other directories m_DocumentationDirectory = m_DataDirectory + "/HTMLHelp"; // Save the path to executable m_FullPathToExecutable = sExeFullPath; // Done, success return true; } void SystemInterface ::LaunchChildSNAP(std::list args) { // Must have a valid path to the EXE if(m_FullPathToExecutable.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 *) m_FullPathToExecutable.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 #ifdef WIN32 _spawnvp(_P_NOWAIT, m_FullPathToExecutable.c_str(), argv); #else int pid; if((pid = fork()) == 0) { if(execvp(m_FullPathToExecutable.c_str(), argv) < 0) exit(-1); } #endif } std::string SystemInterface ::GetFileInRootDirectory(const char *fnRelative) { // Construct the file name string path = m_DataDirectory + "/" + fnRelative; // Make sure the file exists ? // Return the file return path; } bool SystemInterface ::FindRegistryAssociatedWithFile(const char *file, Registry ®istry) { // Convert the file to an absolute path char buffer[1024]; fl_filename_absolute(buffer,1024,file); // Convert to unix slashes for consistency string path(buffer); SystemTools::ConvertToUnixSlashes(path); // Convert the filename to a numeric string (to prevent clashes with the Registry class) path = EncodeFilename(path); // Get the key associated with this filename string key = Key("ImageAssociation.Mapping.Element[%s]",path.c_str()); string code = Entry(key)[""]; // If the code does not exist, return w/o success if(code.length() == 0) return false; // Create a preferences object for the associations subdirectory Fl_Preferences test(Fl_Preferences::USER,"itk.org","SNAP/ImageAssociations"); // Use it to get a path for user data char userDataPath[1024]; test.getUserdataPath(userDataPath,1024); // Create a save filename IRISOStringStream sfile; sfile << userDataPath << "/" << "ImageAssociation." << code << ".txt"; // Try loading the registry try { registry.ReadFromFile(sfile.str().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) { // Create a preferences object for the associations subdirectory string subpath("SNAP/"); subpath += category; Fl_Preferences pref(Fl_Preferences::USER,"itk.org",subpath.c_str()); // Use it to get a path for user data char userDataPath[1024]; pref.getUserdataPath(userDataPath,1024); // Get the names vector names; // Get the listing of all files in there Directory dlist; dlist.Load(userDataPath); for(size_t i = 0; i < dlist.GetNumberOfFiles(); i++) { string fname = dlist.GetFile(i); // Check regular file ostringstream ffull; ffull << userDataPath << "/" << fname; if(SystemTools::FileExists(ffull.str().c_str(), true)) { // Check extension if(SystemTools::GetFilenameExtension(fname) == ".txt") { 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) { // Create a preferences object for the associations subdirectory string subpath("SNAP/"); subpath += category; Fl_Preferences pref(Fl_Preferences::USER,"itk.org",subpath.c_str()); // Use it to get a path for user data char userDataPath[1024]; pref.getUserdataPath(userDataPath,1024); // Create a save filename IRISOStringStream sfile; sfile << userDataPath << "/" << EncodeObjectName(name) << ".txt"; string fname = sfile.str(); // Check the filename if(!SystemTools::FileExists(fname.c_str(), true)) throw IRISException("Saved object file does not exist"); Registry reg(fname.c_str()); return reg; } void SystemInterface ::UpdateSavedObject(const char *category, const char *name, Registry &folder) { // Create a preferences object for the associations subdirectory string subpath("SNAP/"); subpath += category; Fl_Preferences pref(Fl_Preferences::USER,"itk.org",subpath.c_str()); // Use it to get a path for user data char userDataPath[1024]; pref.getUserdataPath(userDataPath,1024); // Create a save filename IRISOStringStream sfile; sfile << userDataPath << "/" << EncodeObjectName(name) << ".txt"; string fname = sfile.str(); // Save the data folder.WriteToFile(fname.c_str()); } void SystemInterface ::DeleteSavedObject(const char *category, const char *name) { // Create a preferences object for the associations subdirectory string subpath("SNAP/"); subpath += category; Fl_Preferences pref(Fl_Preferences::USER,"itk.org",subpath.c_str()); // Use it to get a path for user data char userDataPath[1024]; pref.getUserdataPath(userDataPath,1024); // Create a save filename IRISOStringStream sfile; sfile << userDataPath << "/" << EncodeObjectName(name) << ".txt"; // Save the data SystemTools::RemoveFile(sfile.str().c_str()); } string SystemInterface ::EncodeFilename(const string &src) { IRISOStringStream sout; for(unsigned int i=0;iWriteImageAssociatedSettings(app,registry); // Write the registry back return AssociateRegistryWithFile(file,registry); } 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; } /** Get a filename history list by a particular name */ SystemInterface::HistoryListType SystemInterface ::GetHistory(const char *key) { // Get the history array return Folder("IOHistory").Folder(string(key)).GetArray(string("")); } /** Update a filename history list with another filename */ void SystemInterface ::UpdateHistory(const char *key, const char *filename) { // Create a string for the new file string file(filename); // Get the current history registry HistoryListType array = GetHistory(key); // First, search the history for the instance of the file and delete // existing occurences HistoryListType::iterator it; while((it = find(array.begin(),array.end(),file)) != array.end()) array.erase(it); // Append the file to the end of the array array.push_back(file); // Trim the array to appropriate size if(array.size() > 20) array.erase(array.begin(),array.begin() + array.size() - 20); // Store the new array to the registry Folder("IOHistory").Folder(string(key)).PutArray(array); // Save the preferences at this point SaveUserPreferences(); } // We start versioning at 1000. Every time we change // the protocol, we should increment the version id const short SystemInterface::IPC_VERSION = 0x1003; void SystemInterface ::IPCAttach() { // Initialize the data pointer m_IPCSharedData = NULL; // Determine size of shared memory size_t msize = sizeof(IPCMessage) + sizeof(short); #ifdef WIN32 // Create a shared memory block (key based on the preferences file) m_IPCHandle = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, msize, m_UserPreferenceFile.c_str()); // If the return value is NULL, something failed if(m_IPCHandle) { // Attach to the shared memory block m_IPCSharedData = MapViewOfFile(m_IPCHandle, FILE_MAP_ALL_ACCESS, 0, 0, msize); } #else // Create a unique key for this user key_t keyid = ftok(m_UserPreferenceFile.c_str(), IPC_VERSION); // Get a handle to shared memory m_IPCHandle = shmget(keyid, msize, IPC_CREAT | 0644); // There may be an error! if(m_IPCHandle < 0) { cerr << "Shared memory (shmget) error: " << strerror(errno) << endl; cerr << "Multisession support is disabled" << endl; m_IPCSharedData = NULL; } else { // Get a pointer to shared data block m_IPCSharedData = shmat(m_IPCHandle, (void *) 0, 0); // Check errors again if(!m_IPCSharedData) { cerr << "Shared memory (shmat) error: " << strerror(errno) << endl; cerr << "Multisession support is disabled" << endl; m_IPCSharedData = NULL; } } #endif } bool SystemInterface ::IPCRead(IPCMessage &msg) { // Must have some shared memory if(!m_IPCSharedData) return false; // Read the first short, make sure it's the version number short *vid = static_cast(m_IPCSharedData); if(*vid != IPC_VERSION) return false; // Cast shared memory to the message type IPCMessage *p = reinterpret_cast(vid + 1); // Return a copy msg = *p; // Store the last sender / id m_LastSender = p->sender_pid; m_LastReceivedMessageID = p->message_id; // Success! return true; } bool SystemInterface ::IPCReadIfNew(IPCMessage &msg) { // Must have some shared memory if(!m_IPCSharedData) return false; // Read the first short, make sure it's the version number short *vid = static_cast(m_IPCSharedData); if(*vid != IPC_VERSION) return false; // Cast shared memory to the message type IPCMessage *p = reinterpret_cast(vid + 1); // If we are the sender, the message should be ignored if(p->sender_pid == this->GetProcessID()) return false; // If we have already seen this message from this sender, also ignore it if(m_LastSender == p->sender_pid && m_LastReceivedMessageID == p->message_id) return false; // Store the last sender / id m_LastSender = p->sender_pid; m_LastReceivedMessageID = p->message_id; // Return a copy msg = *p; // Success! return true; } bool SystemInterface ::IPCBroadcastCursor(Vector3d cursor) { // Try reading the current memory IPCMessage mcurr; // This may fail, but that's ok IPCRead(mcurr); // Update the message mcurr.cursor = cursor; return IPCBroadcast(mcurr); } bool SystemInterface ::IPCBroadcastTrackball(Trackball tball) { // Try reading the current memory IPCMessage mcurr; // This may fail, but that's ok IPCRead(mcurr); // Update the message mcurr.trackball = tball; return IPCBroadcast(mcurr); } bool SystemInterface ::IPCBroadcastZoomLevel(double zoom_level) { // Try reading the current memory IPCMessage mcurr; // This may fail, but that's ok IPCRead(mcurr); // Update the message mcurr.zoom_level = zoom_level; return IPCBroadcast(mcurr); } bool SystemInterface ::IPCBroadcastViewPosition(Vector2f vec[3]) { // Try reading the current memory IPCMessage mcurr; // This may fail, but that's ok IPCRead(mcurr); // Update the message mcurr.viewPositionRelative[0] = vec[0]; mcurr.viewPositionRelative[1] = vec[1]; mcurr.viewPositionRelative[2] = vec[2]; return IPCBroadcast(mcurr); } bool SystemInterface ::IPCBroadcast(IPCMessage msg) { // Write to the shared memory if(m_IPCSharedData) { // Write version number short *vid = static_cast(m_IPCSharedData); *vid = IPC_VERSION; // Write the process ID msg.sender_pid = this->GetProcessID(); msg.message_id = ++m_MessageID; // Write message IPCMessage *p = reinterpret_cast(vid + 1); *p = msg; // Done return true; } return false; } void SystemInterface ::IPCClose() { #ifdef WIN32 CloseHandle(m_IPCHandle); #else // Detach from the shared memory segment shmdt(m_IPCSharedData); // If there is noone attached to the memory segment, destroy it struct shmid_ds dsinfo; shmctl(m_IPCHandle, IPC_STAT, &dsinfo); if(dsinfo.shm_nattch == 0) shmctl(m_IPCHandle, IPC_RMID, NULL); #endif } // 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) { // std::cout << "initialize auto update" << std::endl; Entry("System.LastUpdateTimeStamp") << time(NULL); } else if (atoi(lastUpdateTimeStamp.c_str()) + 604800 >= time(NULL)) { // std::cout << "too soon for auto update check" << std::endl; return US_TOO_SOON; } else { // std::cout << "auto update" << std::endl; 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 /version/" << 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; iss >> vmajor; iss >> vminor; iss >> vpatch; iss >> vqual; // Format the version in printable format ostringstream over; over << vmajor << "." << vminor << "." << vpatch << vqual; newversion = over.str(); // Compare version if(vmajor > SNAPVersionMajor || (vmajor == SNAPVersionMajor && vminor > SNAPVersionMinor) || (vmajor == SNAPVersionMajor && vminor == SNAPVersionMinor && vpatch > SNAPVersionPatch)) 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; } itksnap/Common/SystemInterface.h0000644000076500000240000001722011361311637016256 0ustar paulystaff/*========================================================================= 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 "Trackball.h" class IRISApplication; class SNAPRegistryIO; #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(); /** * A method that checks whether the SNAP system directory can be found and * if it can't, prompts the user for the directory. If the user refuses to * supply the directory, it throws an exception */ bool FindDataDirectory(const char *pathToExe); /** * Get a file relative to the root directory (returns absolute filename), or * throws exception if the file does not exist */ std::string GetFileInRootDirectory(const char *fnRelative); /** 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"; } // Typedef for history lists typedef std::vector HistoryListType; /** Get a filename history list by a particular name */ HistoryListType GetHistory(const char *key); /** Update a filename history list with another filename */ void UpdateHistory(const char *key, const char *file); /** 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); /** 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); std::string DecodeObjectName(std::string fname); 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); /** Structure passed on to IPC */ struct IPCMessage { // Process ID of the sender long sender_pid, message_id; // The cursor position in world coordinates Vector3d cursor; // The common zoom factor (screen pixels / mm) double zoom_level; // The position of the viewport center relative to cursor // in all three slice views Vector2f viewPositionRelative[3]; // 3D view settings Trackball trackball; }; /** Get process ID */ long GetProcessID() { return m_ProcessID; } /** Interprocess communication: attach to shared memory */ void IPCAttach(); /** Interprocess communication: read shared memory */ bool IPCRead(IPCMessage &mout); /** Read IPC message, but only if it's new data */ bool IPCReadIfNew(IPCMessage &mout); /** Interprocess communication: write shared memory */ bool IPCBroadcast(IPCMessage mout); bool IPCBroadcastCursor(Vector3d cursor); bool IPCBroadcastTrackball(Trackball tball); bool IPCBroadcastZoomLevel(double zoom); bool IPCBroadcastViewPosition(Vector2f vec[3]); /** Interprocess communication: release shared memory */ void IPCClose(); /** 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 */ void LaunchChildSNAP(std::list args); private: std::string m_UserPreferenceFile; std::string m_DataDirectory; std::string m_DocumentationDirectory; std::string m_FullPathToExecutable; // An object used to write large chunks of SNAP data to the registry SNAPRegistryIO *m_RegistryIO; // Filename encoder std::string EncodeFilename(const std::string &src); // System-specific IPC related stuff #ifdef WIN32 HANDLE m_IPCHandle; #else int m_IPCHandle; #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; // Generic: shared data for IPC void *m_IPCSharedData; // Process ID and other values used by IPC long m_ProcessID, m_MessageID, m_LastSender, m_LastReceivedMessageID; }; #endif //__SystemInterface_h_ itksnap/COPYING0000644000076500000240000010451310735614367012617 0ustar paulystaff 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/CTestConfig.cmake0000644000076500000240000000103111264712531014713 0ustar paulystaff## 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 uses Dart and the Cdash dashboard ## ENABLE_TESTING() ## INCLUDE(Dart) set(CTEST_PROJECT_NAME "ITK-SNAP") set(CTEST_NIGHTLY_START_TIME "01:00:00 EST") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "itksnap.org") set(CTEST_DROP_LOCATION "/cdash/submit.php?project=ITK-SNAP") set(CTEST_DROP_SITE_CDASH TRUE) itksnap/CVS/0000755000076500000240000000000011560342170012176 5ustar paulystaffitksnap/CVS/Entries0000644000076500000240000000035211560342170013532 0ustar paulystaff/CMakeLists.txt/1.96/Tue May 3 20:11:12 2011// /COPYING/1.1/Sun Dec 30 04:05:11 2007// /CTestConfig.cmake/1.2/Mon Oct 12 20:45:13 2009// /README.html/1.3/Wed Oct 20 15:36:42 2010// /ReleaseNotes.txt/1.26/Wed May 4 21:21:57 2011// D itksnap/CVS/Entries.Log0000644000076500000240000000016711560342171014257 0ustar paulystaffA D/CMake//// A D/Common//// A D/Logic//// A D/ProgramData//// A D/Testing//// A D/UserInterface//// A D/Utilities//// itksnap/CVS/Repository0000644000076500000240000000001011560342170014267 0ustar paulystaffitksnap itksnap/CVS/Root0000644000076500000240000000010011560342170013033 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/0000755000076500000240000000000011560342170012600 5ustar paulystaffitksnap/Logic/Common/0000755000076500000240000000000011560342170014030 5ustar paulystaffitksnap/Logic/Common/ColorLabel.h0000644000076500000240000001223511110344057016217 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ColorLabel.h,v $ Language: C++ Date: $Date: 2008/11/17 19:38:23 $ 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 __ColorLabel_h_ #define __ColorLabel_h_ #include "SNAPCommon.h" #include #include /** * \class ColorLabel * \brief Information about a label used for segmentation. * Color labels used to describe pixels in the segmented * image. These labels correspond to the intensities in the * segmentation image in IRISImageData class */ class ColorLabel { public: // Dummy constructor and destructor (to make gcc happy) ColorLabel() {} virtual ~ColorLabel() {} // Read the Visible attribute irisIsMacro(Visible); // Set the Visible attribute irisSetMacro(Visible,bool); // Read the Valid attribute irisIsMacro(Valid); // Set the Valid attribute irisSetMacro(Valid,bool); // Read the DoMesh attribute irisIsMacro(VisibleIn3D); // Set the DoMesh attribute irisSetMacro(VisibleIn3D,bool); // Read the Label attribute virtual const char *GetLabel() const { return m_Label.c_str(); } // Set the Label attribute irisSetMacro(Label,const char *); // Read the Alpha attribute irisGetMacro(Alpha,unsigned char); // Set the Alpha attribute irisSetMacro(Alpha,unsigned char); // Get the value irisGetMacro(Value, LabelType); // Check Opaqueness bool IsOpaque() const { return m_Alpha == 255; } // Read the RGB attributes unsigned char GetRGB(unsigned int index) const { assert(index < 3); return m_RGB[index]; } // Set the RGB attributes void SetRGB(unsigned int index, unsigned char in_Value) { assert(index < 3); m_RGB[index] = in_Value; } // Set all three at once void SetRGB(unsigned char in_Red,unsigned char in_Green,unsigned char in_Blue) { m_RGB[0] = in_Red; m_RGB[1] = in_Green; m_RGB[2] = in_Blue; } // Copy RGB into an array void GetRGBVector(unsigned char array[3]) const { array[0] = m_RGB[0]; array[1] = m_RGB[1]; array[2] = m_RGB[2]; } // Copy RGB into an array void SetRGBVector(const unsigned char array[3]) { m_RGB[0] = array[0]; m_RGB[1] = array[1]; m_RGB[2] = array[2]; } // Copy RGB into an array void GetRGBAVector(unsigned char array[4]) const { array[0] = m_RGB[0]; array[1] = m_RGB[1]; array[2] = m_RGB[2]; array[3] = m_Alpha; } // Copy RGB into an array void SetRGBAVector(const unsigned char array[4]) { m_RGB[0] = array[0]; m_RGB[1] = array[1]; m_RGB[2] = array[2]; m_Alpha = array[3]; } // Copy all properties, except for the label id and the valid flag // from one label to the other. This is used to reassign ids to labels // because the labels are in sequential order void SetPropertiesFromColorLabel(const ColorLabel &lSource) { m_VisibleIn3D = lSource.m_VisibleIn3D; m_Visible = lSource.m_Visible; m_Alpha = lSource.m_Alpha; m_Label = lSource.m_Label; m_RGB[0] = lSource.m_RGB[0]; m_RGB[1] = lSource.m_RGB[1]; m_RGB[2] = lSource.m_RGB[2]; } private: // The descriptive text of the label std::string m_Label; // The intensity assigned to the label in the segmentation image LabelType m_Value; // The ID of the label in some external database. std::string m_DatabaseId; // The internal ID of the label. By default it's equal to the value unsigned int m_Id; // Whether the label is visible in 2D views bool m_Visible; // Whether the mesh for the label is computed bool m_VisibleIn3D; // The system timestamp when the data marked with this label was last updated unsigned long m_UpdateTime; // Whether the label is valid (has been added) bool m_Valid; // The transparency level of the label unsigned char m_Alpha; // The color of the label unsigned char m_RGB[3]; // A list of labels that contain this label std::list< unsigned int > m_Parents; // A list of labels that this label contains std::list< unsigned int > m_Children; }; #endif itksnap/Logic/Common/ColorLabelTable.cxx0000644000076500000240000002457611412166664017570 0ustar paulystaff#include "ColorLabelTable.h" #include #include #include #include using namespace std; extern int fl_parse_color( const char* p, unsigned char& r, unsigned char& g, unsigned char& b); // Some randomly ordered colors const size_t ColorLabelTable::m_ColorListSize = 130; const char *ColorLabelTable::m_ColorList[ColorLabelTable::m_ColorListSize] = { "#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#00FFFF", "#FF00FF", "#FFEFD5", "#0000CD", "#CD853F", "#D2B48C", "#66CDAA", "#000080", "#008B8B", "#2E8B57", "#FFE4E1", "#6A5ACD", "#DDA0DD", "#E9967A", "#A52A2A", "#FFFAFA", "#9370DB", "#DA70D6", "#4B0082", "#FFB6C1", "#3CB371", "#FFEBCD", "#FFE4C4", "#DAA520", "#008080", "#BC8F8F", "#FF69B4", "#FFDAB9", "#DEB887", "#7FFF00", "#8B4513", "#7CFC00", "#FFFFE0", "#4682B4", "#006400", "#EE82EE", "#EEE8AA", "#F0FFF0", "#F5DEB3", "#B8860B", "#20B2AA", "#FF1493", "#191970", "#708090", "#228B22", "#F8F8FF", "#F5FFFA", "#FFA07A", "#90EE90", "#ADFF2F", "#4169E1", "#FF6347", "#FAF0E6", "#800000", "#32CD32", "#F4A460", "#FFFFF0", "#7B68EE", "#FFA500", "#ADD8E6", "#FFC0CB", "#7FFFD4", "#FF8C00", "#8FBC8F", "#DC143C", "#FDF5E6", "#FFFAF0", "#00CED1", "#00FF7F", "#800080", "#FFFACD", "#FA8072", "#9400D3", "#B22222", "#FF7F50", "#87CEEB", "#6495ED", "#F0E68C", "#FAEBD7", "#FFF5EE", "#6B8E23", "#87CEFA", "#00008B", "#8B008B", "#F5F5DC", "#BA55D3", "#FFE4B5", "#FFDEAD", "#00BFFF", "#D2691E", "#FFF8DC", "#2F4F4F", "#483D8B", "#AFEEEE", "#808000", "#B0E0E6", "#FFF0F5", "#8B0000", "#F0FFFF", "#FFD700", "#D8BFD8", "#778899", "#DB7093", "#48D1CC", "#FF00FF", "#C71585", "#9ACD32", "#BDB76B", "#F0F8FF", "#E6E6FA", "#00FA9A", "#556B2F", "#40E0D0", "#9932CC", "#CD5C5C", "#FAFAD2", "#5F9EA0", "#008000", "#FF4500", "#E0FFFF", "#B0C4DE", "#8A2BE2", "#1E90FF", "#F08080", "#98FB98", "#A0522D"}; ColorLabelTable ::ColorLabelTable() { // Initialize the default labels const unsigned int INIT_VALID_LABELS = 7; // Set up the clear color m_DefaultLabel[0].SetRGB(0,0,0); m_DefaultLabel[0].SetAlpha(0); m_DefaultLabel[0].SetValid(true); m_DefaultLabel[0].SetVisible(false); m_DefaultLabel[0].SetVisibleIn3D(false); m_DefaultLabel[0].SetLabel("Clear Label"); // Set the colors from the table for(size_t i = 1; i < MAX_COLOR_LABELS; i++) { unsigned char r,g,b; fl_parse_color(m_ColorList[(i-1) % m_ColorListSize],r,g,b); m_DefaultLabel[i].SetRGB(r,g,b); m_DefaultLabel[i].SetAlpha(255); m_DefaultLabel[i].SetValid(i < INIT_VALID_LABELS); m_DefaultLabel[i].SetVisible(true); m_DefaultLabel[i].SetVisibleIn3D(true); IRISOStringStream sout; sout << "Label " << i; m_DefaultLabel[i].SetLabel(sout.str().c_str()); } // Copy default labels to active labels InitializeToDefaults(); } void ColorLabelTable ::LoadFromFile(const char *file) throw(itk::ExceptionObject) { // Create a stream for reading the file ifstream fin(file); string line; // Create a temporary array of color labels vector xTempLabels; vector xTempLabelIds; // Check that the file is readable if(!fin.good()) { throw itk::ExceptionObject( __FILE__, __LINE__,"File does not exist or can not be opened"); } // Read each line of the file separately for(unsigned int iLine=0;!fin.eof();iLine++) { // Read the line into a string std::getline(fin,line); // Check if the line is a comment or a blank line if(line[0] == '#' || line.length() == 0) continue; // Create a stream to parse that string IRISIStringStream iss(line); iss.exceptions(std::ios::badbit | std::ios::failbit); try { // Read in the elements of the file int idx, red, green, blue, visible, mesh; float alpha; iss >> idx; iss >> red; iss >> green; iss >> blue; iss >> alpha; iss >> visible; iss >> mesh; // Skip to a quotation mark iss.ignore(line.length(),'\"'); // Allocate a label of appropriate size char *label = new char[line.length()+1]; // Read the label iss.get(label,line.length(),'\"'); // Create a new color label ColorLabel cl; // Store the results cl.SetValid(true); cl.SetRGB(0,(unsigned char) red); cl.SetRGB(1,(unsigned char) green); cl.SetRGB(2,(unsigned char) blue); cl.SetAlpha( (unsigned char) (255 * alpha) ); cl.SetVisible(visible != 0); cl.SetVisibleIn3D(mesh != 0); cl.SetLabel(label); // Add the color label to the list xTempLabels.push_back(cl); xTempLabelIds.push_back(idx); // Clean up the label delete label; } catch( std::exception ) { // Close the input stream fin.close(); // create an exeption string IRISOStringStream oss; oss << "Syntax error on line " << (iLine+1); // throw our own exception throw itk::ExceptionObject( __FILE__, __LINE__,oss.str().c_str()); } } fin.close(); // Ok, we should have a list of color labels. Now initalize the color label array // the blank state RemoveAllLabels(); // Now, add all the labels for(size_t iLabel = 0; iLabel < xTempLabels.size(); iLabel++) if(xTempLabelIds[iLabel] > 0) SetColorLabel(xTempLabelIds[iLabel], xTempLabels[iLabel]); } void ColorLabelTable ::SaveToFile(const char *file) throw(itk::ExceptionObject) { // Open the file for writing ofstream fout(file); // Check that the file is readable if(!fout.good()) { throw itk::ExceptionObject( __FILE__, __LINE__,"File can not be opened for writing"); } // Print out a header to the file fout << "################################################"<< endl; fout << "# ITK-SnAP Label Description File" << endl; fout << "# File format: " << endl; fout << "# IDX -R- -G- -B- -A-- VIS MSH LABEL" << endl; fout << "# Fields: " << endl; fout << "# IDX: Zero-based index " << endl; fout << "# -R-: Red color component (0..255)" << endl; fout << "# -G-: Green color component (0..255)" << endl; fout << "# -B-: Blue color component (0..255)" << endl; fout << "# -A-: Label transparency (0.00 .. 1.00)" << endl; fout << "# VIS: Label visibility (0 or 1)" << endl; fout << "# IDX: Label mesh visibility (0 or 1)" << endl; fout << "# LABEL: Label description " << endl; fout << "################################################"<< endl; // Print out the labels for(unsigned int i=0;i 0 && index < MAX_COLOR_LABELS) { // Get current color label ColorLabel &cl = m_Label[index]; // Read the color label properties cl.SetAlpha(folder["Alpha"][(int) cl.GetAlpha()]); cl.SetLabel(folder["Label"][cl.GetLabel()]); // Read the color property Vector3i color = folder["Color"][Vector3i(cl.GetRGB(0),cl.GetRGB(1),cl.GetRGB(2))]; cl.SetRGB((unsigned char)color[0],(unsigned char)color[1], (unsigned char)color[2]); // Read the flag property Vector2i flags = folder["Flags"][Vector2i(0,0)]; cl.SetVisibleIn3D(flags[0] > 0); cl.SetVisible(flags[1] > 0); cl.SetValid(true); } } } void ColorLabelTable ::SaveToRegistry(Registry ®istry) { // Write the labels themselves unsigned int validLabels = 0; for(unsigned int i=1;i < MAX_COLOR_LABELS; i++) { // Get the i-th color label const ColorLabel &cl = m_Label[i]; // Only write valid color labels (otherwise, what's the point?) if(!cl.IsValid()) continue; // Create a folder for the color label Registry &folder = registry.Folder(registry.Key("Element[%d]",validLabels)); folder["Index"] << i; folder["Alpha"] << (int) cl.GetAlpha(); folder["Label"] << cl.GetLabel(); folder["Color"] << Vector3i(cl.GetRGB(0),cl.GetRGB(1),cl.GetRGB(2)); folder["Flags"] << Vector2i(cl.IsVisibleIn3D(),cl.IsVisible()); // Increment the valid label counter validLabels++; } registry["NumberOfElements"] << validLabels; } void ColorLabelTable ::RemoveAllLabels() { // Invalidate all the labels for(size_t iLabel = 1; iLabel < MAX_COLOR_LABELS; iLabel++) { m_Label[iLabel].SetValid(false); } } size_t ColorLabelTable ::GetFirstValidLabel() const { for(size_t iLabel = 1; iLabel < MAX_COLOR_LABELS; iLabel++) if(m_Label[iLabel].IsValid()) return iLabel; return 0; } void ColorLabelTable ::InitializeToDefaults() { // Set the properties of all non-clear labels for (size_t 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 __ColorLabelTable_h_ #define __ColorLabelTable_h_ #include "Registry.h" #include "ColorLabel.h" #include "itkExceptionObject.h" /** * \class ColorLabelTable * \brief A table for managing color labels */ class ColorLabelTable { public: ColorLabelTable(); // Flat file IO void LoadFromFile(const char *file) throw(itk::ExceptionObject); void SaveToFile(const char *file) throw(itk::ExceptionObject); // Registry IO void LoadFromRegistry(Registry ®istry); void SaveToRegistry(Registry ®istry); /** Initialize the labels to a default state */ void InitializeToDefaults(); /** Clear all the color labels, except the 'clear' label */ void RemoveAllLabels(); /** Get the number of valid color labels */ size_t GetNumberOfValidLabels(); /** Get the i'th label, i between 0 and 255 */ const ColorLabel &GetColorLabel(size_t id) const { assert(id < MAX_COLOR_LABELS); return m_Label[id]; } /** Set the i'th color label, i between 0 and 255 */ void SetColorLabel(size_t id, const ColorLabel &label) { assert(id < MAX_COLOR_LABELS); m_Label[id] = label; } /** Generate a default color label at index i */ ColorLabel GetDefaultColorLabel(size_t id) { assert(id < MAX_COLOR_LABELS); return m_DefaultLabel[id]; } bool IsColorLabelValid(size_t id) const { return GetColorLabel(id).IsValid(); } /** Sets the color label valid or invalid. During invalidation, the label * reverts to default values */ void SetColorLabelValid(size_t id, bool flag); /** Return the first valid color label, or zero if there aren't any */ size_t GetFirstValidLabel() const; private: // A flat array of color labels ColorLabel m_Label[MAX_COLOR_LABELS], m_DefaultLabel[MAX_COLOR_LABELS]; static const char *m_ColorList[]; static const size_t m_ColorListSize; }; #endif itksnap/Logic/Common/ColorMap.cxx0000644000076500000240000003411711457027270016304 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ColorMap.cxx,v $ Language: C++ Date: $Date: 2010/10/18 11:25:44 $ Version: $Revision: 1.12 $ 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 "ColorMap.h" #include "IRISException.h" #include bool ColorMap::CMPoint ::operator==(const ColorMap::CMPoint& rhs) const { if (m_Index != rhs.m_Index) return false; if (m_Type != rhs.m_Type) return false; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 4; ++j) { if (m_RGBA[i][j] != rhs.m_RGBA[i][j]) return false; } } return true; } ColorMap::CMPoint ::CMPoint() { m_Index = 0.0; m_Type = CONTINUOUS; m_RGBA[0][0] = 0; m_RGBA[1][0] = 0; m_RGBA[0][1] = 0; m_RGBA[1][1] = 0; m_RGBA[0][2] = 0; m_RGBA[1][2] = 0; m_RGBA[0][3] = 0; m_RGBA[1][3] = 0; } // Continuous point ColorMap::CMPoint ::CMPoint(double j, EltType r, EltType g, EltType b, EltType a) { m_Index = j; m_Type = CONTINUOUS; m_RGBA[0][0] = r; m_RGBA[1][0] = r; m_RGBA[0][1] = g; m_RGBA[1][1] = g; m_RGBA[0][2] = b; m_RGBA[1][2] = b; m_RGBA[0][3] = a; m_RGBA[1][3] = a; } // CMPoint with alpha discontinuity ColorMap::CMPoint ::CMPoint(double j, EltType r, EltType g, EltType b, EltType a1, EltType a2) { m_Index = j; m_Type = DISCONTINUOUS; m_RGBA[0][0] = r; m_RGBA[1][0] = r; m_RGBA[0][1] = g; m_RGBA[1][1] = g; m_RGBA[0][2] = b; m_RGBA[1][2] = b; m_RGBA[0][3] = a1; m_RGBA[1][3] = a2; } // CMPoint with full discontinuity ColorMap::CMPoint ::CMPoint(double j, EltType r1, EltType g1, EltType b1, EltType a1, EltType r2, EltType g2, EltType b2, EltType a2) { m_Index = j; m_Type = DISCONTINUOUS; m_RGBA[0][0] = r1; m_RGBA[1][0] = r2; m_RGBA[0][1] = g1; m_RGBA[1][1] = g2; m_RGBA[0][2] = b1; m_RGBA[1][2] = b2; m_RGBA[0][3] = a1; m_RGBA[1][3] = a2; } // CMPoint copy constructor ColorMap::CMPoint ::CMPoint(const CMPoint& rhs) { m_Index = rhs.m_Index; m_Type = rhs.m_Type; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 4; ++j) { m_RGBA[i][j] = rhs.m_RGBA[i][j]; } } } bool operator < (const ColorMap::CMPoint &p1, const ColorMap::CMPoint &p2) { return p1.m_Index < p2.m_Index; } ColorMap ::ColorMap() { SetToSystemPreset(COLORMAP_GREY); } ColorMap ::ColorMap(SystemPreset preset) { SetToSystemPreset(preset); } ColorMap ::ColorMap(const ColorMap& rhs) { CMPointConstIterator it = rhs.m_CMPoints.begin(); m_CMPreset = rhs.m_CMPreset; while (it != rhs.m_CMPoints.end()) { m_CMPoints.push_back( *it ); ++it; } this->UpdateInterpolants(); } bool ColorMap ::operator==(const ColorMap& rhs) const { // Compare two colormaps for equality if (m_CMPoints.size() != rhs.m_CMPoints.size()) return false; CMPointConstIterator it = m_CMPoints.begin(); CMPointConstIterator rit = rhs.m_CMPoints.begin(); while (it != m_CMPoints.end()) { if (!(*it == *rit)) return false; ++it; ++rit; } return true; } size_t ColorMap ::InsertInterpolatedCMPoint(double j) { // Create the new point RGBAType rgba = MapIndexToRGBA(j); CMPoint newbie(j, rgba[0], rgba[1], rgba[2], rgba[3]); // Find where to insert the new point CMPointIterator it = std::lower_bound(m_CMPoints.begin(), m_CMPoints.end(), newbie); // Insert the point after the lower bound CMPointIterator itnew = m_CMPoints.insert(it, newbie); this->UpdateInterpolants(); return itnew - m_CMPoints.begin(); } void ColorMap ::UpdateInterpolants() { // Set the size of the interpolant array to n+1 size_t n = m_CMPoints.size(); m_Interpolants.resize(n+1); // Loop over the component for(unsigned int d = 0; d < 4; d++) { // Set first and last interpolants m_Interpolants[0].slope[d] = 0.0f; m_Interpolants[0].intercept[d] = m_CMPoints[0].m_RGBA[0][d]; m_Interpolants[n].slope[d] = 0.0f; m_Interpolants[n].intercept[d] = m_CMPoints[n-1].m_RGBA[1][d]; // Set the intermediate interpolants for(size_t i = 1; i < n; i++) { float t0 = m_CMPoints[i-1].m_Index; float t1 = m_CMPoints[i].m_Index; float c0 = m_CMPoints[i-1].m_RGBA[1][d]; float c1 = m_CMPoints[i].m_RGBA[0][d]; if(t1 > t0) { m_Interpolants[i].slope[d] = (c1 - c0) / (t1 - t0); m_Interpolants[i].intercept[d] = c0 - m_Interpolants[i].slope[d] * t0; } else { m_Interpolants[i].slope[d] = 0; m_Interpolants[i].intercept[d] = c0; } } } } ColorMap::RGBAType ColorMap ::MapIndexToRGBA(double j) const { // We use simple linear search because most colormaps // are tiny and the overhead of binary search is not worth it size_t n = m_CMPoints.size(), lb; for(lb = 0; lb < n; lb++) if(j < m_CMPoints[lb].m_Index) break; // Get the interpolants const InterpolantData &ic = m_Interpolants[lb]; // Compute the output value RGBAType c; c[0] = (unsigned char)(ic.intercept[0] + ic.slope[0] * j); c[1] = (unsigned char)(ic.intercept[1] + ic.slope[1] * j); c[2] = (unsigned char)(ic.intercept[2] + ic.slope[2] * j); c[3] = (unsigned char)(ic.intercept[3] + ic.slope[3] * j); return c; } void ColorMap ::PrintSelf() { for(size_t i = 0; i < m_CMPoints.size(); i++) { CMPoint p = m_CMPoints[i]; if(p.m_Type == CONTINUOUS) { printf("%02d-C %7.2f (%03d %03d %03d %03d)\n", (int) i, p.m_Index, p.m_RGBA[0][0], p.m_RGBA[0][1], p.m_RGBA[0][2], p.m_RGBA[0][3]); } else { printf("%02d-L %7.2f (%03d %03d %03d %03d)\n", (int) i, p.m_Index, p.m_RGBA[0][0], p.m_RGBA[0][1], p.m_RGBA[0][2], p.m_RGBA[0][3]); printf("%02d-R %7.2f (%03d %03d %03d %03d)\n", (int) i, p.m_Index, p.m_RGBA[1][0], p.m_RGBA[1][1], p.m_RGBA[1][2], p.m_RGBA[1][3]); } } } void ColorMap ::SetToSystemPreset(SystemPreset preset) { m_CMPoints.clear(); m_CMPreset = preset; switch(preset) { case COLORMAP_GREY: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0xff, 0xff, 0xff, 0xff, 0x00) ); break; case COLORMAP_RED: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0xff, 0x00, 0x00, 0xff, 0x00) ); break; case COLORMAP_GREEN: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0x00, 0xff, 0x00, 0xff, 0x00) ); break; case COLORMAP_BLUE: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0x00, 0x00, 0xff, 0xff, 0x00) ); break; case COLORMAP_HOT: m_CMPoints.push_back( CMPoint(0.0 , 0x00, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint( 2.0 / 63.0, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(22.0 / 63.0, 0xd8, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(28.0 / 63.0, 0xff, 0x3a, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(48.0 / 63.0, 0xff, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0 , 0xff, 0xff, 0xff, 0xff, 0x00) ); break; case COLORMAP_COOL: m_CMPoints.push_back( CMPoint(0.0, 0xff, 0x00, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0x00, 0xff, 0xff, 0xff, 0x00) ); break; case COLORMAP_SPRING: m_CMPoints.push_back( CMPoint(0.0, 0xff, 0x00, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0xff, 0xff, 0x00, 0xff, 0x00) ); break; case COLORMAP_SUMMER: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x80, 0x66, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0xff, 0xff, 0x66, 0xff, 0x00) ); break; case COLORMAP_AUTUMN: m_CMPoints.push_back( CMPoint(0.0, 0xff, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0xff, 0xff, 0x00, 0xff, 0x00) ); break; case COLORMAP_WINTER: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x00, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0x00, 0xff, 0x80, 0xff, 0x00) ); break; case COLORMAP_COPPER: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x00, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(0.8334, 0xff, 0xaa, 0x6a, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0xff, 0xcc, 0x80, 0xff, 0x00) ); break; case COLORMAP_HSV: m_CMPoints.push_back( CMPoint(0.0, 0xff, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(0.1667, 0xff, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(0.3334, 0x00, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(0.5000, 0x00, 0xff, 0xff, 0xff) ); m_CMPoints.push_back( CMPoint(0.6667, 0x00, 0x00, 0xff, 0xff) ); m_CMPoints.push_back( CMPoint(0.8334, 0xff, 0x00, 0xff, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0xff, 0x00, 0x00, 0xff, 0x00) ); break; case COLORMAP_JET: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x00, 0x80, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(0.1 , 0x00, 0x00, 0xff, 0xff) ); m_CMPoints.push_back( CMPoint(0.36 , 0x00, 0xff, 0xff, 0xff) ); m_CMPoints.push_back( CMPoint(0.6 , 0xff, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(0.9 , 0xff, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0x80, 0x00, 0x00, 0xff, 0x00) ); break; case COLORMAP_BWR: m_CMPoints.push_back( CMPoint(0.0, 0x00, 0x00, 0xff, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(0.5, 0xff, 0xff, 0xff, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0xff, 0x00, 0x00, 0xff, 0x00) ); break; case COLORMAP_RWB: m_CMPoints.push_back( CMPoint(0.0, 0xff, 0x00, 0x00, 0x00, 0xff) ); m_CMPoints.push_back( CMPoint(0.5, 0xff, 0xff, 0xff, 0xff) ); m_CMPoints.push_back( CMPoint(1.0, 0x00, 0x00, 0xff, 0xff, 0x00) ); break; case COLORMAP_SIZE: // to suppress compiler warning std::cerr << "COLORMAP_SIZE: should never get there ..." << std::endl; break; } this->UpdateInterpolants(); } void ColorMap ::SaveToRegistry(Registry ®) { // Store the number of control points reg["NumberOfControlPoints"] << m_CMPoints.size(); RegistryEnumMap emap; emap.AddPair(CONTINUOUS,"Continuous"); emap.AddPair(DISCONTINUOUS,"Discontinuous"); // Save each control point for(size_t iPoint = 0; iPoint < m_CMPoints.size(); iPoint++) { // Get the current values, just in case CMPoint p = m_CMPoints[iPoint]; // Create a folder in the registry std::string key = reg.Key("ControlPoint[%04d]",iPoint); Registry &folder = reg.Folder(key); folder["Index"] << p.m_Index; folder["Type"].PutEnum(emap, p.m_Type); folder["Left.R"] << (int) p.m_RGBA[0][0]; folder["Left.G"] << (int) p.m_RGBA[0][1]; folder["Left.B"] << (int) p.m_RGBA[0][2]; folder["Left.A"] << (int) p.m_RGBA[0][3]; folder["Right.R"] << (int) p.m_RGBA[1][0]; folder["Right.G"] << (int) p.m_RGBA[1][1]; folder["Right.B"] << (int) p.m_RGBA[1][2]; folder["Right.A"] << (int) p.m_RGBA[1][3]; } } void ColorMap ::LoadFromRegistry(Registry ®) { // Store the number of control points size_t n = reg["NumberOfControlPoints"][0]; if(n == 0) throw IRISException("Can not read color map. No entries found"); // Read each control point CMPointList newpts; RegistryEnumMap emap; emap.AddPair(CONTINUOUS,"Continuous"); emap.AddPair(DISCONTINUOUS,"Discontinuous"); // Save each control point for(size_t iPoint = 0; iPoint < n; iPoint++) { // Get the current values, just in case CMPoint p; // Create a folder in the registry std::string key = reg.Key("ControlPoint[%04d]",iPoint); Registry &folder = reg.Folder(key); p.m_Index = folder["Index"][-1.0]; p.m_Type = folder["Type"].GetEnum(emap, CONTINUOUS); p.m_RGBA[0][0] = (unsigned char) folder["Left.R"][0]; p.m_RGBA[0][1] = (unsigned char) folder["Left.G"][0]; p.m_RGBA[0][2] = (unsigned char) folder["Left.B"][0]; p.m_RGBA[0][3] = (unsigned char) folder["Left.A"][0]; if(p.m_Type == CONTINUOUS) { p.m_RGBA[1][0] = p.m_RGBA[0][0]; p.m_RGBA[1][1] = p.m_RGBA[0][1]; p.m_RGBA[1][2] = p.m_RGBA[0][2]; p.m_RGBA[1][3] = p.m_RGBA[0][3]; } else { p.m_RGBA[1][0] = (unsigned char) folder["Right.R"][0]; p.m_RGBA[1][1] = (unsigned char) folder["Right.G"][0]; p.m_RGBA[1][2] = (unsigned char) folder["Right.B"][0]; p.m_RGBA[1][3] = (unsigned char) folder["Right.A"][0]; } // Check validity if(iPoint == 0 && p.m_Index != 0.0) throw IRISException("Can not read color map. First point has non-zero index."); if(iPoint == n-1 && p.m_Index != 1.0) throw IRISException("Can not read color map. Last point has index not equal to 1."); if(iPoint > 0 && p.m_Index < newpts[iPoint-1].m_Index) throw IRISException("Can not read color map. Indices are not stored in order."); newpts.push_back(p); } // Got this far? store the new map m_CMPoints = newpts; this->UpdateInterpolants(); } itksnap/Logic/Common/ColorMap.h0000644000076500000240000001203411553051537015723 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ColorMap.h,v $ Language: C++ Date: $Date: 2011/04/18 15:06:07 $ Version: $Revision: 1.7 $ 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 __ColorMap_h_ #define __ColorMap_h_ #include "itkRGBAPixel.h" #include "Registry.h" #include /** * \class ColorMap * \brief This class provides a representation of color maps. * A color map is a mapping from the interval [0,1] to the RGBA space. The * mapping is piecewise continuous. Where it is continuous, it is linear. * * The color map is represented as an ordered list of points. Each point has * an index value (between 0 and 1). CMPoints may be continuous (i.e., the color * map is continuous at the point) and discontinuous. A continuous point has * one RGBA value, and discontinuous points have two values. In between points, * the color map is interpolated linearly. */ class ColorMap { public: /** RGBA type */ typedef unsigned char EltType; typedef itk::RGBAPixel RGBAType; /** Type of point */ enum CMPointType { CONTINUOUS, DISCONTINUOUS }; /** System presets */ enum SystemPreset { COLORMAP_GREY = 0, COLORMAP_JET, COLORMAP_HOT, COLORMAP_COOL, COLORMAP_RED, COLORMAP_GREEN, COLORMAP_BLUE, COLORMAP_SPRING, COLORMAP_SUMMER, COLORMAP_AUTUMN, COLORMAP_WINTER, COLORMAP_COPPER, COLORMAP_HSV, COLORMAP_BWR, COLORMAP_RWB, COLORMAP_SIZE }; /** CMPoint representation */ class CMPoint { public: double m_Index; CMPointType m_Type; RGBAType m_RGBA[2]; bool operator==(const CMPoint& rhs) const; CMPoint(); // Continuous point CMPoint(double, EltType r, EltType g, EltType b, EltType a); // CMPoint with alpha discontinuity CMPoint(double, EltType r, EltType g, EltType b, EltType a1, EltType a2); // CMPoint with full discontinuity CMPoint(double, EltType r1, EltType g1, EltType b1, EltType a1, EltType r2, EltType g2, EltType b2, EltType a2); // CMPoint copy constructor CMPoint(const CMPoint& rhs); }; /** * This method maps a value to an RGBA tuple. Values less than 0 and greater * than one are accepted, and are mapped to 0 and 1, respecitively. */ RGBAType MapIndexToRGBA(double j) const; /** This method initializes the color map to one of the system presets */ void SetToSystemPreset(SystemPreset preset); size_t GetNumberOfCMPoints() { return m_CMPoints.size(); } CMPoint GetCMPoint(size_t j) { return m_CMPoints[j]; } void UpdateCMPoint(size_t j, const CMPoint &p) { m_CMPoints[j] = p; this->UpdateInterpolants(); } void DeleteCMPoint(size_t j) { m_CMPoints.erase(m_CMPoints.begin() + j); this->UpdateInterpolants(); } /** * This method inserts a new control point, which is interpolated * using the values of the existing points */ size_t InsertInterpolatedCMPoint(double j); bool operator==(const ColorMap& rhs) const; SystemPreset GetSystemPreset() const { return m_CMPreset; } ColorMap(); ColorMap (SystemPreset preset); ColorMap (const ColorMap& rhs); void PrintSelf(); void SaveToRegistry(Registry ®); void LoadFromRegistry(Registry ®); private: typedef std::vector CMPointList; typedef CMPointList::iterator CMPointIterator; typedef CMPointList::const_iterator CMPointConstIterator; // Array of points, which must always be sorted by the index CMPointList m_CMPoints; // Preset SystemPreset m_CMPreset; // Recomputes internal interpolation terms void UpdateInterpolants(); struct InterpolantData { float slope[4], intercept[4]; }; typedef std::vector InterpolantVector; InterpolantVector m_Interpolants; }; #endif itksnap/Logic/Common/CVS/0000755000076500000240000000000011560342170014463 5ustar paulystaffitksnap/Logic/Common/CVS/Entries0000644000076500000240000000165611560342170016027 0ustar paulystaff/ColorLabel.h/1.3/Mon Nov 17 19:38:23 2008// /ColorLabelTable.cxx/1.8/Mon Jun 28 18:45:08 2010// /ColorLabelTable.h/1.5/Mon Oct 26 22:18:03 2009// /ColorMap.cxx/1.12/Mon Oct 18 11:25:44 2010// /ColorMap.h/1.7/Mon Apr 18 15:06:07 2011// /ImageCoordinateGeometry.cxx/1.4/Mon Oct 12 19:05:56 2009// /ImageCoordinateGeometry.h/1.3/Sat Nov 15 12:20:38 2008// /ImageCoordinateTransform.cxx/1.3/Tue Apr 15 21:42:29 2008// /ImageCoordinateTransform.h/1.2/Sun Dec 30 04:05:13 2007// /ImageRayIntersectionFinder.h/1.3/Fri Jan 23 20:09:38 2009// /ImageRayIntersectionFinder.txx/1.4/Fri Jan 23 20:09:38 2009// /SNAPRegistryIO.cxx/1.4/Tue Jun 9 05:43:00 2009// /SNAPRegistryIO.h/1.3/Wed Jul 22 21:06:23 2009// /SNAPSegmentationROISettings.cxx/1.2/Sun Dec 30 04:05:13 2007// /SNAPSegmentationROISettings.h/1.2/Sun Dec 30 04:05:13 2007// /SegmentationStatistics.cxx/1.1/Mon May 31 19:52:37 2010// /SegmentationStatistics.h/1.2/Tue Jun 15 16:54:35 2010// D itksnap/Logic/Common/CVS/Repository0000644000076500000240000000002511560342170016562 0ustar paulystaffitksnap/Logic/Common itksnap/Logic/Common/CVS/Root0000644000076500000240000000010011560342170015320 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/Common/ImageCoordinateGeometry.cxx0000644000076500000240000001346711264677024021347 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageCoordinateGeometry.cxx,v $ Language: C++ Date: $Date: 2009/10/12 19:05:56 $ 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 "ImageCoordinateGeometry.h" ImageCoordinateGeometry ::ImageCoordinateGeometry() { // By default all transforms are identity. Nothing to do here } ImageCoordinateGeometry ::ImageCoordinateGeometry(DirectionMatrix imageDirection, std::string displayAnatomyRAICode[3], const Vector3ui &imageSize) { SetGeometry(imageDirection,displayAnatomyRAICode,imageSize); } void ImageCoordinateGeometry ::SetGeometry(DirectionMatrix imageDirection, std::string displayAnatomyRAICode[3], const Vector3ui &imageSize) { // Store the image direction matrix m_ImageDirectionCosineMatrix = imageDirection; // Remap the direction matrix to an RAI code std::string imageAnatomyRAICode = ConvertDirectionMatrixToClosestRAICode(m_ImageDirectionCosineMatrix); // We can easily compute the image to anatomy transform m_ImageToAnatomyTransform.SetTransform( ConvertRAIToCoordinateMapping(imageAnatomyRAICode.c_str()),imageSize); // Compute the size of the anatomy image in its coordinates Vector3ui szAnatomy = m_ImageToAnatomyTransform.TransformSize(imageSize); // Compute the anatomy to display transform for(unsigned int slice=0;slice < 3;slice++) { // Make sure the code is valid assert(IsRAICodeValid(displayAnatomyRAICode[slice].c_str())); m_AnatomyToDisplayTransform[slice].SetTransform( InvertMappingVector( ConvertRAIToCoordinateMapping(displayAnatomyRAICode[slice].c_str())), szAnatomy); // Compute the combined transform m_ImageToDisplayTransform[slice] = m_AnatomyToDisplayTransform[slice].Product(m_ImageToAnatomyTransform); // Compute the opposite direction transform m_DisplayToImageTransform[slice] = m_ImageToDisplayTransform[slice].Inverse(); } } bool ImageCoordinateGeometry ::IsRAICodeValid(const char *rai) { if(rai == NULL) return false; // Check the validity of the RAI code - no repetition // is allowed bool rl = false,ap = false,is = false; for (int i=0;i<3;i++) { switch (rai[i]) { case 'r' : case 'R' : case 'l' : case 'L' : rl = true;break; case 'a' : case 'A' : case 'p' : case 'P' : ap = true;break; case 'i' : case 'I' : case 's' : case 'S' : is = true;break; default: return false; } } // All three should have been encountered! return rl && ap && is; } Vector3i ImageCoordinateGeometry ::ConvertRAIToCoordinateMapping(const char *rai) { assert(IsRAICodeValid(rai)); Vector3i result; for(unsigned int i=0;i<3;i++) { switch(rai[i]) { case 'r' : case 'R' : result[i] = 1;break; case 'l' : case 'L' : result[i] = -1;break; case 'a' : case 'A' : result[i] = 2;break; case 'p' : case 'P' : result[i] = -2;break; case 'i' : case 'I' : result[i] = 3;break; case 's' : case 'S' : result[i] = -3;break; default: assert(0); } } return result; } std::string ImageCoordinateGeometry ::ConvertDirectionMatrixToClosestRAICode(DirectionMatrix mat) { // RAI codes for cardinal directions const static std::string rai_start("RAI"), rai_end("LPS"); std::string rai_out("..."); for(size_t i = 0; i < 3; i++) { // Get the direction of the i-th voxel coordinate vnl_vector dir_i = mat.get_column(i); // Get the maximum angle with any axis double maxabs_i = dir_i.inf_norm(); for(size_t off = 0; off < 3; off++) { // This trick allows us to visit (i,i) first, so that if one of the // direction cosines makes the same angle with two of the axes, we // can still assign a valid RAI code size_t j = (i + off) % 3; // Is j the best-matching direction? if(fabs(dir_i[j]) == maxabs_i) { rai_out[i] = dir_i[j] > 0 ? rai_start[j] : rai_end[j]; break; } } } return rai_out; } bool ImageCoordinateGeometry ::IsDirectionMatrixOblique(DirectionMatrix mat) { for(size_t i = 0; i < 3; i++) for(size_t j = 0; j < 3; j++) if(fabs(mat[i][j]) > 0 && fabs(mat[i][j]) < 1) return true; return false; } Vector3i ImageCoordinateGeometry ::InvertMappingVector(const Vector3i &map) { Vector3i inv; for(int i=0;i<3;i++) { if(map[i] > 0) inv[map[i]-1] = i+1; else inv[-1-map[i]] = -i-1; } return inv; } itksnap/Logic/Common/ImageCoordinateGeometry.h0000644000076500000240000001054111107537226020755 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageCoordinateGeometry.h,v $ Language: C++ Date: $Date: 2008/11/15 12:20: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. =========================================================================*/ #ifndef __ImageCoordinateGeometry_h_ #define __ImageCoordinateGeometry_h_ #include "ImageCoordinateTransform.h" /** * \class ImageCoordinateGeometry * \brief This class describes the geometric relationship between the image * coordinate system, the patient coordinate system, and the display coordinate * system. */ class ImageCoordinateGeometry { public: typedef vnl_matrix DirectionMatrix; /** Constructor initializes geometry with default identity transforms */ ImageCoordinateGeometry(); /** Constructor that calls GetGeometry */ ImageCoordinateGeometry(DirectionMatrix imageDirection, std::string displayAnatomyRAICode[3], const Vector3ui &imageSize); /** Virtual destructor */ virtual ~ImageCoordinateGeometry() {} /** Initializes geometry with tranforms specified by character * RAI codes for the image-anatomy and display-anatomy transforms and the * image size */ void SetGeometry(DirectionMatrix imageDirection, std::string displayAnatomyRAICode[3], const Vector3ui &imageSize); /** Get the transform from image to patient coordinate system */ irisGetMacro(ImageToAnatomyTransform,const ImageCoordinateTransform &); /** Get the image to anatomy direction matrix */ irisGetMacro(ImageDirectionCosineMatrix, DirectionMatrix); /** Get the transform from patient to display coordinate system */ const ImageCoordinateTransform & GetAnatomyToDisplayTransform(unsigned int i) { return m_AnatomyToDisplayTransform[i]; } /** Get the transform from image to display coordinate system */ const ImageCoordinateTransform & GetImageToDisplayTransform(unsigned int i) { return m_ImageToDisplayTransform[i]; } /** Get the transform from display to image coordinate system */ const ImageCoordinateTransform & GetDisplayToImageTransform(unsigned int i) { return m_DisplayToImageTransform[i]; } /** Check an RAI orientation code for validity */ static bool IsRAICodeValid(const char *code); /** Map an RAI code to a mapping vector of positive or negative 1,2,3 */ static Vector3i ConvertRAIToCoordinateMapping(const char *code); /** Map ITK direction cosines matrix to the closest RAI code */ static std::string ConvertDirectionMatrixToClosestRAICode(DirectionMatrix mat); /** Check if the direction cosines are oblique (not parallel to coord system) */ static bool IsDirectionMatrixOblique(DirectionMatrix mat); /** Invert a mapping vector of 1,2,3 */ static Vector3i InvertMappingVector(const Vector3i &mapping); private: // Slice transform information ImageCoordinateTransform m_ImageToAnatomyTransform; ImageCoordinateTransform m_AnatomyToDisplayTransform[3]; // Three anatomy to display transforms (for each of the 3D slices) ImageCoordinateTransform m_ImageToDisplayTransform[3]; ImageCoordinateTransform m_DisplayToImageTransform[3]; // Image to anatomy direction matrix DirectionMatrix m_ImageDirectionCosineMatrix; }; #endif itksnap/Logic/Common/ImageCoordinateTransform.cxx0000644000076500000240000001310311001220505021463 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageCoordinateTransform.cxx,v $ Language: C++ Date: $Date: 2008/04/15 21:42:29 $ 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. =========================================================================*/ #include "ImageCoordinateTransform.h" #include "vnl/vnl_inverse.h" #include #include #include Vector3ui IRISSNAPdummyVector; iris_vector_fixed irissnapdummyvector = to_float(IRISSNAPdummyVector); ImageCoordinateTransform ::ImageCoordinateTransform() { // Transform is the identity m_Transform.fill(0.0f); m_Transform(0,0) = m_Transform(1,1) = m_Transform(2,2) = 1.0f; // The offset is zero m_Offset.fill(0.0f); ComputeSecondaryVectors(); } void ImageCoordinateTransform ::SetTransform(const Vector3i &map, const Vector3ui &size) { unsigned int i; // Make sure it's a legal mapping for(i=0;i<3;i++) { assert(abs(map[i]) <= 3 && abs(map[i]) > 0); assert(abs(map[i]) != abs(map[(i+1) % 3])); } // Initialize the transform matrix m_Transform.fill(0); for(i=0;i<3;i++) { if(map[i] > 0) m_Transform(map[i]-1,i) = 1.0; else m_Transform(-1-map[i],i) = -1.0; } // Initialize the offset vector m_Offset = m_Transform * to_float(size); for(i=0;i<3;i++) { // Update the offset vector to make it right m_Offset[i] = m_Offset[i] < 0 ? - m_Offset[i] : 0; } ComputeSecondaryVectors(); } void ImageCoordinateTransform ::ComputeSecondaryVectors() { // For this calculation we need the transpose of the matrix MatrixType T = m_Transform.transpose(); Vector3f map = T * Vector3f(0.0f,1.0f,2.0f); m_AxesIndex[0] = (unsigned int) fabs(map[0]); m_AxesIndex[1] = (unsigned int) fabs(map[1]); m_AxesIndex[2] = (unsigned int) fabs(map[2]); m_AxesDirection = to_int(T * Vector3f(1.0f)); } ImageCoordinateTransform ImageCoordinateTransform ::Inverse() const { // Compute the new transform's details ImageCoordinateTransform inv; inv.m_Transform = vnl_inverse(m_Transform); inv.m_Offset = - inv.m_Transform * m_Offset; // Compute additional quantities inv.ComputeSecondaryVectors(); /* // Compute the transpose of the transform int newMapping[3]; for(int i=0;i<3;i++) { if(m_Mapping[i] > 0) newMapping[m_Mapping[i]-1] = i+1; else newMapping[-1-m_Mapping[i]] = -i-1; } return ImageCoordinateTransform(newMapping[0],newMapping[1],newMapping[2]); */ return inv; } ImageCoordinateTransform ImageCoordinateTransform ::Product(const ImageCoordinateTransform &t1) const { // Compute the new transform's details ImageCoordinateTransform prod; prod.m_Transform = m_Transform * t1.m_Transform; prod.m_Offset = m_Transform * t1.m_Offset + m_Offset; // Compute additional quantities prod.ComputeSecondaryVectors(); /* // Multiply two transforms int newMapping[3]; for(int i=0;i<3;i++) { if(m_Mapping[i] > 0) newMapping[i] = t1.m_Mapping[m_Mapping[i]-1]; else newMapping[i] = -t1.m_Mapping[-1-m_Mapping[i]]; } return ImageCoordinateTransform(newMapping[0],newMapping[1],newMapping[2]); */ return prod; } Vector3f ImageCoordinateTransform ::TransformPoint(const Vector3f &x) const { return m_Transform * x + m_Offset; /* // Transform the vector Vector3f xVec = TransformVector(x); // Transform the size Vector3f xSpace = TransformVector(xSourceVolumeSize); // Compute an offset to add to the vector if(xVec[0] < 0) xVec[0] -= xSpace[0]; if(xVec[1] < 0) xVec[1] -= xSpace[1]; if(xVec[2] < 0) xVec[2] -= xSpace[2]; // Return the result return xVec; */ } Vector3f ImageCoordinateTransform ::TransformVector(const Vector3f &x) const { return m_Transform * x; } Vector3ui ImageCoordinateTransform ::TransformSize(const Vector3ui &sz) const { Vector3f szSigned = m_Transform * to_float(sz); return Vector3ui( (unsigned int)fabs(szSigned(0)), (unsigned int)fabs(szSigned(1)), (unsigned int)fabs(szSigned(2))); } Vector3ui ImageCoordinateTransform ::TransformVoxelIndex(const Vector3ui &x) const { // Convert to voxel center coordinates Vector3f xVoxelCenter = to_float(x) + Vector3f(0.5); // Make sure the mapping is legit Vector3f xMappedCenter = TransformPoint(xVoxelCenter); assert(xMappedCenter(0) >= 0.0f && xMappedCenter(1) >= 0.0f && xMappedCenter(2) >= 0.0f); // Compute the result and scale down to integers return to_unsigned_int(xMappedCenter); } itksnap/Logic/Common/ImageCoordinateTransform.h0000644000076500000240000000754310735614371021150 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageCoordinateTransform.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:13 $ 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 __ImageCoordinateTransform_h_ #define __ImageCoordinateTransform_h_ #include "SNAPCommon.h" #include /** * \class ImageCoordinateTransform * * This transform captures the coordinate renaming and change of sense * that occurs between the image coordinates, the anatomical coordinates * and the image coordinates. * * This transform can be visualized as a coordinate frame attached to any * of the eight corners of an image volume. This transform is a combination * of rotations by angles dividible by 90 degrees, multiplucations by -1 or 1 * and translation by the image size * * In order to define one of these transforms, we need to pass in a coordinate * mapping vector (example: (-1,3,-2) means x maps to -x, y to z and z to -y), * as well as the size of the image volume. */ class ImageCoordinateTransform { public: /** Default constructor creates an identity transform */ ImageCoordinateTransform(); /** * Initialize the transform with new singed coordinate mappings * (1-based signed indices) */ void SetTransform(const Vector3i &map, const Vector3ui &size); /** Compute the inverse of this transform */ ImageCoordinateTransform Inverse() const; /** Multiply by another transform */ ImageCoordinateTransform Product(const ImageCoordinateTransform &t1) const; /** Apply transform to a vector */ Vector3f TransformVector(const Vector3f &x) const; /** Apply transform to a point. */ Vector3f TransformPoint(const Vector3f &x) const; /** Apply to an integer voxel index */ Vector3ui TransformVoxelIndex(const Vector3ui &xVoxel) const; /** Apply to a size vector */ Vector3ui TransformSize(const Vector3ui &xSize) const; /** Get the index of a particular coordinate */ unsigned int GetCoordinateIndexZeroBased(unsigned int c) const { return m_AxesIndex[c]; } /** Get the orientation of a particular coordinate (returns 1 or -1) */ int GetCoordinateOrientation(unsigned int c) const { return m_AxesDirection[c]; } private: typedef vnl_matrix_fixed MatrixType; // A transform matrix MatrixType m_Transform; // An offset vector Vector3f m_Offset; // The operation abs(i) - 1 applied to m_Mapping Vector3i m_AxesIndex; // The operation sign(i) applied to m_Mapping Vector3i m_AxesDirection; // Compute the internal vectors once the matrix and offset have been computed void ComputeSecondaryVectors(); }; #endif itksnap/Logic/Common/ImageRayIntersectionFinder.h0000644000076500000240000000515011136422002021407 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageRayIntersectionFinder.h,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. =========================================================================*/ #ifndef __ImageRayIntersectionFinder_h_ #define __ImageRayIntersectionFinder_h_ #include "SNAPCommon.h" #include /** * \class ImageRayIntersectionFinder * \brief An algorithm for testing ray hits against arbitrary images. * This algorithm traverses a ray until it finds a pixel that satisfies the * hit tester (a functor with operator () which returns 0 for no-hit and * 1 for hit). */ template class ImageRayIntersectionFinder { public: virtual ~ImageRayIntersectionFinder() {} /** Image type */ typedef itk::OrientedImage ImageType; /** Set the hit-test functor to evaluate for hits */ irisSetMacro(HitTester,THitTester); /** * Compute the intersection (index of the first pixel in the * image that the ray crosses and which satisfies the THitTester's * condition. * * Returns: 1 on success, 0 on no hit and -1 if the ray misses the * image completely. */ int FindIntersection(ImageType *image,Vector3d xRayStart, Vector3d xRayVector,Vector3i &xHitIndex) const; private: /** The hit tester used internally */ THitTester m_HitTester; }; #ifndef ITK_MANUAL_INSTANTIATION #include "ImageRayIntersectionFinder.txx" #endif #endif itksnap/Logic/Common/ImageRayIntersectionFinder.txx0000644000076500000240000001217311136422002022006 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageRayIntersectionFinder.txx,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ 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 "itkOrientedImage.h" template int ImageRayIntersectionFinder ::FindIntersection(ImageType *image,Vector3d point, Vector3d ray,Vector3i &hit) const { typename ImageType::IndexType lIndex; typename ImageType::SizeType size = image->GetLargestPossibleRegion().GetSize(); double delta[3][3] = {{0.,0.,0.},{0.,0.,0.},{0.,0.,0.}}, dratio[3]={0.,0.,0.}; int signrx, signry, signrz; double rayLen = ray.two_norm(); if(rayLen == 0) return -1; ray /= rayLen; double rx = ray[0];double ry = ray[1];double rz = ray[2]; if (rx >=0) signrx = 1; else signrx = -1; if (ry >=0) signry = 1; else signry = -1; if (rz >=0) signrz = 1; else signrz = -1; // offset everything by (.5, .5) [becuz samples are at center of voxels] // this offset will put borders of voxels at integer values // we will work with this offset grid and offset back to check samples // we really only need to offset "point" double px = point[0]+0.5; double py = point[1]+0.5; double pz = point[2]+0.5; // get the starting point within data extents int c = 0; while ( (px < 0 || px >= size[0]|| py < 0 || py >= size[1]|| pz < 0 || pz >= size[2]) && c < 10000) { px += rx; py += ry; pz += rz; c++; } if (c >= 9999) return -1; // walk along ray to find intersection with any voxel with val > 0 while ( (px >= 0 && px < size[0]&& py >= 0 && py < size[1] && pz >= 0 && pz < size[2]) ) { // offset point by (-.5, -.5) [to account for earlier offset] and // get the nearest sample voxel within unit cube around (px,py,pz) // lx = my_round(px-0.5); // ly = my_round(py-0.5); // lz = my_round(pz-0.5); lIndex[0] = (int)px; lIndex[1] = (int)py; lIndex[2] = (int)pz; // Get the pixel TPixel hitPixel = image->GetPixel(lIndex); // Test if the pixel is a hit if(m_HitTester(hitPixel)) { hit[0] = lIndex[0]; hit[1] = lIndex[1]; hit[2] = lIndex[2]; return 1; } // BEGIN : walk along ray to border of next voxel touched by ray // compute path to YZ-plane surface of next voxel if (rx == 0) { // ray is parallel to 0 axis delta[0][0] = 9999; } else { delta[0][0] = (int)(px+signrx) - px; } // compute path to XZ-plane surface of next voxel if (ry == 0) { // ray is parallel to 1 axis delta[1][0] = 9999; } else { delta[1][1] = (int)(py+signry) - py; dratio[1] = delta[1][1]/ry; delta[1][0] = dratio[1] * rx; } // compute path to XY-plane surface of next voxel if (rz == 0) { // ray is parallel to 2 axis delta[2][0] = 9999; } else { delta[2][2] = (int)(pz+signrz) - pz; dratio[2] = delta[2][2]/rz; delta[2][0] = dratio[2] * rx; } // choose the shortest path if ( fabs(delta[0][0]) <= fabs(delta[1][0]) && fabs(delta[0][0]) <= fabs(delta[2][0]) ) { dratio[0] = delta[0][0]/rx; delta[0][1] = dratio[0] * ry; delta[0][2] = dratio[0] * rz; px += delta[0][0]; py += delta[0][1]; pz += delta[0][2]; } else if ( fabs(delta[1][0]) <= fabs(delta[0][0]) && fabs(delta[1][0]) <= fabs(delta[2][0]) ) { delta[1][2] = dratio[1] * rz; px += delta[1][0]; py += delta[1][1]; pz += delta[1][2]; } else { //if (fabs(delta[2][0] <= fabs(delta[0][0] && fabs(delta[2][0] <= fabs(delta[0][0]) delta[2][1] = dratio[2] * ry; px += delta[2][0]; py += delta[2][1]; pz += delta[2][2]; } // END : walk along ray to border of next voxel touched by ray } // while ( (px return 0; } itksnap/Logic/Common/SegmentationStatistics.cxx0000644000076500000240000001335511401011605021261 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SegmentationStatistics.cxx,v $ Language: C++ Date: $Date: 2010/05/31 19:52:37 $ Version: $Revision: 1.1 $ 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 "SegmentationStatistics.h" #include "GenericImageData.h" using namespace std; struct SegmentationStatisticsSource { string name; GreyImageWrapper *image; GreyImageWrapper::ConstIterator it; GreyTypeToNativeFunctor funk; }; void SegmentationStatistics ::Compute(GenericImageData *id) { // A list of image sources vector isrc; // Populate image sources if(id->IsGreyLoaded()) { SegmentationStatisticsSource src; src.name = "image"; src.image = id->GetGrey(); src.it = src.image->GetImageConstIterator(); src.funk = src.image->GetNativeMapping(); isrc.push_back(src); } // Add all grey overlays size_t k = 1; GenericImageData::WrapperIterator it = id->GetOverlays()->begin(); for(; it != id->GetOverlays()->end(); it++, k++) { // Is it a grey wrapper? GreyImageWrapper *wrapper = dynamic_cast(*it); if (wrapper) { SegmentationStatisticsSource src; ostringstream oss; oss << "ovl " << k; src.name = oss.str(); src.image = wrapper; src.it = src.image->GetImageConstIterator(); src.funk = src.image->GetNativeMapping(); isrc.push_back(src); } } // Get the number of gray image layers size_t ngray = isrc.size(); // Clear and initialize the statistics table for(size_t i = 0; i < MAX_COLOR_LABELS; i++) { m_Stats[i] = Entry(); for(size_t j = 0; j < ngray; j++) { GrayStats gs; gs.layer_id = isrc[j].name; m_Stats[i].gray.push_back(gs); } } // Compute the statistics by iterating over each voxel for(LabelImageWrapper::ConstIterator itLabel = id->GetSegmentation()->GetImageConstIterator(); !itLabel.IsAtEnd(); ++itLabel) { // Get the label and the corresponding entry LabelType label = itLabel.Value(); Entry &entry = m_Stats[label]; // Increment number of voxels for this label entry.count++; // Update the gray statistics for(size_t j = 0; j < ngray; j++) { double v = isrc[j].funk( isrc[j].it.Value() ); ++isrc[j].it; entry.gray[j].sum += v; entry.gray[j].sumsq += v * v; } } // Compute the size of a voxel, in mm^3 const double *spacing = id->GetMain()->GetImageBase()->GetSpacing().GetDataPointer(); double volVoxel = spacing[0] * spacing[1] * spacing[2]; // Compute the mean and standard deviation for (size_t i=0; i < MAX_COLOR_LABELS; i++) { Entry &entry = m_Stats[i]; for(size_t j = 0; j < ngray; j++) { entry.gray[j].mean = entry.gray[j].sum / entry.count; entry.gray[j].sd = sqrt( (entry.gray[j].sumsq - entry.gray[j].sum * entry.gray[j].mean) / (entry.count - 1)); } entry.volume_mm3 = entry.count * volVoxel; } } void SegmentationStatistics ::ExportLegacy(ostream &fout, const ColorLabelTable &clt) { // Write voxel volumes to the file fout << "##########################################################" << std::endl; fout << "# SNAP Voxel Count File" << std::endl; fout << "# File format:" << std::endl; fout << "# LABEL: ID / NUMBER / VOLUME / MEAN / SD" << std::endl; fout << "# Fields:" << std::endl; fout << "# LABEL Label description" << std::endl; fout << "# ID The numerical id of the label" << std::endl; fout << "# NUMBER Number of voxels that have that label " << std::endl; fout << "# VOLUME Volume of those voxels in cubic mm " << std::endl; fout << "# MEAN Mean intensity of those voxels " << std::endl; fout << "# SD Standard deviation of those voxels " << std::endl; fout << "##########################################################" << std::endl; for (size_t i=1; i 0) { fout << std::left << std::setw(40) << cl.GetLabel() << ": "; fout << std::right << std::setw(4) << i << " / "; fout << std::right << std::setw(10) << m_Stats[i].count << " / "; fout << std::setw(10) << (m_Stats[i].volume_mm3); for(size_t j = 0; j < m_Stats[i].gray.size(); j++) { fout << " / " << std::internal << std::setw(10) << m_Stats[i].gray[j].mean << " / " << std::setw(10) << m_Stats[i].gray[j].sd; } fout << endl; } } } itksnap/Logic/Common/SegmentationStatistics.h0000644000076500000240000000472411405730113020714 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SegmentationStatistics.h,v $ Language: C++ Date: $Date: 2010/06/15 16:54:35 $ 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 __SegmentationStatistics_h_ #define __SegmentationStatistics_h_ #include "SNAPCommon.h" #include #include #include class GenericImageData; class ColorLabelTable; class SegmentationStatistics { public: /* Data structure cooresponding to a gray overlay image */ struct GrayStats { std::string layer_id; double sum, sumsq, mean, sd; GrayStats() : sum(0), sumsq(0), mean(0), sd() {} }; /* Data structure corresponding to a row in the statistics table */ struct Entry { unsigned long int count; double volume_mm3; std::vector gray; Entry() : count(0),volume_mm3(0) {} }; /* Compute statistics from a segmentation image */ void Compute(GenericImageData *id); /* Export to a text file using legacy format */ void ExportLegacy(std::ostream &oss, const ColorLabelTable &clt); /* Export to a text file as a formatted table */ void ExportText(std::ostream &oss, const ColorLabelTable &clt); const Entry * GetStats() const { return m_Stats; } private: // Label statistics Entry m_Stats[MAX_COLOR_LABELS]; }; #endif itksnap/Logic/Common/SNAPRegistryIO.cxx0000644000076500000240000004376311213373144017313 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPRegistryIO.cxx,v $ Language: C++ Date: $Date: 2009/06/09 05:43:00 $ 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 "IRISVectorTypes.h" #include "SNAPRegistryIO.h" #include "IRISApplication.h" #include "IRISImageData.h" #include #include #include #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif using namespace std; SNAPRegistryIO ::SNAPRegistryIO() { // Set up the enum objects m_EnumMapCoverage.AddPair(PAINT_OVER_ALL,"OverAll"); m_EnumMapCoverage.AddPair(PAINT_OVER_COLORS,"OverVisible"); m_EnumMapCoverage.AddPair(PAINT_OVER_ONE,"OverOne"); m_EnumMapSolver.AddPair(SnakeParameters::DENSE_SOLVER,"Dense"); m_EnumMapSolver.AddPair(SnakeParameters::LEGACY_SOLVER,"Legacy"); m_EnumMapSolver.AddPair(SnakeParameters::SPARSE_FIELD_SOLVER,"SparseField"); m_EnumMapSolver.AddPair(SnakeParameters::NARROW_BAND_SOLVER,"NarrowBand"); m_EnumMapSolver.AddPair(SnakeParameters::PARALLEL_SPARSE_FIELD_SOLVER, "ParallelSparseField"); m_EnumMapSnakeType.AddPair(SnakeParameters::EDGE_SNAKE,"EdgeStopping"); m_EnumMapSnakeType.AddPair(SnakeParameters::REGION_SNAKE,"RegionCompetition"); m_EnumMapROI.AddPair(SNAPSegmentationROISettings::NEAREST_NEIGHBOR,"Nearest"); m_EnumMapROI.AddPair(SNAPSegmentationROISettings::TRILINEAR,"TriLinear"); m_EnumMapROI.AddPair(SNAPSegmentationROISettings::TRICUBIC,"Cubic"); m_EnumMapROI.AddPair(SNAPSegmentationROISettings::SINC_WINDOW_05,"Sinc05"); } /** Read snake parameters from a registry */ SnakeParameters SNAPRegistryIO ::ReadSnakeParameters( Registry ®istry, const SnakeParameters &defaultSet) { SnakeParameters out; out.SetAutomaticTimeStep( registry["AutomaticTimeStep"][defaultSet.GetAutomaticTimeStep()]); out.SetTimeStepFactor( registry["TimeStepFactor"][defaultSet.GetTimeStepFactor()]); out.SetGround( registry["Ground"][defaultSet.GetGround()]); out.SetClamp( registry["Clamp"][defaultSet.GetClamp()]); out.SetPropagationWeight( registry["PropagationWeight"][defaultSet.GetPropagationWeight()]); out.SetPropagationSpeedExponent( registry["PropagationSpeedExponent"][defaultSet.GetPropagationSpeedExponent()]); out.SetCurvatureWeight( registry["CurvatureWeight"][defaultSet.GetCurvatureWeight()]); out.SetCurvatureSpeedExponent( registry["CurvatureSpeedExponent"][defaultSet.GetCurvatureSpeedExponent()]); out.SetLaplacianWeight( registry["LaplacianWeight"][defaultSet.GetLaplacianWeight()]); out.SetLaplacianSpeedExponent( registry["LaplacianSpeedExponent"][defaultSet.GetLaplacianSpeedExponent()]); out.SetAdvectionWeight( registry["AdvectionWeight"][defaultSet.GetAdvectionWeight()]); out.SetAdvectionSpeedExponent( registry["AdvectionSpeedExponent"][defaultSet.GetAdvectionSpeedExponent()]); out.SetSnakeType( registry["SnakeType"].GetEnum( m_EnumMapSnakeType,defaultSet.GetSnakeType())); out.SetSolver( registry["SolverAlgorithm"].GetEnum( m_EnumMapSolver,defaultSet.GetSolver())); return out; } /** Write snake parameters to a registry */ void SNAPRegistryIO ::WriteSnakeParameters(const SnakeParameters &in,Registry ®istry) { registry["AutomaticTimeStep"] << in.GetAutomaticTimeStep(); registry["TimeStepFactor"] << in.GetTimeStepFactor(); registry["Ground"] << in.GetGround(); registry["Clamp"] << in.GetClamp(); registry["PropagationWeight"] << in.GetPropagationWeight(); registry["PropagationSpeedExponent"][in.GetPropagationSpeedExponent()]; registry["CurvatureWeight"] << in.GetCurvatureWeight(); registry["CurvatureSpeedExponent"] << in.GetCurvatureSpeedExponent(); registry["LaplacianWeight"] << in.GetLaplacianWeight(); registry["LaplacianSpeedExponent"] << in.GetLaplacianSpeedExponent(); registry["AdvectionWeight"] << in.GetAdvectionWeight(); registry["AdvectionSpeedExponent"] << in.GetAdvectionSpeedExponent(); registry["SnakeType"].PutEnum(m_EnumMapSnakeType,in.GetSnakeType()); registry["SolverAlgorithm"].PutEnum(m_EnumMapSolver,in.GetSolver()); } /** Read mesh options from a registry */ MeshOptions SNAPRegistryIO ::ReadMeshOptions( Registry ®istry, const MeshOptions &defaultSet) { MeshOptions out; out.SetUseGaussianSmoothing( registry["UseGaussianSmoothing"][defaultSet.GetUseGaussianSmoothing()]); out.SetUseDecimation( registry["UseDecimation"][defaultSet.GetUseDecimation()]); out.SetUseMeshSmoothing( registry["UseMeshSmoothing"][defaultSet.GetUseMeshSmoothing()]); out.SetGaussianStandardDeviation( registry["GaussianStandardDeviation"][defaultSet.GetGaussianStandardDeviation()]); out.SetGaussianError( registry["GaussianError"][defaultSet.GetGaussianError()]); out.SetDecimateTargetReduction( registry["DecimateTargetReduction"][defaultSet.GetDecimateTargetReduction()]); out.SetDecimateInitialError( registry["DecimateInitialError"][defaultSet.GetDecimateInitialError()]); out.SetDecimateAspectRatio( registry["DecimateAspectRatio"][defaultSet.GetDecimateAspectRatio()]); out.SetDecimateFeatureAngle( registry["DecimateFeatureAngle"][defaultSet.GetDecimateFeatureAngle()]); out.SetDecimateErrorIncrement( registry["DecimateErrorIncrement"][defaultSet.GetDecimateErrorIncrement()]); out.SetDecimateMaximumIterations( registry["DecimateMaximumIterations"][defaultSet.GetDecimateMaximumIterations()]); out.SetDecimatePreserveTopology( registry["DecimatePreserveTopology"][defaultSet.GetDecimatePreserveTopology()]); out.SetMeshSmoothingRelaxationFactor( registry["MeshSmoothingRelaxationFactor"][defaultSet.GetMeshSmoothingRelaxationFactor()]); out.SetMeshSmoothingIterations( registry["MeshSmoothingIterations"][defaultSet.GetMeshSmoothingIterations()]); out.SetMeshSmoothingConvergence( registry["MeshSmoothingConvergence"][defaultSet.GetMeshSmoothingConvergence()]); out.SetMeshSmoothingFeatureAngle( registry["MeshSmoothingFeatureAngle"][defaultSet.GetMeshSmoothingFeatureAngle()]); out.SetMeshSmoothingFeatureEdgeSmoothing( registry["MeshSmoothingFeatureEdgeSmoothing"][defaultSet.GetMeshSmoothingFeatureEdgeSmoothing()]); out.SetMeshSmoothingBoundarySmoothing( registry["MeshSmoothingBoundarySmoothing"][defaultSet.GetMeshSmoothingBoundarySmoothing()]); return out; } /** Write mesh options to a registry */ void SNAPRegistryIO ::WriteMeshOptions(const MeshOptions &in,Registry ®istry) { registry["UseGaussianSmoothing"] << in.GetUseGaussianSmoothing(); registry["UseDecimation"] << in.GetUseDecimation(); registry["UseMeshSmoothing"] << in.GetUseMeshSmoothing(); registry["GaussianStandardDeviation"] << in.GetGaussianStandardDeviation(); registry["GaussianError"] << in.GetGaussianError(); registry["DecimateTargetReduction"] << in.GetDecimateTargetReduction(); registry["DecimateInitialError"] << in.GetDecimateInitialError(); registry["DecimateAspectRatio"] << in.GetDecimateAspectRatio(); registry["DecimateFeatureAngle"] << in.GetDecimateFeatureAngle(); registry["DecimateErrorIncrement"] << in.GetDecimateErrorIncrement(); registry["DecimateMaximumIterations"] << in.GetDecimateMaximumIterations(); registry["DecimatePreserveTopology"] << in.GetDecimatePreserveTopology(); registry["MeshSmoothingRelaxationFactor"] << in.GetMeshSmoothingRelaxationFactor(); registry["MeshSmoothingIterations"] << in.GetMeshSmoothingIterations(); registry["MeshSmoothingConvergence"] << in.GetMeshSmoothingConvergence(); registry["MeshSmoothingFeatureAngle"] << in.GetMeshSmoothingFeatureAngle(); registry["MeshSmoothingFeatureEdgeSmoothing"] << in.GetMeshSmoothingFeatureEdgeSmoothing(); registry["MeshSmoothingBoundarySmoothing"] << in.GetMeshSmoothingBoundarySmoothing(); } /** Read edge preprocessing settings from a registry */ EdgePreprocessingSettings SNAPRegistryIO ::ReadEdgePreprocessingSettings( Registry ®istry, const EdgePreprocessingSettings &defaultSet) { EdgePreprocessingSettings out; out.SetGaussianBlurScale( registry["GaussianBlurScale"][defaultSet.GetGaussianBlurScale()]); out.SetRemappingSteepness( registry["RemappingSteepness"][defaultSet.GetRemappingSteepness()]); out.SetRemappingExponent( registry["RemappingExponent"][defaultSet.GetRemappingExponent()]); return out; } /** Write edge preprocessing settings to a registry */ void SNAPRegistryIO ::WriteEdgePreprocessingSettings(const EdgePreprocessingSettings &in, Registry ®istry) { registry["GaussianBlurScale"] << in.GetGaussianBlurScale(); registry["RemappingSteepness"] << in.GetRemappingSteepness(); registry["RemappingExponent"] << in.GetRemappingExponent(); } /** Read threshold settings from a registry */ ThresholdSettings SNAPRegistryIO ::ReadThresholdSettings( Registry ®istry, const ThresholdSettings &defaultSet) { ThresholdSettings out; out.SetLowerThreshold( registry["LowerThreshold"][defaultSet.GetLowerThreshold()]); out.SetUpperThreshold( registry["UpperThreshold"][defaultSet.GetUpperThreshold()]); out.SetSmoothness( registry["Smoothness"][defaultSet.GetSmoothness()]); out.SetLowerThresholdEnabled( registry["LowerThresholdEnabled"][defaultSet.IsLowerThresholdEnabled()]); out.SetUpperThresholdEnabled( registry["UpperThresholdEnabled"][defaultSet.IsUpperThresholdEnabled()]); // Check that what we've read is valid if(!out.IsValid()) out = defaultSet; return out; } /** Write threshold settings to a registry */ void SNAPRegistryIO ::WriteThresholdSettings(const ThresholdSettings &in,Registry ®istry) { registry["LowerThreshold"] << in.GetLowerThreshold(); registry["UpperThreshold"] << in.GetUpperThreshold(); registry["Smoothness"] << in.GetSmoothness(); registry["LowerThresholdEnabled"] << in.IsLowerThresholdEnabled(); registry["UpperThresholdEnabled"] << in.IsUpperThresholdEnabled(); } /** Write region of interest settings to a registry folder */ void SNAPRegistryIO ::WriteSegmentationROISettings( const SNAPSegmentationROISettings &in, Registry &folder) { folder["ResampleFlag"] << in.GetResampleFlag(); folder["VoxelScale"] << in.GetVoxelScale(); for(unsigned int d = 0; d < 3; d++) { Registry &sub = folder.Folder(folder.Key("ROIBox[%d]",d)); sub["Index"] << in.GetROI().GetIndex(d); sub["Size"] << in.GetROI().GetSize(d); } folder["InterpolationMethod"].PutEnum(m_EnumMapROI,in.GetInterpolationMethod()); } SNAPSegmentationROISettings SNAPRegistryIO ::ReadSegmentationROISettings( Registry &folder, const SNAPSegmentationROISettings &dfl) { SNAPSegmentationROISettings out; // Read resampling properties out.SetResampleFlag(folder["ResampleFlag"][dfl.GetResampleFlag()]); out.SetVoxelScale(folder["VoxelScale"][dfl.GetVoxelScale()]); out.SetInterpolationMethod( folder["InterpolationMethod"].GetEnum( m_EnumMapROI, dfl.GetInterpolationMethod())); // Read in the bounding box information itk::ImageRegion<3> dflRegion = dfl.GetROI(); itk::ImageRegion<3> outRegion; for(unsigned int d = 0; d < 3; d++) { Registry &sub = folder.Folder(folder.Key("ROIBox[%d]",d)); outRegion.SetIndex(d, sub["Index"][(int)dflRegion.GetIndex(d)]); outRegion.SetSize(d, sub["Size"][(int) dflRegion.GetSize(d)]); } out.SetROI(outRegion); return out; } void SNAPRegistryIO ::WriteImageAssociatedSettings(IRISApplication *app, Registry ®istry) { // Get the global state for this object GlobalState *gs = app->GetGlobalState(); // Write snake parameters WriteSnakeParameters( gs->GetSnakeParameters(), registry.Folder("SNAP.SnakeParameters")); // Write the preprocessing settings WriteEdgePreprocessingSettings( gs->GetEdgePreprocessingSettings(), registry.Folder("SNAP.Preprocessing.Edge")); WriteThresholdSettings( gs->GetThresholdSettings(), registry.Folder("SNAP.Preprocessing.Region")); // Write the mesh display options WriteMeshOptions( gs->GetMeshOptions(), registry.Folder("IRIS.MeshOptions")); // Save the intensity mapping curve if (app->GetIRISImageData()->IsGreyLoaded()) { app->GetCurrentImageData()->GetGrey()->GetIntensityMapFunction()-> SaveToRegistry(registry.Folder("IRIS.IntensityCurve")); } // Write file related information registry["Files.Segmentation.FileName"] << gs->GetLastAssociatedSegmentationFileName(); registry["Files.Preprocessing.FileName"] << gs->GetLastAssociatedPreprocessingFileName(); registry["Files.Grey.Orientation"] << app->GetImageToAnatomyRAI(); registry["Files.Grey.Dimensions"] << to_int(app->GetIRISImageData()->GetVolumeExtents()); // Write information about the current label state registry["IRIS.LabelState.DrawingLabel"] << (int) gs->GetDrawingColorLabel(); registry["IRIS.LabelState.OverwriteLabel"] << (int) gs->GetOverWriteColorLabel(); registry["IRIS.LabelState.CoverageMode"].PutEnum( m_EnumMapCoverage,gs->GetCoverageMode()); registry["IRIS.LabelState.PolygonInvert"] << gs->GetPolygonInvert(); registry["IRIS.LabelState.OverallAlpha"] << (int) gs->GetSegmentationAlpha(); // Write the information about the bounding box and ROI sub-sampling WriteSegmentationROISettings( gs->GetSegmentationROISettings(), registry.Folder("IRIS.BoundingBox")); // Write the color label table app->GetColorLabelTable()->SaveToRegistry(registry.Folder("IRIS.LabelTable")); } bool SNAPRegistryIO ::ReadImageAssociatedSettings( Registry ®istry, IRISApplication *app, bool restoreLabels, bool restorePreprocessing, bool restoreParameters, bool restoreDisplayOptions) { // Get a pointer to the global state GlobalState *gs = app->GetGlobalState(); // First of all, make sure that the image referred to in the association file // matches the image currently loaded Vector3i xxx=iris_vector_fixed(7); Vector3i dims = (registry["Files.Grey.Dimensions"])[Vector3i(0)]; if(dims != to_int(app->GetIRISImageData()->GetVolumeExtents())) return false; // Read the snake parameters (TODO: expand the parameters to include // different settings for edge and in-out parameters) if(restoreParameters) { gs->SetSnakeParameters( SNAPRegistryIO::ReadSnakeParameters( registry.Folder("SNAP.SnakeParameters"), gs->GetSnakeParameters())); } // Read the preprocessing settings if(restorePreprocessing) { // Read the edge preprocessing settings gs->SetEdgePreprocessingSettings( SNAPRegistryIO::ReadEdgePreprocessingSettings( registry.Folder("SNAP.Preprocessing.Edge"), gs->GetEdgePreprocessingSettings())); // Read the thresholding settings (note that since they depend on an image // we have to use re-initialized defaults if (app->GetIRISImageData()->IsGreyLoaded()) { gs->SetThresholdSettings( SNAPRegistryIO::ReadThresholdSettings( registry.Folder("SNAP.Preprocessing.Region"), ThresholdSettings::MakeDefaultSettings( app->GetIRISImageData()->GetGrey()))); } } // Read the display options if(restoreDisplayOptions) { // Read the mesh options gs->SetMeshOptions( SNAPRegistryIO::ReadMeshOptions( registry.Folder("IRIS.MeshOptions"), gs->GetMeshOptions())); // Restore the intensity mapping curve if (app->GetIRISImageData()->IsGreyLoaded()) { app->GetCurrentImageData()->GetGrey()->GetIntensityMapFunction()-> LoadFromRegistry(registry.Folder("IRIS.IntensityCurve")); app->GetCurrentImageData()->GetGrey()->UpdateIntensityMapFunction(); } } // Read the information about the bounding box and ROI sub-sampling gs->SetSegmentationROISettings( SNAPRegistryIO::ReadSegmentationROISettings( registry.Folder("IRIS.BoundingBox"), gs->GetSegmentationROISettings())); // Read the label info if(restoreLabels) { // Restore the table of color labels app->GetColorLabelTable()->LoadFromRegistry(registry.Folder("IRIS.LabelTable")); // Read the drawing color label gs->SetDrawingColorLabel( (LabelType)registry["IRIS.LabelState.DrawingLabel"][1]); // Read the override color label gs->SetOverWriteColorLabel( (LabelType)registry["IRIS.LabelState.OverwriteLabel"][0]); // Read the coverage mode gs->SetCoverageMode( registry["IRIS.LabelState.CoverageMode"].GetEnum( m_EnumMapCoverage,gs->GetCoverageMode())); // Read the polygon inversion state gs->SetPolygonInvert( registry["IRIS.LabelState.PolygonInvert"][gs->GetPolygonInvert()]); // Read the segmentation alpha gs->SetSegmentationAlpha( registry["IRIS.LabelState.OverallAlpha"][gs->GetSegmentationAlpha()]); } // If restore labels // Read other settings gs->SetLastAssociatedSegmentationFileName( registry["Files.Segmentation.FileName"][""]); gs->SetLastAssociatedPreprocessingFileName( registry["Files.Preprocessing.FileName"][""]); // Done! return true; } itksnap/Logic/Common/SNAPRegistryIO.h0000644000076500000240000000772611231677517016752 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPRegistryIO.h,v $ Language: C++ Date: $Date: 2009/07/22 21:06:23 $ 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 __SNAPRegistryIO_h_ #define __SNAPRegistryIO_h_ #include "GlobalState.h" #include "Registry.h" #include "SnakeParameters.h" #include "MeshOptions.h" #include "EdgePreprocessingSettings.h" #include "ThresholdSettings.h" #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif class IRISApplication; class SNAPRegistryIO { public: SNAPRegistryIO(); virtual ~SNAPRegistryIO() {} /** Write settings associated with a greyscale image to a registry */ void WriteImageAssociatedSettings( IRISApplication *app, Registry ®istry); /** Read settings associated with a greyscale image and apply them to * the current application */ bool ReadImageAssociatedSettings( Registry ®istry, IRISApplication *app, bool restoreLabels, bool restorePreprocessing, bool restoreParameters, bool restoreDisplayOptions); /** Read snake parameters from a registry */ SnakeParameters ReadSnakeParameters( Registry ®istry,const SnakeParameters &defaultSet); /** Write snake parameters to a registry */ void WriteSnakeParameters( const SnakeParameters &in,Registry ®istry); /** Read mesh options from a registry */ MeshOptions ReadMeshOptions( Registry ®istry,const MeshOptions &defaultSet); /** Write mesh options to a registry */ void WriteMeshOptions( const MeshOptions &in,Registry ®istry); /** Read edge preprocessing settings from a registry */ EdgePreprocessingSettings ReadEdgePreprocessingSettings( Registry ®istry,const EdgePreprocessingSettings &defaultSet); /** Write edge preprocessing settings to a registry */ void WriteEdgePreprocessingSettings( const EdgePreprocessingSettings &in,Registry ®istry); /** Read threshold settings from a registry */ ThresholdSettings ReadThresholdSettings( Registry ®istry,const ThresholdSettings &defaultSet); /** Write threshold settings to a registry */ void WriteThresholdSettings( const ThresholdSettings &in,Registry ®istry); /** Read ROI settings from a registry */ SNAPSegmentationROISettings ReadSegmentationROISettings( Registry &folder, const SNAPSegmentationROISettings &defaultSet); /** Write ROI settings to a registry */ void WriteSegmentationROISettings( const SNAPSegmentationROISettings &in, Registry &folder); // Some enumeraticns used by this class RegistryEnumMap m_EnumMapCoverage; RegistryEnumMap m_EnumMapSolver; RegistryEnumMap m_EnumMapSnakeType; RegistryEnumMap m_EnumMapROI; }; #endif // __SNAPRegistryIO_h_ itksnap/Logic/Common/SNAPSegmentationROISettings.cxx0000644000076500000240000000405010735614371021775 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPSegmentationROISettings.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:13 $ 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 "SNAPSegmentationROISettings.h" bool SNAPSegmentationROISettings ::TransformImageVoxelToROIVoxel(const Vector3ui &vImage, Vector3ui &vROI) { itk::Index<3> idx = to_itkIndex(vImage); if(m_ROI.IsInside(idx)) { for(unsigned int i = 0; i < 3; i++) { unsigned int p = vImage[i] - m_ROI.GetIndex()[i]; vROI[i] = (unsigned int) (p / m_VoxelScale[i]); } return true; } else return false; } void SNAPSegmentationROISettings ::TransformROIVoxelToImageVoxel(const Vector3ui &vROI, Vector3ui &vImage) { for(unsigned int i = 0; i < 3; i++) { unsigned int p = (unsigned int) (vROI[i] * m_VoxelScale[i]); vImage[i] = p + m_ROI.GetIndex()[i]; } } itksnap/Logic/Common/SNAPSegmentationROISettings.h0000644000076500000240000000703510735614371021430 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPSegmentationROISettings.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:13 $ 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 __SNAPSegmentationROISettings_h_ #define __SNAPSegmentationROISettings_h_ // #include "itkImageRegion.h" #include "SNAPCommon.h" #include "itkImageRegion.h" #include "IRISVectorTypesToITKConversion.h" /** * Settings describing the region of interest selected for the SnAP * segmentation and the resampling associated with it */ class SNAPSegmentationROISettings { public: // List of available interpolation methods enum InterpolationMethod { NEAREST_NEIGHBOR, TRILINEAR, TRICUBIC, SINC_WINDOW_05 }; SNAPSegmentationROISettings() { m_ResampleFlag = false; m_VoxelScale.fill(1.0); m_InterpolationMethod = NEAREST_NEIGHBOR; } virtual ~SNAPSegmentationROISettings() {} // Get the region of interest, in the main IRIS image irisSetMacro(ROI,itk::ImageRegion<3>); // Set the region of interest, in the main IRIS image irisGetMacro(ROI,itk::ImageRegion<3>); // Get whether or not resampling is desired for the region irisSetMacro(ResampleFlag,bool); // Set whether or not resampling is desired for the region irisGetMacro(ResampleFlag,bool); // Get the scaling factor for each dimension irisSetMacro(VoxelScale,Vector3d); // Set the scaling factor for each dimension irisGetMacro(VoxelScale,Vector3d); // Get the interpolation method used irisSetMacro(InterpolationMethod,InterpolationMethod); // Set the interpolation method used irisGetMacro(InterpolationMethod,InterpolationMethod); // Map image voxel to an ROI voxel, if the result is outside of the region // this will return false, and not change the index bool TransformImageVoxelToROIVoxel(const Vector3ui &vImage, Vector3ui &vROI); // Map ROI voxel into an image voxel. The result will be inside the image void TransformROIVoxelToImageVoxel(const Vector3ui &vROI, Vector3ui &vImage); private: // The region of interest, in the main IRIS image itk::ImageRegion<3> m_ROI; // Whether or not resampling is desired for the region bool m_ResampleFlag; // The scaling factor for each dimension Vector3d m_VoxelScale; // The interpolation method used InterpolationMethod m_InterpolationMethod; }; #endif // __SNAPSegmentationROISettings_h_ itksnap/Logic/CVS/0000755000076500000240000000000011560342170013233 5ustar paulystaffitksnap/Logic/CVS/Entries0000644000076500000240000000000211560342170014557 0ustar paulystaffD itksnap/Logic/CVS/Entries.Log0000644000076500000240000000024111560342170015304 0ustar paulystaffA D/Common//// A D/Framework//// A D/ImageWrapper//// A D/LevelSet//// A D/Mesh//// A D/Preprocessing//// A D/Processing//// A D/Slicing//// A D/UserFilters//// itksnap/Logic/CVS/Repository0000644000076500000240000000001611560342170015332 0ustar paulystaffitksnap/Logic itksnap/Logic/CVS/Root0000644000076500000240000000010011560342170014070 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/Framework/0000755000076500000240000000000011560342170014535 5ustar paulystaffitksnap/Logic/Framework/CVS/0000755000076500000240000000000011560342170015170 5ustar paulystaffitksnap/Logic/Framework/CVS/Entries0000644000076500000240000000123511560342170016525 0ustar paulystaff/GenericImageData.cxx/1.14/Mon Jun 28 18:45:08 2010// /GenericImageData.h/1.11/Sat Aug 29 23:02:43 2009// /GlobalState.cxx/1.8/Tue Jun 16 04:55:45 2009// /GlobalState.h/1.19/Sat Aug 29 23:18:42 2009// /IRISApplication.cxx/1.37/Mon Apr 18 17:35:30 2011// /IRISApplication.h/1.18/Wed Aug 26 01:10:20 2009// /IRISImageData.cxx/1.8/Sat Aug 29 23:02:43 2009// /IRISImageData.h/1.11/Sat Aug 29 23:02:44 2009// /SNAPImageData.cxx/1.11/Mon Apr 18 17:35:30 2011// /SNAPImageData.h/1.4/Fri Jan 23 20:09:38 2009// /UndoDataManager.h/1.4/Sat Nov 15 12:20:38 2008// /UndoDataManager.txx/1.4/Sat Nov 15 12:20:38 2008// /UndoDataManager_LabelType.cxx/1.3/Sun Dec 30 04:05:14 2007// D itksnap/Logic/Framework/CVS/Repository0000644000076500000240000000003011560342170017263 0ustar paulystaffitksnap/Logic/Framework itksnap/Logic/Framework/CVS/Root0000644000076500000240000000010011560342170016025 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/Framework/GenericImageData.cxx0000644000076500000240000002601311412166664020404 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GenericImageData.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ Version: $Revision: 1.14 $ 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. =========================================================================*/ // ITK Includes #include "itkOrientedImage.h" #include "itkImageIterator.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkUnaryFunctorImageFilter.h" #include "UnaryFunctorCache.h" #include "itkRGBAPixel.h" #include "IRISSlicer.h" #include "IRISApplication.h" #include /** Borland compiler is very lazy so we need to instantiate the template * by hand */ #if defined(__BORLANDC__) typedef IRISSlicer GenericImageDataDummyIRISSlicerTypeUchar; typedef itk::SmartPointer GenericImageDataDummySmartPointerSlicerType; typedef IRISSlicer GenericImageDataDummyIRISSlicerTypeShort; typedef itk::SmartPointer GenericImageDataDummySmartPointerSlicerShortType; typedef itk::ImageRegion<3> GenericImageDataBorlandDummyImageRegionType; typedef itk::ImageRegion<2> GenericImageDataBorlandDummyImageRegionType2; typedef itk::ImageBase<3> GenericImageDataBorlandDummyImageBaseType; typedef itk::ImageBase<2> GenericImageDataBorlandDummyImageBaseType2; typedef itk::Image GenericImageDataBorlandDummyImageType; typedef itk::Image GenericImageDataBorlandDummyImageType2; typedef itk::ImageRegionConstIterator GenericImageDataBorlandDummyConstIteratorType; typedef itk::Image GenericImageDataBorlandDummyShortImageType; typedef itk::Image GenericImageDataBorlandDummyShortImageType2; typedef itk::Image,2> GenericImageDataBorlandDummyShortImageTypeRGBA; typedef itk::ImageRegionConstIterator GenericImageDataBorlandDummyConstIteratorShortType; typedef itk::MinimumMaximumImageCalculator GenericImageDataBorlandDummyMinMaxCalc; #endif #include "GreyImageWrapper.h" #if defined(__BORLANDC__) typedef CachingUnaryFunctor GenericImageDataBorlamdCachingUnaryFunctor; typedef itk::UnaryFunctorImageFilter GenericImageDataDummyFunctorType; typedef itk::SmartPointer GenericImageDataDummyFunctorTypePointerType; #endif #include "GenericImageData.h" #include "LabelImageWrapper.h" #include "RGBImageWrapper.h" // System includes #include #include #include void GenericImageData ::SetSegmentationVoxel(const Vector3ui &index, LabelType value) { // Make sure that the main image data and the segmentation data exist assert(m_MainImageWrapper->IsInitialized() && m_LabelWrapper.IsInitialized()); // Store the voxel m_LabelWrapper.GetVoxelForUpdate(index) = value; // Mark the image as modified m_LabelWrapper.GetImage()->Modified(); } GenericImageData ::GenericImageData(IRISApplication *parent) { // Copy the parent object m_Parent = parent; // Make main image wrapper point to grey wrapper initially m_MainImageWrapper = &m_GreyWrapper; // Pass the label table from the parent to the label wrapper m_LabelWrapper.SetLabelColorTable(m_Parent->GetColorLabelTable()); // Add to the primary wrapper list m_MainWrappers.push_back(m_MainImageWrapper); m_MainWrappers.push_back(&m_LabelWrapper); } Vector3d GenericImageData ::GetImageSpacing() { assert(m_MainImageWrapper->IsInitialized()); return m_MainImageWrapper->GetImageBase()->GetSpacing().GetVnlVector(); } Vector3d GenericImageData ::GetImageOrigin() { assert(m_MainImageWrapper->IsInitialized()); return m_MainImageWrapper->GetImageBase()->GetOrigin().GetVnlVector(); } void GenericImageData ::SetGreyImage(GreyImageType *newGreyImage, const ImageCoordinateGeometry &newGeometry, const GreyTypeToNativeFunctor &native) { m_GreyWrapper.SetImage(newGreyImage); m_GreyWrapper.SetNativeMapping(native); m_GreyWrapper.SetAlpha(255); m_GreyWrapper.UpdateIntensityMapFunction(); SetMainImageCommon(&m_GreyWrapper, newGeometry); } void GenericImageData ::SetRGBImage(RGBImageType *newRGBImage, const ImageCoordinateGeometry &newGeometry) { m_RGBWrapper.SetImage(newRGBImage); m_RGBWrapper.SetAlpha(255); SetMainImageCommon(&m_RGBWrapper, newGeometry); } void GenericImageData ::SetMainImageCommon(ImageWrapperBase *wrapper, const ImageCoordinateGeometry &newGeometry) { // Make the wrapper the main image m_MainImageWrapper = wrapper; m_MainWrappers.pop_front(); m_MainWrappers.push_front(m_MainImageWrapper); // Initialize the segmentation data to zeros m_LabelWrapper.InitializeToWrapper(m_MainImageWrapper, (LabelType) 0); // Pass the coordinate transform to the wrappers SetImageGeometry(newGeometry); } void GenericImageData ::UnloadMainImage() { // First unload the overlays if exist UnloadOverlays(); // Clear the main image wrappers m_LabelWrapper.Reset(); m_MainImageWrapper->Reset(); } void GenericImageData ::SetGreyOverlay(GreyImageType *newGreyImage, const GreyTypeToNativeFunctor &native) { // Check that the image matches the size of the main image assert(m_MainImageWrapper->GetBufferedRegion() == newGreyImage->GetBufferedRegion()); // Pass the image to a Grey image wrapper GreyImageWrapper *newGreyOverlayWrapper = new GreyImageWrapper; newGreyOverlayWrapper->SetImage(newGreyImage); newGreyOverlayWrapper->SetNativeMapping(native); newGreyOverlayWrapper->SetAlpha(128); newGreyOverlayWrapper->UpdateIntensityMapFunction(); SetOverlayCommon(newGreyOverlayWrapper); } void GenericImageData ::SetRGBOverlay(RGBImageType *newRGBImage) { // Check that the image matches the size of the main image assert(m_MainImageWrapper->GetBufferedRegion() == newRGBImage->GetBufferedRegion()); // Pass the image to a RGB image wrapper RGBImageWrapper *newRGBOverlayWrapper = new RGBImageWrapper; newRGBOverlayWrapper->SetImage(newRGBImage); newRGBOverlayWrapper->SetAlpha(128); SetOverlayCommon(newRGBOverlayWrapper); } void GenericImageData ::SetOverlayCommon(ImageWrapperBase *overlay) { // Sync up spacing between the main and overlay image overlay->GetImageBase()->SetSpacing(m_MainImageWrapper->GetImageBase()->GetSpacing()); overlay->GetImageBase()->SetOrigin(m_MainImageWrapper->GetImageBase()->GetOrigin()); // Propagate the geometry information to this wrapper for(unsigned int iSlice = 0;iSlice < 3;iSlice ++) { overlay->SetImageToDisplayTransform( iSlice,m_ImageGeometry.GetImageToDisplayTransform(iSlice)); } // Add to the overlay wrapper list m_OverlayWrappers.push_back(overlay); } void GenericImageData ::UnloadOverlays() { while (m_OverlayWrappers.size() > 0) UnloadOverlayLast(); } void GenericImageData ::UnloadOverlayLast() { // Make sure at least one grey overlay is loaded if (!IsOverlayLoaded()) return; // Release the data associated with the last overlay ImageWrapperBase *wrapper = m_OverlayWrappers.back(); delete wrapper; wrapper = NULL; // Clear it off the wrapper lists m_OverlayWrappers.pop_back(); } void GenericImageData ::SetSegmentationImage(LabelImageType *newLabelImage) { // Check that the image matches the size of the grey image assert(m_MainImageWrapper->IsInitialized() && m_MainImageWrapper->GetBufferedRegion() == newLabelImage->GetBufferedRegion()); // Pass the image to the segmentation wrapper m_LabelWrapper.SetImage(newLabelImage); // Sync up spacing between the main and label image newLabelImage->SetSpacing(m_MainImageWrapper->GetImageBase()->GetSpacing()); newLabelImage->SetOrigin(m_MainImageWrapper->GetImageBase()->GetOrigin()); } bool GenericImageData ::IsGreyLoaded() { return m_GreyWrapper.IsInitialized(); } bool GenericImageData ::IsOverlayLoaded() { return (m_OverlayWrappers.size() > 0); } bool GenericImageData ::IsRGBLoaded() { return m_RGBWrapper.IsInitialized(); } bool GenericImageData ::IsSegmentationLoaded() { return m_LabelWrapper.IsInitialized(); } void GenericImageData ::SetCrosshairs(const Vector3ui &crosshairs) { SetCrosshairs(m_MainWrappers, crosshairs); SetCrosshairs(m_OverlayWrappers, crosshairs); } void GenericImageData ::SetCrosshairs(WrapperList &list, const Vector3ui &crosshairs) { WrapperIterator it = list.begin(); while (it != list.end()) { ImageWrapperBase *wrapper = *it++; if (wrapper->IsInitialized()) wrapper->SetSliceIndex(crosshairs); } } GenericImageData::RegionType GenericImageData ::GetImageRegion() const { assert(m_MainImageWrapper->IsInitialized()); return m_MainImageWrapper->GetBufferedRegion(); } void GenericImageData ::SetImageGeometry(const ImageCoordinateGeometry &geometry) { SetImageGeometry(m_MainWrappers, geometry); SetImageGeometry(m_OverlayWrappers, geometry); } void GenericImageData ::SetImageGeometry(WrapperList &list, const ImageCoordinateGeometry &geometry) { // Save the geometry m_ImageGeometry = geometry; // Propagate the geometry to the image wrappers for(WrapperIterator it = list.begin(); it != list.end(); ++it) { ImageWrapperBase *wrapper = *it; if(wrapper->IsInitialized()) { // Set the direction matrix in the image wrapper->GetImageBase()->SetDirection( itk::Matrix(geometry.GetImageDirectionCosineMatrix())); // Update the geometry for each slice for(unsigned int iSlice = 0;iSlice < 3;iSlice ++) { wrapper->SetImageToDisplayTransform( iSlice,m_ImageGeometry.GetImageToDisplayTransform(iSlice)); } } } } itksnap/Logic/Framework/GenericImageData.h0000644000076500000240000001713211246331623020025 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GenericImageData.h,v $ Language: C++ Date: $Date: 2009/08/29 23:02:43 $ 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. ----- 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 __GenericImageData_h_ #define __GenericImageData_h_ #include "SNAPCommon.h" #include "IRISException.h" #include "LabelImageWrapper.h" #include "GreyImageWrapper.h" #include "RGBImageWrapper.h" #include "GlobalState.h" #include "ImageCoordinateGeometry.h" class IRISApplication; /** * \class GenericImageData * \brief This class encapsulates the image data used by * the IRIS component of SnAP. * * This data consists of a grey image [gi] and a segmentation image [si]. * The following rules must be satisfied by this class: * + exists(si) ==> exists(gi) * + if exists(si) then size(si) == size(gi) */ class GenericImageData { public: // Image type definitions typedef GreyImageWrapper::ImageType GreyImageType; typedef RGBImageWrapper::ImageType RGBImageType; typedef LabelImageWrapper::ImageType LabelImageType; typedef itk::ImageRegion<3> RegionType; typedef std::list WrapperList; typedef WrapperList::iterator WrapperIterator; typedef WrapperList::const_iterator WrapperConstIterator; GenericImageData(IRISApplication *parent); virtual ~GenericImageData() {}; /** * Access the 'main' image, either grey or RGB. The main image is the * one that all other images must mimic */ ImageWrapperBase* GetMain() { assert(m_MainImageWrapper->IsInitialized()); return m_MainImageWrapper; } bool IsMainLoaded() { return m_MainImageWrapper->IsInitialized(); } /** * Access the greyscale image (read only access is allowed) */ GreyImageWrapper* GetGrey() { assert(m_GreyWrapper.IsInitialized()); return &m_GreyWrapper; } /** * Access the RGB image (read only access is allowed) */ RGBImageWrapper* GetRGB() { assert(m_RGBWrapper.IsInitialized()); return &m_RGBWrapper; } /** * Access the overlay images (read only access is allowed) */ WrapperList* GetOverlays() { return &m_OverlayWrappers; } /** * Access the segmentation image (read only access allowed * to preserve state) */ LabelImageWrapper* GetSegmentation() { assert(m_MainImageWrapper->IsInitialized() && m_LabelWrapper.IsInitialized()); return &m_LabelWrapper; } /** * Get the extents of the image volume */ Vector3ui GetVolumeExtents() const { assert(m_MainImageWrapper->IsInitialized()); return m_MainImageWrapper->GetSize(); } /** * Get the ImageRegion (largest possible region of all the images) */ RegionType GetImageRegion() const; /** * Get the spacing of the gray scale image (and all the associated images) */ Vector3d GetImageSpacing(); /** * Get the origin of the gray scale image (and all the associated images) */ Vector3d GetImageOrigin(); /** * Set the grey image (read important note). * * Note: this method replaces the internal pointer to the grey image * by the pointer that is passed in. That means that the caller should relinquish * control of this pointer and that the GenericImageData class will dispose of the * pointer properly. * * The second parameter to this method is the new geometry object, which depends * on the size of the grey image and will be updated. */ virtual void SetGreyImage( GreyImageType *newGreyImage, const ImageCoordinateGeometry &newGeometry, const GreyTypeToNativeFunctor &native); virtual void SetRGBImage(RGBImageType *newRGBImage, const ImageCoordinateGeometry &newGeometry); virtual void UnloadMainImage(); virtual void SetGreyOverlay( GreyImageType *newGreyImage, const GreyTypeToNativeFunctor &native); virtual void SetRGBOverlay(RGBImageType *newRGBImage); virtual void UnloadOverlays(); virtual void UnloadOverlayLast(); /** * This method sets the segmentation image (see note for SetGrey). */ virtual void SetSegmentationImage(LabelImageType *newLabelImage); /** * Set voxel in segmentation image */ void SetSegmentationVoxel(const Vector3ui &index, LabelType value); /** * Check validity of greyscale image */ bool IsGreyLoaded(); /** * Check validity of RGB image */ bool IsRGBLoaded(); /** * Check validity of overlay images */ bool IsOverlayLoaded(); /** * Check validity of segmentation image */ bool IsSegmentationLoaded(); /** * Set the cursor (crosshairs) position, in pixel coordinates */ virtual void SetCrosshairs(const Vector3ui &crosshairs); /** * Set the image coordinate geometry for this image set. Propagates * the transform to the internal image wrappers */ virtual void SetImageGeometry(const ImageCoordinateGeometry &geometry); /** Get the image coordinate geometry */ irisGetMacro(ImageGeometry,ImageCoordinateGeometry); protected: virtual void SetMainImageCommon(ImageWrapperBase *wrapper, const ImageCoordinateGeometry &geometry); virtual void SetOverlayCommon(ImageWrapperBase *wrapper); virtual void SetCrosshairs(WrapperList &list, const Vector3ui &crosshairs); virtual void SetImageGeometry(WrapperList &list, const ImageCoordinateGeometry &geometry); // Wrapper around the grey-scale image GreyImageWrapper m_GreyWrapper; // Wrapper around the RGB image RGBImageWrapper m_RGBWrapper; // A pointer to the 'main' image, i.e., the image that is treated as the // reference for all other images. It is typically the grey image, but // since we now allow for RGB images, it can point to the RGB image too ImageWrapperBase *m_MainImageWrapper; // Wrapper around the segmentatoin image LabelImageWrapper m_LabelWrapper; // A list of linked wrappers, whose cursor position and image geometry // are updated concurrently WrapperList m_MainWrappers; WrapperList m_OverlayWrappers; // Parent object IRISApplication *m_Parent; // Image coordinate geometry (it's placed here because the transform depends // on image size) ImageCoordinateGeometry m_ImageGeometry; }; #endif itksnap/Logic/Framework/GlobalState.cxx0000644000076500000240000001376711215622721017500 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GlobalState.cxx,v $ Language: C++ Date: $Date: 2009/06/16 04:55:45 $ Version: $Revision: 1.8 $ 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 "GlobalState.h" GlobalState ::GlobalState() { m_GreyFileExtension = NULL; m_DrawingColorLabel = 1; m_OverWriteColorLabel = 0; m_CrosshairsPosition[0] = 0; m_CrosshairsPosition[1] = 0; m_CrosshairsPosition[2] = 0; m_ToolbarMode = CROSSHAIRS_MODE; m_CoverageMode = PAINT_OVER_ALL; m_UpdateSliceFlag = 1; m_InterpolateGrey = false; m_InterpolateSegmentation = false; m_PolygonInvert = false; m_LockHeld = 0; m_LockOwner = 0; m_SegmentationAlpha = 0; // SNAP is off initially m_SNAPActive = false; // Presets m_SpeedColorMapInRegionMode = COLORMAP_BLUE_BLACK_WHITE; m_SpeedColorMapInEdgeMode = COLORMAP_BLACK_BLACK_WHITE; // Snake stuff: m_SpeedValid = false; m_ShowSpeed = false; m_IsValidROI = false; m_ShowROI = false; m_DraggingROI = false; m_SnakeMode = EDGE_SNAKE; m_SnakeActive = false; m_SpeedViewZero = false; m_SnakeParameters = SnakeParameters::GetDefaultEdgeParameters(); m_ThresholdSettings = ThresholdSettings::MakeDefaultSettingsWithoutImage(); m_EdgePreprocessingSettings = EdgePreprocessingSettings::MakeDefaultSettings(); // Default preview modes: enabled for in-out, disabled for edges (too slow) m_ShowPreprocessedEdgePreview = false; m_ShowPreprocessedInOutPreview = true; // The preview is not currently valid m_SpeedPreviewValid = false; // Bubbles m_ActiveBubble = -1; // Set paintbrush defaults m_PaintbrushSettings.radius = 4; m_PaintbrushSettings.mode = PAINTBRUSH_RECTANGULAR; m_PaintbrushSettings.flat = true; m_PaintbrushSettings.isotropic = false; m_PaintbrushSettings.chase = false; m_PaintbrushSettings.watershed.level = 0.2; m_PaintbrushSettings.watershed.smooth_iterations = 15; // Set annotation defaults m_AnnotationSettings.shownOnAllSlices = false; } GlobalState ::~GlobalState() { if(m_GreyFileExtension != NULL) delete [] m_GreyFileExtension; } #ifdef DRAWING_LOCK // TODO: What's this for? // mutual exclusion on the drawing lock - only one window can draw at a time int GlobalState ::GetDrawingLock( short windowID ) { if ((!m_LockHeld) || (m_LockOwner == windowID)) { m_LockHeld = 1; m_LockOwner = windowID; return 1; } return 0; } int GlobalState ::ReleaseDrawingLock(short windowID) { if (m_LockHeld && (m_LockOwner != windowID)) { cerr << "hmph: Cannot ReleaseDrawingLock(): not lock owner" << endl; return 0; } m_LockHeld = 0; m_LockOwner = 0; return 1; } #endif /* DRAWING_LOCK */ /** * Pulls the extension off of a filename and * saves it for future use in saving preproc * data * * PRE: none * POST: if fname is valid, m_GreyFileExtension will * have the file extension (including leading * period) of the file */ void GlobalState ::SetGreyExtension(const char *fname) { const char * basename; // Make sure fname is valid if(fname != NULL) { // Get rid of any currently saved // extension if(m_GreyFileExtension != NULL) { delete [] m_GreyFileExtension; m_GreyFileExtension = NULL; } // Get rid of all path information // TODO: This is platform specific!!! basename = strrchr(fname, '/'); if(basename) { basename++; } else { basename = fname; } // Find the first period and count everything // to the right of it as path. Allows for // multiple periods (ie .hdr.gz) basename = strchr(basename, '.'); if(basename) { m_GreyFileExtension = new char[strlen(basename) + 1]; strcpy(m_GreyFileExtension, basename); } } } /** * Returns the extension for the grey file, * user is responsible for deallocating the * memory for ext * PRE: ext is NULL or dynamically allocated array * POST: ext points to a null terminated string * which contains the extension in m_GreyFileExtension * or ext is NULL if m_GreyFileExtension is empty */ void GlobalState ::GetGreyExtension(char *& ext) { // Get rid of any data in ext if(ext != NULL) delete [] ext; // Copy m_GreyFileExtension into ext if(m_GreyFileExtension == NULL) ext = NULL; else { ext = new char[strlen(m_GreyFileExtension) + 1]; strcpy(ext, m_GreyFileExtension); } } /** Set the colormap in current preprocessing mode*/ void GlobalState ::SetSpeedColorMap(ColorMapPreset xPreset) { if(m_SnakeMode == EDGE_SNAKE) SetSpeedColorMapInEdgeMode(xPreset); else SetSpeedColorMapInRegionMode(xPreset); } /** Get the colormap in current preprocessing mode*/ ColorMapPreset GlobalState ::GetSpeedColorMap() { return (m_SnakeMode == EDGE_SNAKE) ? GetSpeedColorMapInEdgeMode() : GetSpeedColorMapInRegionMode(); } itksnap/Logic/Framework/GlobalState.h0000644000076500000240000006256311246333522017125 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GlobalState.h,v $ Language: C++ Date: $Date: 2009/08/29 23:18:42 $ Version: $Revision: 1.19 $ 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 __GlobalState_h_ #define __GlobalState_h_ #include #include "SNAPCommon.h" #include "EdgePreprocessingSettings.h" #include "MeshOptions.h" #include "SnakeParameters.h" #include "ThresholdSettings.h" #include "SNAPSegmentationROISettings.h" #include "itkImageRegion.h" enum ToolbarModeType { POLYGON_DRAWING_MODE, NAVIGATION_MODE, CROSSHAIRS_MODE, PAINTBRUSH_MODE, ANNOTATION_MODE, ROI_MODE }; enum ToolbarMode3DType { TRACKBALL_MODE, CROSSHAIRS_3D_MODE, SPRAYPAINT_MODE, SCALPEL_MODE }; enum CoverageModeType { PAINT_OVER_ALL, PAINT_OVER_ONE, PAINT_OVER_COLORS }; enum MeshFilterType { SELECT_MESH_SINGLE, SELECT_MESH_VISIBLE, SELECT_MESH_ALL }; enum SnakeType { IN_OUT_SNAKE, EDGE_SNAKE }; enum ConstraintsType { SAPIRO, SCHLEGEL, TURELLO, USER }; /** Color map presets */ enum ColorMapPreset { COLORMAP_BLACK_BLACK_WHITE = 0, COLORMAP_BLUE_BLACK_WHITE, COLORMAP_BLACK_GRAY_WHITE, COLORMAP_BLUE_WHITE_RED, COLORMAP_BLACK_YELLOW_WHITE }; enum PaintbrushMode { PAINTBRUSH_RECTANGULAR = 0, PAINTBRUSH_ROUND = 1, PAINTBRUSH_WATERSHED = 2 }; enum AnatomicalDirection { ANATOMY_AXIAL = 0, ANATOMY_SAGITTAL, ANATOMY_CORONAL }; /** Watershed settings for paintbrush */ struct PaintbrushWatershedSettings { // Level of thresholding double level; // Amount of smoothing unsigned int smooth_iterations; }; /** Paintbrush settings */ struct PaintbrushSettings { double radius; PaintbrushMode mode; bool flat; bool isotropic; bool chase; PaintbrushWatershedSettings watershed; }; /** Annotation settings */ struct AnnotationSettings { bool shownOnAllSlices; }; /** * Bubble structure: an object of this class stores information about an * individual bubble initialized in the Snake window */ struct Bubble { // center of the bubble (voxel index) Vector3i center; // radius of the bubble (in mm.) double radius; }; /** * \class GlobalState * \brief Contains global variables describing the state of the application. */ class GlobalState { public: // Region of interest definition typedef itk::ImageRegion<3> RegionType; // Define the bubble array typedef std::vector BubbleArray; GlobalState(); virtual ~GlobalState(); /** Get color label used to draw polygons */ irisSetMacro(DrawingColorLabel,LabelType); /** Set color label used to draw polygons */ irisGetMacro(DrawingColorLabel,LabelType); /** Get color label over which we can draw */ irisSetMacro(OverWriteColorLabel,LabelType); /** Set color label over which we can draw */ irisGetMacro(OverWriteColorLabel,LabelType); /** Get whether the grey image display uses linear interpolation */ irisSetMacro(InterpolateGrey,bool ); /** Set whether the grey image display uses linear interpolation */ irisGetMacro(InterpolateGrey,bool ); /** Get whether the segmentation image uses linear interpolation */ irisSetMacro(InterpolateSegmentation,bool ); /** Set whether the segmentation image uses linear interpolation */ irisGetMacro(InterpolateSegmentation,bool ); /** Get whether polygons drawn are inverted or not */ irisSetMacro(PolygonInvert,bool ); /** Set whether polygons drawn are inverted or not */ irisGetMacro(PolygonInvert,bool ); /** Get the transparency of the segmentation overlay */ irisSetMacro(SegmentationAlpha,unsigned char ); /** Set the transparency of the segmentation overlay */ irisGetMacro(SegmentationAlpha,unsigned char ); /** Get the current toolbar mode */ irisSetMacro(ToolbarMode,ToolbarModeType ); /** Set the current toolbar mode */ irisGetMacro(ToolbarMode,ToolbarModeType ); /** Get the current 3D toolbar mode */ irisSetMacro(ToolbarMode3D,ToolbarMode3DType); /** Set the current 3D toolbar mode */ irisGetMacro(ToolbarMode3D,ToolbarMode3DType); /** Get whether the slice requires an update or not (TODO: obsolete?) */ irisSetMacro(UpdateSliceFlag,int ); /** Set whether the slice requires an update or not (TODO: obsolete?) */ irisGetMacro(UpdateSliceFlag,int ); /** Get current mode of polygon/snake painting (over all, over label) */ irisSetMacro(CoverageMode,CoverageModeType ); /** Set current mode of polygon/snake painting (over all, over label) */ irisGetMacro(CoverageMode,CoverageModeType ); /** Get whether the region of interest is valid */ irisSetMacro(IsValidROI,bool ); /** Set whether the region of interest is valid */ irisGetMacro(IsValidROI,bool ); /** Get whether the region of interest is visible */ irisSetMacro(ShowROI,bool ); /** Set whether the region of interest is visible */ irisGetMacro(ShowROI,bool ); /** Get whether the region of interest is being dragged */ irisSetMacro(DraggingROI,bool ); /** Set whether the region of interest is being dragged */ irisGetMacro(DraggingROI,bool ); /** Get whether SNAP is currently active */ irisSetMacro(SNAPActive,bool ); /** Set whether SNAP is currently active */ irisGetMacro(SNAPActive,bool ); /** Get whether the speed (preprocessing) image is visible */ irisSetMacro(ShowSpeed,bool ); /** Set whether the speed (preprocessing) image is visible */ irisGetMacro(ShowSpeed,bool ); /** Get whether the speed (preprocessing) image is valid */ irisSetMacro(SpeedValid,bool ); /** Set whether the speed (preprocessing) image is valid */ irisGetMacro(SpeedValid,bool ); /** Get whether the zero level of the speed image is being displayed */ irisSetMacro(SpeedViewZero,bool ); /** Set whether the zero level of the speed image is being displayed */ irisGetMacro(SpeedViewZero,bool ); /** Get the colormap used to display speed in edge mode */ irisGetMacro(SpeedColorMapInEdgeMode, ColorMapPreset); /** Set the colormap used to display speed in edge mode */ irisSetMacro(SpeedColorMapInEdgeMode, ColorMapPreset); /** Get the colormap used to display speed in region mode */ irisGetMacro(SpeedColorMapInRegionMode, ColorMapPreset); /** Set the colormap used to display speed in region mode */ irisSetMacro(SpeedColorMapInRegionMode, ColorMapPreset); /** Set the colormap in current preprocessing mode*/ void SetSpeedColorMap(ColorMapPreset xPreset); /** Get the colormap in current preprocessing mode*/ ColorMapPreset GetSpeedColorMap(); /** Get the type of the snake being used */ irisSetMacro(SnakeMode,SnakeType ); /** Set the type of the snake being used */ irisGetMacro(SnakeMode,SnakeType ); /** Get whether the snake is currently active */ irisSetMacro(SnakeActive,bool ); /** Set whether the snake is currently active */ irisGetMacro(SnakeActive,bool ); /** Set whether the speed preview is valid or not */ irisSetMacro(SpeedPreviewValid, bool); /** Get whether the speed preview is valid or not */ irisGetMacro(SpeedPreviewValid, bool); /** Set the auto-preview feature of edge-mode preprocessor uses */ irisSetMacro(ShowPreprocessedEdgePreview,bool); /** Get the auto-preview feature of edge-mode preprocessor uses */ irisGetMacro(ShowPreprocessedEdgePreview,bool); /** Set the auto-preview feature of edge-mode preprocessor uses */ irisSetMacro(ShowPreprocessedInOutPreview,bool); /** Get the auto-preview feature of edge-mode preprocessor uses */ irisGetMacro(ShowPreprocessedInOutPreview,bool); /** Get the current settings for in/out snake processing */ irisGetMacro(ThresholdSettings,ThresholdSettings); /** Set the current settings for in/out snake processing */ irisSetMacro(ThresholdSettings,ThresholdSettings); /** Set the current settings for edge snake processing */ irisSetMacro(EdgePreprocessingSettings,EdgePreprocessingSettings); /** Get the current settings for edge snake processing */ irisGetMacro(EdgePreprocessingSettings,EdgePreprocessingSettings); /** Get the current parameters of the snake algorithm */ irisGetMacro(SnakeParameters,SnakeParameters); /** Set the current parameters of the snake algorithm */ irisSetMacro(SnakeParameters,SnakeParameters); /** Get the current mesh rendering options */ irisGetMacro(MeshOptions,MeshOptions); /** Set the current mesh rendering options */ irisSetMacro(MeshOptions,MeshOptions); /** Set the settings associated with the region of interest extracted for segmentation */ irisSetMacro(SegmentationROISettings,const SNAPSegmentationROISettings &); /** Set the settings associated with the region of interest extracted for segmentation */ irisGetMacro(SegmentationROISettings,const SNAPSegmentationROISettings &); /** Shortcut ot set the actual bounding box in the ROI from the settings */ void SetSegmentationROI(const RegionType &roi) { m_SegmentationROISettings.SetROI(roi); } /** Shortcut ot get the actual bounding box in the ROI from the settings */ RegionType GetSegmentationROI() { return m_SegmentationROISettings.GetROI(); } /** Get the current paintbrush settings */ irisGetMacro(PaintbrushSettings, const PaintbrushSettings &); /** Set the current paintbrush settings */ irisSetMacro(PaintbrushSettings, const PaintbrushSettings &); /** Get the current annotation settings */ irisGetMacro(AnnotationSettings, const AnnotationSettings &); /** Set the current annotation settings */ irisSetMacro(AnnotationSettings, const AnnotationSettings &); #ifdef DRAWING_LOCK int GetDrawingLock( short ); int ReleaseDrawingLock( short ); #endif /* DRAWING_LOCK */ /** Set the extension of the grey image */ void SetGreyExtension(const char * fname); /** Get the extension of the grey image */ void GetGreyExtension(char *& ext); /** Set the grey image file name */ irisSetStringMacro(GreyFileName); /** Get the grey image file name */ irisGetStringMacro(GreyFileName); /** Set the grey overlay image file name */ irisSetStringMacro(GreyOverlayFileName); /** Get the grey overlay image file name */ irisGetStringMacro(GreyOverlayFileName); /** Set the RGB image file name */ irisSetStringMacro(RGBFileName); /** Get the RGB image file name */ irisGetStringMacro(RGBFileName); /** Set the RGB overlay image file name */ irisSetStringMacro(RGBOverlayFileName); /** Get the RGB overlay image file name */ irisGetStringMacro(RGBOverlayFileName); /** Set the segmentation image file name */ irisSetStringMacro(SegmentationFileName); /** Get the segmentation image file name */ irisGetStringMacro(SegmentationFileName); /** Set the preprocessing image file name */ irisSetStringMacro(PreprocessingFileName); /** Get the preprocessing image file name */ irisGetStringMacro(PreprocessingFileName); /** Set the segmentation image file name */ irisSetStringMacro(LastAssociatedSegmentationFileName); /** Get the segmentation image file name */ irisGetStringMacro(LastAssociatedSegmentationFileName); /** Set the preprocessing image file name */ irisSetStringMacro(LastAssociatedPreprocessingFileName); /** Get the preprocessing image file name */ irisGetStringMacro(LastAssociatedPreprocessingFileName); /** Set the preprocessing image file name */ irisSetStringMacro(LevelSetFileName); /** Get the preprocessing image file name */ irisGetStringMacro(LevelSetFileName); /** Set the advection file name */ void SetAdvectionFileName(unsigned int i, const char *name) { m_AdvectionFileName[i] = name; } /** Get the advection file name */ const char *GetAdvectionFileName(unsigned int i) { return m_AdvectionFileName[i].c_str(); } /** Get the array of bubbles */ irisGetMacro(BubbleArray, BubbleArray); /** Set the array of bubbles */ irisSetMacro(BubbleArray, BubbleArray); /** * Get the active bubble number. This can be -1 indicating that there is no * active bubble or 0 .. N-1, where N is the size of the bubble array. The * active bubble is really a GUI concept for the time being. In general, the * bubbles will be eventually replaced by more advanced class hierarchy of * seeds, sensors, attractors, and detractors. */ irisGetMacro(ActiveBubble, int); /** Set the active bubble */ irisSetMacro(ActiveBubble, int); /** Check if there is an active bubble (i.e., ActiveBubble == -1) */ bool IsActiveBubbleOn() const { return (m_ActiveBubble >= 0); } /** Disable active bubble (i.e., set ActiveBubble to -1) */ void UnsetActiveBubble() { m_ActiveBubble = -1; } private: /** Get the current crosshairs position */ irisSetMacro(CrosshairsPosition,Vector3ui ); /** Set the current crosshairs position */ irisGetMacro(CrosshairsPosition,Vector3ui ); friend class IRISApplication; /** Color label used to draw polygons */ LabelType m_DrawingColorLabel; /** Color label over which we can draw */ LabelType m_OverWriteColorLabel; /** Whether the grey image display uses linear interpolation */ bool m_InterpolateGrey; /** Whether the segmentation image uses linear interpolation */ bool m_InterpolateSegmentation; /** Whether polygons drawn are inverted or not */ bool m_PolygonInvert; /** The transparency of the segmentation overlay */ unsigned char m_SegmentationAlpha; /** The current crosshairs position */ Vector3ui m_CrosshairsPosition; /** The current toolbar mode */ ToolbarModeType m_ToolbarMode; /** The current 3D toolbar mode */ ToolbarMode3DType m_ToolbarMode3D; /** Whether the slice requires an update or not (TODO: obsolete?) */ int m_UpdateSliceFlag; /** Current mode of polygon/snake painting (over all, over label) */ CoverageModeType m_CoverageMode; /** Whether the region of interest is valid */ bool m_IsValidROI; /** Whether the region of interest is visible */ bool m_ShowROI; /** Whether the region of interest is being dragged */ bool m_DraggingROI; /** Whether SNAP is currently active */ bool m_SNAPActive; /** Whether the speed (preprocessing) image is visible */ bool m_ShowSpeed; /** Whether the speed (preprocessing) image is valid */ bool m_SpeedValid; /** Whether the zero level of the speed image is being displayed */ bool m_SpeedViewZero; /** Color map preset in edge mode */ ColorMapPreset m_SpeedColorMapInEdgeMode; /** Color map preset in region mode */ ColorMapPreset m_SpeedColorMapInRegionMode; /** The type of the snake being used */ SnakeType m_SnakeMode; /** Whether the snake is currently active */ bool m_SnakeActive; /** Grey image file extension */ char * m_GreyFileExtension; /** The region of interest for the segmentation (drawn by the user) */ SNAPSegmentationROISettings m_SegmentationROISettings; int m_LockHeld; int m_LockOwner; // Current settings for threshold preprocessing ThresholdSettings m_ThresholdSettings; // Current settings for threshold preprocessing EdgePreprocessingSettings m_EdgePreprocessingSettings; // Current mesh options MeshOptions m_MeshOptions; // Whether preview is valid or not bool m_SpeedPreviewValid; // Auto-preview state bool m_ShowPreprocessedEdgePreview; bool m_ShowPreprocessedInOutPreview; // Current settings for the snake algorithm SnakeParameters m_SnakeParameters; // File name of the current grey file std::string m_GreyFileName; // File name of the current grey overlay file std::string m_GreyOverlayFileName; // File name of the current RGB file std::string m_RGBFileName; // File name of the current RGB overlay file std::string m_RGBOverlayFileName; // File name of the current grey file std::string m_SegmentationFileName; std::string m_LastAssociatedSegmentationFileName; // File name of the current preprocessing file std::string m_PreprocessingFileName; std::string m_LastAssociatedPreprocessingFileName; // File name of level set image file std::string m_LevelSetFileName; // File names for advection images std::string m_AdvectionFileName[3]; // Array of bubbles BubbleArray m_BubbleArray; // Current bubble int m_ActiveBubble; // Paintbrush settings PaintbrushSettings m_PaintbrushSettings; // Annotation settings AnnotationSettings m_AnnotationSettings; }; #endif // __GlobalState_h_ /* *$Log: GlobalState.h,v $ *Revision 1.19 2009/08/29 23:18:42 garyhuizhang *ENH: GreyImageWrapper uses the new ColorMap class * *Revision 1.18 2009/06/16 06:47:52 garyhuizhang *ENH: color map supports for grey overlay * *Revision 1.17 2009/06/16 04:55:45 garyhuizhang *ENH: per overlay opacity adjustment * *Revision 1.16 2009/06/09 04:34:00 garyhuizhang *ENH: color map for grey to RGB mapping * *Revision 1.15 2009/02/18 00:22:44 garyhuizhang *FIX: minor clean up * *Revision 1.14 2009/02/10 00:10:12 garyhuizhang *ENH: Support two drawing options in the Annotation mode: 1) each line shown only on the slice level it is drawn; 2) each line is shown on all slice levels * *Revision 1.13 2009/01/23 21:48:59 pyushkevich *ENH: Added hidden annotation mode (very bad code) * *Revision 1.12 2009/01/17 10:40:28 pyushkevich *Added synchronization to 3D window viewpoint * *Revision 1.11 2008/12/02 05:14:19 pyushkevich *New feature: watershed-based adaptive paint brush. Based on the similar tool in ITK-Grey (which was derived from ITK-SNAP). * *Revision 1.10 2008/11/17 19:38:23 pyushkevich *Added tools dialog to label editor window * *Revision 1.9 2008/11/15 12:20:38 pyushkevich *Several new features added for release 1.8, including (1) support for reading floating point and mapping to short range; (2) use of image direction cosines to determine image orientation; (3) new reorient image dialog and changes to the IO wizard; (4) display of NIFTI world coordinates and yoking based on them; (5) multi-session zoom; (6) fixes to the way we keep track of unsaved changes to segmentation, including a new discard dialog; (7) more streamlined code for offline loading; (8) new command-line options, allowing RGB files to be read and opening SNAP by doubleclicking images; (9) versioning for IPC communications; (10) ruler for display windows; (11) bug fixes and other stuff I can't remember * *Revision 1.8 2008/03/25 19:31:31 pyushkevich *Bug fixes for release 1.6.0 * *Revision 1.7 2008/02/10 23:55:22 pyushkevich *Added "Auto" button to the intensity curve window; Added prompt before quitting on unsaved data; Fixed issues with undo on segmentation image load; Added synchronization between SNAP sessions. * *Revision 1.6 2007/12/30 04:05:13 pyushkevich *GPL License * *Revision 1.5 2007/09/15 15:59:20 pyushkevich *Improved the paintbrush mode, allowed more variety of brush sizes * *Revision 1.4 2007/06/06 22:27:20 garyhuizhang *Added support for RGB images in SNAP * *Revision 1.3 2007/05/10 20:19:50 pyushkevich *Added VTK mesh export code and GUI * *Revision 1.2 2006/12/06 01:26:06 pyushkevich *Preparing for 1.4.1. Seems to be stable in Windows but some bugs might be still there * *Revision 1.1 2006/12/02 04:22:11 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.11 2006/02/01 20:21:23 pauly *ENH: An improvement to the main SNAP UI structure: one set of GL windows is used to support SNAP and IRIS modes * *Revision 1.10 2005/12/19 03:43:11 pauly *ENH: SNAP enhancements and bug fixes for 1.4 release * *Revision 1.9 2005/10/29 14:00:13 pauly *ENH: SNAP enhacements like color maps and progress bar for 3D rendering * *Revision 1.8 2004/07/24 19:00:03 pauly *ENH: Thumbnail UI for slice zooming * *Revision 1.7 2004/03/19 00:54:47 pauly *ENH: Added the ability to externally load the advection image * *Revision 1.6 2003/12/07 19:48:41 pauly *ENH: Resampling, multiresolution * *Revision 1.5 2003/10/09 22:45:12 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.4 2003/10/02 14:54:52 pauly *ENH: Development during the September code freeze * *Revision 1.1 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:52:23 pauly *Initial checkin of SNAP application to the InsightApplications tree * *Revision 1.10 2003/07/12 01:34:18 pauly *More final changes before ITK checkin * *Revision 1.9 2003/07/11 23:28:52 pauly **** empty log message *** * *Revision 1.8 2003/06/08 16:11:42 pauly *User interface changes *Automatic mesh updating in SNAP mode * *Revision 1.7 2003/06/03 00:06:46 pauly *Almost ready for Pittsburgh demo * *Revision 1.6 2003/05/22 17:36:19 pauly *Edge preprocessing settings * *Revision 1.5 2003/05/14 18:33:58 pauly *SNAP Component is working. Double thresholds have been enabled. Many other changes. * *Revision 1.4 2003/05/07 19:14:46 pauly *More progress on getting old segmentation working in the new SNAP. Almost there, region of interest and bubbles are working. * *Revision 1.3 2003/04/23 20:36:23 pauly **** empty log message *** * *Revision 1.2 2003/04/23 06:05:18 pauly **** empty log message *** * *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.16 2002/05/08 17:33:34 moon *I made some changes Guido wanted in the GUI, including removing *Turello/Sapiro/Schlegel options (I only hid them, the code is all *still there), changing a bunch of the ranges, defaults, etc. of the *snake parameters. * *Revision 1.15 2002/04/27 18:30:14 moon *Finished commenting * *Revision 1.14 2002/04/27 00:08:43 talbert *Final commenting run through . . . no functional changes. * *Revision 1.13 2002/04/26 17:38:05 moon *Added global variable used by the Apply+ button in the in/out dialog so that *2D windows know to show zero level visualization rather than seg data. * *Revision 1.12 2002/04/24 19:51:34 moon *Added a flag for when the ROI was being dragged. The roi dragging is now better in *some ways, although it is not completely perfect. * *Revision 1.11 2002/04/24 14:14:01 moon *Implemented separate brightness/contrast settings for grey/preproc data * *Revision 1.10 2002/04/20 21:57:20 talbert *Added some code to access the extension of the grey file. * *Revision 1.9 2002/04/20 18:20:40 talbert *Added functions to the global state which allowed access to the new *data member m_GreyFileExtension. * *Revision 1.8 2002/04/19 23:04:12 moon *Changed more stuff to get the snake params state synched with the global state. * *Revision 1.7 2002/04/19 20:35:43 moon *Made preproc dialogs check global state and only preproc if parameters have changed. *So no if you hit apply, then ok, it doesn't re process on the ok. *Added global state for preproc params and snake params. Still need to get snake *params synched. * *Revision 1.6 2002/04/18 21:05:20 moon *Changed the IRIS window ROI stuff. Now the ROI is always valid if an image is *loaded, but there is a toggle to show it or not. This will work better with *Konstantin's addition of being able to drag the roi box. Added global state *as appropIriate. * *Revision 1.5 2002/04/01 22:31:30 moon *Added snakeMode and snakeActive to global state *snakeMode is in/out or edge, snakeActive is whether the snake *has been initialized, meaning it should be drawn in the windows * *Revision 1.4 2002/03/26 18:16:40 scheuerm *Added loading and display of preprocessed data: *- added vtkImageDeepCopy function *- added flags indicating which dataset to display in GlobalState *- added flag indicating whether to load gray or preprocessed data * in the GUI class * *Revision 1.3 2002/03/08 14:06:29 moon *Added Header and Log tags to all files **/ itksnap/Logic/Framework/IRISApplication.cxx0000644000076500000240000011572211553073142020225 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISApplication.cxx,v $ Language: C++ Date: $Date: 2011/04/18 17:35:30 $ Version: $Revision: 1.37 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #include "IRISApplication.h" #include "GlobalState.h" #include "GuidedNativeImageIO.h" #include "IRISImageData.h" #include "IRISVectorTypesToITKConversion.h" #include "SNAPImageData.h" #include "MeshObject.h" #include "MeshExportSettings.h" #include "SegmentationStatistics.h" #include "itkImageRegionIterator.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkPasteImageFilter.h" #include "itkImageRegionIterator.h" #include "itkIdentityTransform.h" #include "itkResampleImageFilter.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkImageFileWriter.h" #include "itkFlipImageFilter.h" #include #include "vtkAppendPolyData.h" #include "vtkUnsignedShortArray.h" #include "vtkPointData.h" #include "IRISSlicer.h" #include #include #include IRISApplication ::IRISApplication() : m_UndoManager(4,200000) { // Construct new global state object m_GlobalState = new GlobalState; // Create a new system interface m_SystemInterface = new SystemInterface(); // Initialize the color table m_ColorLabelTable = new ColorLabelTable(); // Contruct the IRIS and SNAP data objects m_IRISImageData = new IRISImageData(this); m_SNAPImageData = NULL; // Set the current IRIS pointer m_CurrentImageData = m_IRISImageData; // Initialize the display-anatomy transformation with RPI code m_DisplayToAnatomyRAI[0] = "RPS"; m_DisplayToAnatomyRAI[1] = "AIR"; m_DisplayToAnatomyRAI[2] = "RIP"; } std::string IRISApplication:: GetImageToAnatomyRAI() { assert(m_CurrentImageData->IsMainLoaded()); return ImageCoordinateGeometry::ConvertDirectionMatrixToClosestRAICode( m_CurrentImageData->GetImageGeometry().GetImageDirectionCosineMatrix()); } std::string IRISApplication:: GetDisplayToAnatomyRAI(unsigned int slice) { return m_DisplayToAnatomyRAI[slice]; } IRISApplication ::~IRISApplication() { delete m_IRISImageData; if(m_SNAPImageData) delete m_SNAPImageData; delete m_GlobalState; delete m_ColorLabelTable; delete m_SystemInterface; } void IRISApplication ::InitializeSNAPImageData(const SNAPSegmentationROISettings &roi, CommandType *progressCommand) { assert(m_SNAPImageData == NULL); assert(m_IRISImageData->IsMainLoaded()); // Create the SNAP image data object m_SNAPImageData = new SNAPImageData(this); // Get the roi chunk from the grey image GreyImageType::Pointer imgNewGrey = m_IRISImageData->GetGrey()->DeepCopyRegion(roi,progressCommand); // Get the size of the region Vector3ui size = to_unsigned_int( Vector3ul(imgNewGrey->GetLargestPossibleRegion().GetSize().GetSize())); // Compute an image coordinate geometry for the region of interest ImageCoordinateGeometry icg( m_IRISImageData->GetImageGeometry().GetImageDirectionCosineMatrix(), m_DisplayToAnatomyRAI, size); // Assign the new wrapper to the target m_SNAPImageData->SetGreyImage( imgNewGrey, icg, m_IRISImageData->GetGrey()->GetNativeMapping()); // Override the interpolator in ROI for label interpolation, or we will get // nonsense SNAPSegmentationROISettings roiLabel = roi; roiLabel.SetInterpolationMethod(SNAPSegmentationROISettings::NEAREST_NEIGHBOR); // Get chunk of the label image LabelImageType::Pointer imgNewLabel = m_IRISImageData->GetSegmentation()->DeepCopyRegion(roiLabel,progressCommand); // Filter the segmentation image to only allow voxels of 0 intensity and // of the current drawing color LabelType passThroughLabel = m_GlobalState->GetDrawingColorLabel(); typedef itk::ImageRegionIterator IteratorType; IteratorType itLabel(imgNewLabel,imgNewLabel->GetBufferedRegion()); while(!itLabel.IsAtEnd()) { if(itLabel.Value() != passThroughLabel) itLabel.Value() = (LabelType) 0; ++itLabel; } // Pass the cleaned up segmentation image to SNAP m_SNAPImageData->SetSegmentationImage(imgNewLabel); // Pass the label description of the drawing label to the SNAP image data m_SNAPImageData->SetColorLabel( m_ColorLabelTable->GetColorLabel(passThroughLabel)); // Assign the intensity mapping function to the Snap data m_SNAPImageData->GetGrey()->SetReferenceIntensityRange( m_IRISImageData->GetGrey()->GetImageMin(), m_IRISImageData->GetGrey()->GetImageMax()); m_SNAPImageData->GetGrey()->CopyIntensityMap(*m_IRISImageData->GetGrey()); m_SNAPImageData->GetGrey()->UpdateIntensityMapFunction(); // Copy the colormap too m_SNAPImageData->GetGrey()->SetColorMap(m_IRISImageData->GetGrey()->GetColorMap()); // Initialize the speed image of the SNAP image data m_SNAPImageData->InitializeSpeed(); // Remember the ROI object m_GlobalState->SetSegmentationROISettings(roi); } void IRISApplication ::SetDisplayToAnatomyRAI(const char *rai0,const char *rai1,const char *rai2) { // Store the new RAI code m_DisplayToAnatomyRAI[0] = rai0; m_DisplayToAnatomyRAI[1] = rai1; m_DisplayToAnatomyRAI[2] = rai2; if(!m_IRISImageData->IsMainLoaded()) return; // Create the appropriate transform and pass it to the IRIS data m_IRISImageData->SetImageGeometry( ImageCoordinateGeometry( m_IRISImageData->GetImageGeometry().GetImageDirectionCosineMatrix(), m_DisplayToAnatomyRAI, m_IRISImageData->GetVolumeExtents())); // Do the same for the SNAP data if needed if(!m_SNAPImageData) return; // Create the appropriate transform and pass it to the SNAP data m_SNAPImageData->SetImageGeometry( ImageCoordinateGeometry( m_SNAPImageData->GetImageGeometry().GetImageDirectionCosineMatrix(), m_DisplayToAnatomyRAI, m_SNAPImageData->GetVolumeExtents())); } void IRISApplication ::UpdateSNAPSpeedImage(SpeedImageType *newSpeedImage, SnakeType snakeMode) { // This has to happen in SNAP mode assert(m_SNAPImageData); // Make sure the dimensions of the speed image are appropriate assert(m_SNAPImageData->GetGrey()->GetImage()->GetBufferedRegion().GetSize() == newSpeedImage->GetBufferedRegion().GetSize()); // Initialize the speed wrapper if(!m_SNAPImageData->IsSpeedLoaded()) m_SNAPImageData->InitializeSpeed(); // Send the speed image to the image data m_SNAPImageData->GetSpeed()->SetImage(newSpeedImage); // Save the snake mode m_GlobalState->SetSnakeMode(snakeMode); // Set the speed as valid m_GlobalState->SetSpeedValid(true); // Set the snake state if(snakeMode == EDGE_SNAKE) { m_SNAPImageData->GetSpeed()->SetModeToEdgeSnake(); } else { m_SNAPImageData->GetSpeed()->SetModeToInsideOutsideSnake(); } } void IRISApplication ::UnloadOverlays() { // unload all the overlays m_IRISImageData->UnloadOverlays(); // for overlay, we don't want to change the cursor location // just force the IRISSlicer to update m_IRISImageData->SetCrosshairs(m_GlobalState->GetCrosshairsPosition()); } void IRISApplication ::UnloadOverlayLast() { // unload the last overlay m_IRISImageData->UnloadOverlayLast(); // for overlay, we don't want to change the cursor location // just force the IRISSlicer to update m_IRISImageData->SetCrosshairs(m_GlobalState->GetCrosshairsPosition()); } void IRISApplication ::ClearIRISSegmentationImage() { // This has to happen in 'pure' IRIS mode assert(m_SNAPImageData == NULL); // Fill the image with blanks this->m_IRISImageData->GetSegmentation()->GetImage()->FillBuffer(0); this->m_IRISImageData->GetSegmentation()->GetImage()->Modified(); // Fill the undo image with blanks too this->m_IRISImageData->GetUndoImage()->GetImage()->FillBuffer(0); this->m_IRISImageData->GetUndoImage()->GetImage()->Modified(); // Clear the undo buffer m_UndoManager.Clear(); } void IRISApplication ::UpdateIRISSegmentationImage(GuidedNativeImageIO *io) { // This has to happen in 'pure' IRIS mode assert(m_SNAPImageData == NULL); // Cast the image to label type CastNativeImageToScalar caster; LabelImageType::Pointer imgLabel = caster(io); // The header of the label image is made to match that of the grey image imgLabel->SetOrigin(m_CurrentImageData->GetMain()->GetImageBase()->GetOrigin()); imgLabel->SetSpacing(m_CurrentImageData->GetMain()->GetImageBase()->GetSpacing()); imgLabel->SetDirection(m_CurrentImageData->GetMain()->GetImageBase()->GetDirection()); // Update the iris data m_IRISImageData->SetSegmentationImage(imgLabel); // Check that the range is valid #if MAX_COLOR_LABELS < 0xffff if(m_IRISImageData->GetSegmentation()->GetImageMax() > MAX_COLOR_LABELS) { m_IRISImageData->GetSegmentation()->GetImage()->FillBuffer(0); throw IRISException( "Segmentation image has more labels than maximum allowed (%d)", MAX_COLOR_LABELS); } #endif // Update the color labels, so that for every label in the image // there is a valid color label LabelImageWrapper::ConstIterator it = m_IRISImageData->GetSegmentation()->GetImageConstIterator(); for( ; !it.IsAtEnd(); ++it) if(!m_ColorLabelTable->IsColorLabelValid(it.Get())) m_ColorLabelTable->SetColorLabelValid(it.Get(), true); // Reset the UNDO manager m_UndoManager.Clear(); } LabelType IRISApplication ::DrawOverLabel(LabelType iTarget) { // Get the current merge settings CoverageModeType iMode = m_GlobalState->GetCoverageMode(); LabelType iDrawing = m_GlobalState->GetDrawingColorLabel(); LabelType iDrawOver = m_GlobalState->GetOverWriteColorLabel(); // Assign the output intensity based on the current drawing mode bool visible = m_ColorLabelTable->GetColorLabel(iTarget).IsVisible(); // If mode is paint over all, the victim is overridden return ((iMode == PAINT_OVER_ALL) || (iMode == PAINT_OVER_COLORS && visible) || (iMode == PAINT_OVER_ONE && iDrawOver == iTarget)) ? iDrawing : iTarget; } void IRISApplication ::UpdateIRISWithSnapImageData(CommandType *progressCommand) { assert(m_SNAPImageData != NULL); // Get pointers to the source and destination images typedef LevelSetImageWrapper::ImageType SourceImageType; typedef LabelImageWrapper::ImageType TargetImageType; // If the voxel size of the image does not match the voxel size of the // main image, we need to resample the region SourceImageType::Pointer source = m_SNAPImageData->GetSnake()->GetImage(); TargetImageType::Pointer target = m_IRISImageData->GetSegmentation()->GetImage(); // Construct are region of interest into which the result will be pasted SNAPSegmentationROISettings roi = m_GlobalState->GetSegmentationROISettings(); // If the ROI has been resampled, resample the segmentation in reverse direction if(roi.GetResampleFlag()) { // Create a resampling filter typedef itk::ResampleImageFilter ResampleFilterType; ResampleFilterType::Pointer fltSample = ResampleFilterType::New(); // Initialize the resampling filter with an identity transform fltSample->SetInput(source); fltSample->SetTransform(itk::IdentityTransform::New()); // Typedefs for interpolators typedef itk::NearestNeighborInterpolateImageFunction< SourceImageType,double> NNInterpolatorType; typedef itk::LinearInterpolateImageFunction< SourceImageType,double> LinearInterpolatorType; typedef itk::BSplineInterpolateImageFunction< SourceImageType,double> CubicInterpolatorType; // More typedefs are needed for the sinc interpolator const unsigned int VRadius = 5; typedef itk::Function::HammingWindowFunction WindowFunction; typedef itk::ConstantBoundaryCondition Condition; typedef itk::WindowedSincInterpolateImageFunction< SourceImageType, VRadius, WindowFunction, Condition, double> SincInterpolatorType; // Choose the interpolator switch(roi.GetInterpolationMethod()) { case SNAPSegmentationROISettings::NEAREST_NEIGHBOR : fltSample->SetInterpolator(NNInterpolatorType::New()); break; case SNAPSegmentationROISettings::TRILINEAR : fltSample->SetInterpolator(LinearInterpolatorType::New()); break; case SNAPSegmentationROISettings::TRICUBIC : fltSample->SetInterpolator(CubicInterpolatorType::New()); break; case SNAPSegmentationROISettings::SINC_WINDOW_05 : fltSample->SetInterpolator(SincInterpolatorType::New()); break; }; // Set the image sizes and spacing. We are creating an image of the // dimensions of the ROI defined in the IRIS image space. fltSample->SetSize(roi.GetROI().GetSize()); fltSample->SetOutputSpacing(target->GetSpacing()); fltSample->SetOutputOrigin(source->GetOrigin()); fltSample->SetOutputDirection(source->GetDirection()); // Watch the segmentation progress if(progressCommand) fltSample->AddObserver(itk::AnyEvent(),progressCommand); // Set the unknown intensity to positive value fltSample->SetDefaultPixelValue(4.0f); // Perform resampling fltSample->UpdateLargestPossibleRegion(); // Change the source to the output source = fltSample->GetOutput(); } // Create iterators for copying from one to the other typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator TargetIteratorType; SourceIteratorType itSource(source,source->GetLargestPossibleRegion()); TargetIteratorType itTarget(target,roi.GetROI()); // Figure out which color draws and which color is clear unsigned int iClear = m_GlobalState->GetPolygonInvert() ? 1 : 0; // Construct a merge table that contains an output intensity for every // possible combination of two input intensities (note that snap image only // has two possible intensities LabelType mergeTable[2][MAX_COLOR_LABELS]; // Perform the merge for(unsigned int i=0;iGetDrawingColorLabel()) ? i : 0; // If mode is paint over all, the victim is overridden mergeTable[1-iClear][i] = DrawOverLabel((LabelType) i); } // Go through both iterators, copy the new over the old itSource.GoToBegin(); itTarget.GoToBegin(); while(!itSource.IsAtEnd()) { // Get the two voxels LabelType &voxIRIS = itTarget.Value(); float voxSNAP = itSource.Value(); // Check that we're ok (debug mode only) assert(!itTarget.IsAtEnd()); // Perform the merge voxIRIS = mergeTable[voxSNAP <= 0 ? 1 : 0][voxIRIS]; // Iterate ++itSource; ++itTarget; } // The target has been modified target->Modified(); } void IRISApplication ::SetCursorPosition(const Vector3ui cursor) { m_GlobalState->SetCrosshairsPosition(cursor); this->GetCurrentImageData()->SetCrosshairs(cursor); } Vector3ui IRISApplication ::GetCursorPosition() const { return m_GlobalState->GetCrosshairsPosition(); } void IRISApplication ::StoreUndoPoint(const char *text) { // Set the current state as the undo point. We store the difference between // the last 'undo' image and the current segmentation image, and then copy // the current segmentation image into the undo image LabelImageWrapper *undo = m_IRISImageData->GetUndoImage(); LabelImageWrapper *seg = m_IRISImageData->GetSegmentation(); LabelType *dseg = seg->GetVoxelPointer(); LabelType *dundo = undo->GetVoxelPointer(); size_t n = seg->GetNumberOfVoxels(); // Create the Undo delta object UndoManagerType::Delta *delta = new UndoManagerType::Delta(); // Copy and encode for(size_t i = 0; i < n; i++) { LabelType vSrc = dseg[i], vDst = dundo[i]; delta->Encode(vSrc - vDst); dundo[i] = vSrc; } // Important last step! delta->FinishEncoding(); // Set modified flag on the undo image undo->GetImage()->Modified(); // Add the delta object m_UndoManager.AppendDelta(delta); } void IRISApplication ::ClearUndoPoints() { m_UndoManager.Clear(); } bool IRISApplication ::IsUndoPossible() { return m_UndoManager.IsUndoPossible(); } void IRISApplication ::Undo() { // In order to undo, we must take the 'current' delta and apply // it to the image UndoManagerType::Delta *delta = m_UndoManager.GetDeltaForUndo(); LabelImageWrapper *imUndo = m_IRISImageData->GetUndoImage(); LabelImageWrapper *imSeg = m_IRISImageData->GetSegmentation(); LabelType *dundo = imUndo->GetVoxelPointer(); LabelType *dseg = imSeg->GetVoxelPointer(); // Applying the delta means adding for(size_t i = 0; i < delta->GetNumberOfRLEs(); i++) { size_t n = delta->GetRLELength(i); LabelType d = delta->GetRLEValue(i); if(d == 0) { dundo += n; dseg += n; } else { for(size_t j = 0; j < n; j++) { *dundo -= d; *dseg = *dundo; ++dundo; ++dseg; } } } // Set modified flags imSeg->GetImage()->Modified(); imUndo->GetImage()->Modified(); } bool IRISApplication ::IsRedoPossible() { return m_UndoManager.IsRedoPossible(); } void IRISApplication ::Redo() { // In order to undo, we must take the 'current' delta and apply // it to the image UndoManagerType::Delta *delta = m_UndoManager.GetDeltaForRedo(); LabelImageWrapper *imUndo = m_IRISImageData->GetUndoImage(); LabelImageWrapper *imSeg = m_IRISImageData->GetSegmentation(); LabelType *dundo = imUndo->GetVoxelPointer(); LabelType *dseg = imSeg->GetVoxelPointer(); // Applying the delta means adding for(size_t i = 0; i < delta->GetNumberOfRLEs(); i++) { size_t n = delta->GetRLELength(i); LabelType d = delta->GetRLEValue(i); if(d == 0) { dundo += n; dseg += n; } else { for(size_t j = 0; j < n; j++) { *dundo += d; *dseg = *dundo; ++dundo; ++dseg; } } } // Set modified flags imSeg->GetImage()->Modified(); imUndo->GetImage()->Modified(); } void IRISApplication ::ReleaseSNAPImageData() { assert(m_SNAPImageData && m_CurrentImageData != m_SNAPImageData); delete m_SNAPImageData; m_SNAPImageData = NULL; } void IRISApplication ::SetCurrentImageDataToIRIS() { assert(m_IRISImageData); m_CurrentImageData = m_IRISImageData; } void IRISApplication ::SetCurrentImageDataToSNAP() { assert(m_SNAPImageData); m_CurrentImageData = m_SNAPImageData; } size_t IRISApplication ::GetImageDirectionForAnatomicalDirection(AnatomicalDirection iAnat) { std::string myrai = this->GetImageToAnatomyRAI(); string rai1 = "SRA", rai2 = "ILP"; char c1 = rai1[iAnat], c2 = rai2[iAnat]; for(size_t j = 0; j < 3; j++) if(myrai[j] == c1 || myrai[j] == c2) return j; assert(0); return 0; } size_t IRISApplication ::GetDisplayWindowForAnatomicalDirection( AnatomicalDirection iAnat) { string rai1 = "SRA", rai2 = "ILP"; char c1 = rai1[iAnat], c2 = rai2[iAnat]; for(size_t j = 0; j < 3; j++) { char sd = m_DisplayToAnatomyRAI[j][2]; if(sd == c1 || sd == c2) return j; } assert(0); return 0; } void IRISApplication ::ExportSlice(AnatomicalDirection iSliceAnat, const char *file) { // Get the slice index in image coordinates size_t iSliceImg = GetImageDirectionForAnatomicalDirection(iSliceAnat); // Find the slicer that slices along that direction GreyImageWrapper::DisplaySlicePointer imgGrey = NULL; for(size_t i = 0; i < 3; i++) { if(iSliceImg == m_CurrentImageData->GetGrey()->GetSlicer(i)->GetSliceDirectionImageAxis()) { imgGrey = m_CurrentImageData->GetGrey()->GetDisplaySlice(i); break; } } assert(imgGrey); // Flip the image in the Y direction typedef itk::FlipImageFilter FlipFilter; FlipFilter::Pointer fltFlip = FlipFilter::New(); fltFlip->SetInput(imgGrey); FlipFilter::FlipAxesArrayType arrFlips; arrFlips[0] = false; arrFlips[1] = true; fltFlip->SetFlipAxes(arrFlips); // Create a writer for saving the image typedef itk::ImageFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetInput(fltFlip->GetOutput()); writer->SetFileName(file); writer->Update(); } void IRISApplication ::ExportSegmentationStatistics(const char *file) throw(itk::ExceptionObject) { // Make sure that the segmentation image exists assert(m_CurrentImageData->IsSegmentationLoaded()); SegmentationStatistics stats; stats.Compute(m_CurrentImageData); // Open the selected file for writing std::ofstream fout(file); // Check if the file is readable if(!fout.good()) throw itk::ExceptionObject(__FILE__, __LINE__, "File can not be opened for writing"); try { stats.ExportLegacy(fout, *m_ColorLabelTable); } catch(...) { throw itk::ExceptionObject(__FILE__, __LINE__, "File can not be written"); } fout.close(); } void IRISApplication ::ExportSegmentationMesh(const MeshExportSettings &sets, itk::Command *progress) throw(itk::ExceptionObject) { // Based on the export settings, we will export one of the labels or all labels MeshObject mob; mob.Initialize(this); mob.GenerateVTKMeshes(progress); // If in SNAP mode, just save the first mesh if(m_SNAPImageData) { // Get the VTK mesh for the label vtkPolyData *mesh = mob.GetVTKMesh(0); // Export the mesh GuidedMeshIO io; Registry rFormat = sets.GetMeshFormat(); io.SaveMesh(sets.GetMeshFileName().c_str(), rFormat, mesh); } // If only one mesh is to be exported, life is easy else if(sets.GetFlagSingleLabel()) { for(size_t i = 0; i < mob.GetNumberOfVTKMeshes(); i++) { if(mob.GetVTKMeshLabel(i) == sets.GetExportLabel()) { // Get the VTK mesh for the label vtkPolyData *mesh = mob.GetVTKMesh(i); // Export the mesh GuidedMeshIO io; Registry rFormat = sets.GetMeshFormat(); io.SaveMesh(sets.GetMeshFileName().c_str(), rFormat, mesh); } } } else if(sets.GetFlagSingleScene()) { // Create an append filter vtkAppendPolyData *append = vtkAppendPolyData::New(); std::vector scalarArray; for(size_t i = 0; i < mob.GetNumberOfVTKMeshes(); i++) { // Get the VTK mesh for the label vtkPolyData *mesh = mob.GetVTKMesh(i); vtkUnsignedShortArray *scalar = vtkUnsignedShortArray::New(); scalar->SetNumberOfComponents(1); scalar->Allocate(mesh->GetNumberOfPoints()); for(int j = 0; j < mesh->GetNumberOfPoints(); j++) scalar->InsertNextTuple1(mob.GetVTKMeshLabel(i)); mesh->GetPointData()->SetScalars(scalar); scalarArray.push_back(scalar); append->AddInput(mesh); } append->Update(); // Export the mesh GuidedMeshIO io; Registry rFormat = sets.GetMeshFormat(); io.SaveMesh(sets.GetMeshFileName().c_str(), rFormat, append->GetOutput()); append->Delete(); for(size_t i = 0; i < scalarArray.size(); i++) scalarArray[i]->Delete(); } else { // Take apart the filename std::string full = itksys::SystemTools::CollapseFullPath(sets.GetMeshFileName().c_str()); std::string path = itksys::SystemTools::GetFilenamePath(full.c_str()); std::string file = itksys::SystemTools::GetFilenameWithoutExtension(full.c_str()); std::string extn = itksys::SystemTools::GetFilenameExtension(full.c_str()); std::string prefix = file; // Are the last 5 characters of the filename numeric? if(file.length() >= 5) { string suffix = file.substr(file.length()-5,5); if(count_if(suffix.begin(), suffix.end(), isdigit) == 5) prefix = file.substr(0, file.length()-5); } // Loop, saving each mesh into a filename for(size_t i = 0; i < mob.GetNumberOfVTKMeshes(); i++) { // Get the VTK mesh for the label vtkPolyData *mesh = mob.GetVTKMesh(i); // Generate filename char outfn[4096]; sprintf(outfn, "%s/%s%05d%s", path.c_str(), prefix.c_str(), mob.GetVTKMeshLabel(i), extn.c_str()); // Export the mesh GuidedMeshIO io; Registry rFormat = sets.GetMeshFormat(); io.SaveMesh(outfn, rFormat, mesh); } } mob.DiscardVTKMeshes(); } size_t IRISApplication ::ReplaceLabel(LabelType drawing, LabelType drawover) { // Get the label image assert(m_CurrentImageData->IsSegmentationLoaded()); LabelImageWrapper::ImagePointer imgLabel = m_CurrentImageData->GetSegmentation()->GetImage(); // Get the number of voxels size_t nvoxels = 0; // Update the segmentation typedef itk::ImageRegionIterator< LabelImageWrapper::ImageType> IteratorType; for(IteratorType it(imgLabel, imgLabel->GetBufferedRegion()); !it.IsAtEnd(); ++it) { if(it.Get() == drawover) { it.Set(drawing); ++nvoxels; } } // Register that the image has been updated imgLabel->Modified(); return nvoxels; } void IRISApplication ::RelabelSegmentationWithCutPlane(const Vector3d &normal, double intercept) { // Get the label image LabelImageWrapper::ImagePointer imgLabel = m_CurrentImageData->GetSegmentation()->GetImage(); // Get an iterator for the image typedef itk::ImageRegionIteratorWithIndex< LabelImageWrapper::ImageType> IteratorType; IteratorType it(imgLabel, imgLabel->GetBufferedRegion()); // Compute a label mapping table based on the color labels LabelType table[MAX_COLOR_LABELS]; // The clear label does not get painted over, no matter what table[0] = 0; // The other labels get painted over, depending on current settings for(unsigned int i = 1; i < MAX_COLOR_LABELS; i++) table[i] = DrawOverLabel(i); // Adjust the intercept by 0.5 for voxel offset intercept -= 0.5 * (normal[0] + normal[1] + normal[2]); // Iterate over the image, relabeling labels on one side of the plane while(!it.IsAtEnd()) { // Compute the distance to the plane const long *index = it.GetIndex().GetIndex(); double distance = index[0]*normal[0] + index[1]*normal[1] + index[2]*normal[2] - intercept; // Check the side of the plane if(distance > 0) { LabelType &voxel = it.Value(); voxel = table[voxel]; } // Next voxel ++it; } // Register that the image has been updated imgLabel->Modified(); } int IRISApplication ::GetRayIntersectionWithSegmentation(const Vector3d &point, const Vector3d &ray, Vector3i &hit) const { // Get the label wrapper LabelImageWrapper *xLabelWrapper = m_CurrentImageData->GetSegmentation(); assert(xLabelWrapper->IsInitialized()); Vector3ui lIndex; Vector3ui lSize = xLabelWrapper->GetSize(); double delta[3][3] = {{0.,0.,0.},{0.,0.,0.},{0.,0.,0.}}, dratio[3] = {0., 0., 0.}; int signrx, signry, signrz; double rx = ray[0]; double ry = ray[1]; double rz = ray[2]; double rlen = rx*rx+ry*ry+rz*rz; if(rlen == 0) return -1; double rfac = 1.0 / sqrt(rlen); rx *= rfac; ry *= rfac; rz *= rfac; if (rx >=0) signrx = 1; else signrx = -1; if (ry >=0) signry = 1; else signry = -1; if (rz >=0) signrz = 1; else signrz = -1; // offset everything by (.5, .5) [becuz samples are at center of voxels] // this offset will put borders of voxels at integer values // we will work with this offset grid and offset back to check samples // we really only need to offset "point" double px = point[0]+0.5; double py = point[1]+0.5; double pz = point[2]+0.5; // get the starting point within data extents int c = 0; while ( (px < 0 || px >= lSize[0]|| py < 0 || py >= lSize[1]|| pz < 0 || pz >= lSize[2]) && c < 10000) { px += rx; py += ry; pz += rz; c++; } if (c >= 9999) return -1; // walk along ray to find intersection with any voxel with val > 0 while ( (px >= 0 && px < lSize[0]&& py >= 0 && py < lSize[1] && pz >= 0 && pz < lSize[2]) ) { // offset point by (-.5, -.5) [to account for earlier offset] and // get the nearest sample voxel within unit cube around (px,py,pz) // lx = my_round(px-0.5); // ly = my_round(py-0.5); // lz = my_round(pz-0.5); lIndex[0] = (int)px; lIndex[1] = (int)py; lIndex[2] = (int)pz; LabelType hitlabel = m_CurrentImageData->GetSegmentation()->GetVoxel(lIndex); const ColorLabel &cl = m_ColorLabelTable->GetColorLabel(hitlabel); if (cl.IsValid() && cl.IsVisible()) { hit[0] = lIndex[0]; hit[1] = lIndex[1]; hit[2] = lIndex[2]; return 1; } // BEGIN : walk along ray to border of next voxel touched by ray // compute path to YZ-plane surface of next voxel if (rx == 0) { // ray is parallel to 0 axis delta[0][0] = 9999; } else { delta[0][0] = (int)(px+signrx) - px; } // compute path to XZ-plane surface of next voxel if (ry == 0) { // ray is parallel to 1 axis delta[1][0] = 9999; } else { delta[1][1] = (int)(py+signry) - py; dratio[1] = delta[1][1]/ry; delta[1][0] = dratio[1] * rx; } // compute path to XY-plane surface of next voxel if (rz == 0) { // ray is parallel to 2 axis delta[2][0] = 9999; } else { delta[2][2] = (int)(pz+signrz) - pz; dratio[2] = delta[2][2]/rz; delta[2][0] = dratio[2] * rx; } // choose the shortest path if ( fabs(delta[0][0]) <= fabs(delta[1][0]) && fabs(delta[0][0]) <= fabs(delta[2][0]) ) { dratio[0] = delta[0][0]/rx; delta[0][1] = dratio[0] * ry; delta[0][2] = dratio[0] * rz; px += delta[0][0]; py += delta[0][1]; pz += delta[0][2]; } else if ( fabs(delta[1][0]) <= fabs(delta[0][0]) && fabs(delta[1][0]) <= fabs(delta[2][0]) ) { delta[1][2] = dratio[1] * rz; px += delta[1][0]; py += delta[1][1]; pz += delta[1][2]; } else { //if (fabs(delta[2][0] <= fabs(delta[0][0] && fabs(delta[2][0] <= fabs(delta[0][0]) delta[2][1] = dratio[2] * ry; px += delta[2][0]; py += delta[2][1]; pz += delta[2][2]; } // END : walk along ray to border of next voxel touched by ray } // while ( (px return 0; } IRISApplication::MainImageType IRISApplication ::AddIRISOverlayImage(GuidedNativeImageIO *io, MainImageType force_type) { assert(m_SNAPImageData == NULL); assert(m_IRISImageData->IsMainLoaded()); assert(io->IsNativeImageLoaded()); // If the input type is 'ANY', determine based on number of components MainImageType type = (force_type == MAIN_ANY) ? (io->GetNumberOfComponentsInNativeImage() == 3 ? MAIN_RGB : MAIN_SCALAR) : force_type; // Cast the native image to desired format and pass on to IRISImageData if(type == MAIN_SCALAR) { // Rescale the image to grey RescaleNativeImageToScalar rescaler; GreyImageType::Pointer imgGrey = rescaler(io); GreyTypeToNativeFunctor mapper(rescaler.GetNativeScale(), rescaler.GetNativeShift()); // At this point, deallocate the native image, so that we don't use more memory io->DeallocateNativeImage(); // Add the image as the current grayscale overlay m_IRISImageData->SetGreyOverlay(imgGrey, mapper); } else if(type == MAIN_RGB) { // Cast image to RGB CastNativeImageToRGB caster; RGBImageType::Pointer imgRGB = caster(io); // At this point, deallocate the native image, so that we don't use more memory io->DeallocateNativeImage(); // Add the image as the current RGB overlay m_IRISImageData->SetRGBOverlay(imgRGB); } else throw itk::ExceptionObject("Unsupported overlay image type"); // for overlay, we don't want to change the cursor location // just force the IRISSlicer to update m_IRISImageData->SetCrosshairs(m_GlobalState->GetCrosshairsPosition()); // Return the type loaded as return type; } IRISApplication::MainImageType IRISApplication ::UpdateIRISMainImage(GuidedNativeImageIO *io, MainImageType force_type) { // This has to happen in 'pure' IRIS mode assert(m_SNAPImageData == NULL); // If the input type is 'ANY', determine based on number of components MainImageType type = (force_type == MAIN_ANY) ? (io->GetNumberOfComponentsInNativeImage() == 3 ? MAIN_RGB : MAIN_SCALAR) : force_type; // Get the size of the image as a vector of uint Vector3ui size = to_unsigned_int(Vector3ul( io->GetNativeImage()->GetBufferedRegion().GetSize().GetSize())); // Compute the new image geometry for the IRIS data ImageCoordinateGeometry icg( io->GetNativeImage()->GetDirection().GetVnlMatrix(), m_DisplayToAnatomyRAI, size); // Cast the native image to desired format and pass on to IRISImageData if(type == MAIN_SCALAR) { // Rescale the image to grey RescaleNativeImageToScalar rescaler; GreyImageType::Pointer imgGrey = rescaler(io); GreyTypeToNativeFunctor mapper(rescaler.GetNativeScale(), rescaler.GetNativeShift()); // At this point, deallocate the native image, so that we don't use more memory io->DeallocateNativeImage(); // Set the image as the current grayscale image m_IRISImageData->SetGreyImage(imgGrey, icg, mapper); // Update the preprocessing settings in the global state m_GlobalState->SetEdgePreprocessingSettings( EdgePreprocessingSettings::MakeDefaultSettings()); m_GlobalState->SetThresholdSettings( ThresholdSettings::MakeDefaultSettings( m_IRISImageData->GetGrey())); } else if(type == MAIN_RGB) { // Cast to RGB CastNativeImageToRGB caster; RGBImageType::Pointer imgRGB = caster(io); m_IRISImageData->SetRGBImage(imgRGB,icg); // At this point, deallocate the native image, so that we don't use more memory io->DeallocateNativeImage(); } else throw itk::ExceptionObject("Unsupported main image type"); // Update the crosshairs position Vector3ui cursor = size; cursor /= 2; m_IRISImageData->SetCrosshairs(cursor); // TODO: Unify this! m_GlobalState->SetCrosshairsPosition(cursor); // Reset the UNDO manager m_UndoManager.Clear(); return type; } IRISApplication::MainImageType IRISApplication ::LoadMainImage(const char *filename, MainImageType force_type) { // Load the settings associated with this file Registry regFull; m_SystemInterface->FindRegistryAssociatedWithFile(filename, regFull); // Get the folder dealing with grey image properties Registry &folder = regFull.Folder("Files.Grey"); // Create a native image IO object GuidedNativeImageIO io; io.ReadNativeImage(filename, folder); // Detemine the type MainImageType type = UpdateIRISMainImage(&io, force_type); if(type == MAIN_SCALAR) { // Save the filename for the UI m_GlobalState->SetGreyFileName(filename); } else if(type == MAIN_RGB) { m_GlobalState->SetRGBFileName(filename); } else throw itk::ExceptionObject("Unsupported main image type"); return type; } IRISApplication::MainImageType IRISApplication ::LoadOverlayImage(const char *filename, MainImageType force_type) { // Load the settings associated with this file Registry regFull; m_SystemInterface->FindRegistryAssociatedWithFile(filename, regFull); // Get the folder dealing with grey image properties Registry &folder = regFull.Folder("Files.Grey"); // Create a native image IO object GuidedNativeImageIO io; io.ReadNativeImage(filename, folder); // Detemine the type return AddIRISOverlayImage(&io, force_type); } /* void IRISApplication ::LoadRGBImageFile(const char *filename, const bool isMain) { // Load the settings associated with this file Registry regFull; m_SystemInterface->FindRegistryAssociatedWithFile(filename, regFull); // Get the folder dealing with grey image properties Registry ®RGB = regFull.Folder("Files.RGB"); // Create the image reader GuidedImageIO io; // Load the image (exception may occur here) RGBImageType::Pointer imgRGB = io.ReadImage(filename, regRGB, false); if (isMain) { // Set the image as the current main image UpdateIRISRGBImage(imgRGB); } else { // Set the image as the overlay UpdateIRISRGBOverlay(imgRGB); } // Save the filename for the UI m_GlobalState->SetRGBFileName(filename); } */ void IRISApplication ::LoadLabelImageFile(const char *filename) { // Load the settings associated with this file Registry regFull; m_SystemInterface->FindRegistryAssociatedWithFile(filename, regFull); // Get the folder dealing with grey image properties // TODO: Figure out something about this!!! Registry ®Grey = regFull.Folder("Files.Grey"); // Read the image in native format GuidedNativeImageIO io; io.ReadNativeImage(filename, regGrey); // Set the image as the current grayscale image UpdateIRISSegmentationImage(&io); // Save the filename for the UI m_GlobalState->SetSegmentationFileName(filename); m_GlobalState->SetLastAssociatedPreprocessingFileName(filename); } void IRISApplication ::ReorientImage(vnl_matrix_fixed inDirection) { // This should only be possible in IRIS mode assert(m_CurrentImageData == m_IRISImageData); // The main image should be loaded at this point assert(m_CurrentImageData->IsMainLoaded()); // Compute a new coordinate transform object ImageCoordinateGeometry icg( inDirection, m_DisplayToAnatomyRAI, m_CurrentImageData->GetMain()->GetSize()); // Send this coordinate transform to the image data m_CurrentImageData->SetImageGeometry(icg); } itksnap/Logic/Framework/IRISApplication.h0000644000076500000240000002517611245105574017660 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISApplication.h,v $ Language: C++ Date: $Date: 2009/08/26 01:10:20 $ Version: $Revision: 1.18 $ 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 "ImageCoordinateTransform.h" #include "itkImageRegion.h" #include "itkExceptionObject.h" #include "GlobalState.h" #include "ColorLabelTable.h" #include "itkCommand.h" #include "SystemInterface.h" #include "UndoDataManager.h" // #include "itkImage.h" // Forward reference to the classes pointed at class GenericImageData; class IRISImageData; class SNAPImageData; class MeshExportSettings; class GuidedNativeImageIO; namespace itk { template class OrientedImage; } /** * \class IRISApplication * \brief This class encapsulates the highest level login of SNAP and IRIS. * * TODO: Organize the interaction between this class, IRISImageData and SNAPImageData * in a more intuitive way. * * 'RAI' codes used by this class: * The code is a string that describes the transform from image space to patient * coordinate system as three letters from RLAPIS. For instance, PSR * means that the image origin is at the posterior-superior-right corner * of the image coordinate system and that the x axis maps to A-P axis, * y to I-S and x to R-L. * * \sa IRISImageData * \sa SNAPImageData */ class IRISApplication { public: // Typedefs typedef itk::ImageRegion<3> RegionType; typedef itk::Size<3> SizeType; typedef itk::OrientedImage GreyImageType; typedef itk::OrientedImage RGBImageType; typedef itk::OrientedImage LabelImageType; typedef itk::OrientedImage SpeedImageType; typedef itk::Command CommandType; typedef UndoDataManager UndoManagerType; // The main image can be of these types enum MainImageType { MAIN_SCALAR, MAIN_RGB, MAIN_ANY }; /** * Constructor for the IRIS/SNAP application */ IRISApplication(); /** * Destructor for the application */ virtual ~IRISApplication(); /** * Get image data related to IRIS operations */ irisGetMacro(IRISImageData,IRISImageData *); /** * Get image data related to SNAP operations */ irisGetMacro(SNAPImageData,SNAPImageData *); /** * Get the image data currently used */ irisGetMacro(CurrentImageData,GenericImageData *); /** * Enter the IRIS mode */ void SetCurrentImageDataToIRIS(); /** * Enter the SNAP mode */ void SetCurrentImageDataToSNAP(); /** * Set a new main image for IRIS. This method is called to load either grey or * RGB image data into IRISImageData. The parameter is the GuidedNativeImageIO, * which holds an image in native format. The second parameter specified whether * to force RGB or grey image, or to determine image type based on the data. */ MainImageType UpdateIRISMainImage( GuidedNativeImageIO *nativeIO, MainImageType force_type); /** * Add an overlay image into IRIS. This method is called to load either grey or * RGB image data into IRISImageData. The parameter is the GuidedNativeImageIO, * which holds an image in native format. The second parameter specified whether * to force RGB or grey image, or to determine image type based on the data. */ MainImageType AddIRISOverlayImage( GuidedNativeImageIO *nativeIO, MainImageType force_type); /** * Set a new grey image for the IRIS Image data. This method is called when the * grey image is loaded. The prerequisite to this method is that the SNAP data * not be active (CurrentImageData == IRISImageData). */ void UnloadOverlays(); void UnloadOverlayLast(); /** * Update the IRIS image data with an external segmentation image (e.g., * loaded from a file). */ void UpdateIRISSegmentationImage(GuidedNativeImageIO *io); /** * Clear the IRIS segmentation image */ void ClearIRISSegmentationImage(); /** * Update the SNAP image data with an external speed image (e.g., * loaded from a file). */ void UpdateSNAPSpeedImage(SpeedImageType *newSpeedImage, SnakeType snakeMode); /** * Initialize SNAP Image data using region of interest extents, and a new * voxel size. */ void InitializeSNAPImageData(const SNAPSegmentationROISettings &roi, CommandType *progressCommand = NULL); /** * Update IRIS image data with the segmentation contained in the SNAP image * data. */ void UpdateIRISWithSnapImageData(CommandType *progressCommand = NULL); /** * Get the segmentation label data */ irisGetMacro(ColorLabelTable, ColorLabelTable *); /** Release the SNAP Image data */ void ReleaseSNAPImageData(); /** Update the display-anatomy mapping as an RAI code */ void SetDisplayToAnatomyRAI(const char *rai0,const char *rai1,const char *rai2); /** Get the current image to anatomy RAI code */ std::string GetImageToAnatomyRAI(); /** Get the current display to anatomy RAI code */ std::string GetDisplayToAnatomyRAI(unsigned int slice); /** Get the image axis for a given anatomical direction */ size_t GetImageDirectionForAnatomicalDirection( AnatomicalDirection iAnat); /** Get the display window corresponding to an anatomical direction */ size_t GetDisplayWindowForAnatomicalDirection( AnatomicalDirection iAnat); /** * Get the global state object */ irisGetMacro(GlobalState, GlobalState *); /** * Get the system interface */ irisGetMacro(SystemInterface, SystemInterface *); /** * Set the current cursor position. This will cause all the active image * wrappers to update their current slice numbers */ void SetCursorPosition(const Vector3ui cursor); /** * Get the cursor position */ Vector3ui GetCursorPosition() const; /** * Export the current slice of the image into a file */ void ExportSlice(AnatomicalDirection iSliceAnatomy, const char *file); /** Export voxel statistis to a file */ void ExportSegmentationStatistics(const char *file) throw(itk::ExceptionObject); /** * Export the 3D mesh to a file, using settings passed in the * MeshExportSettings structure. */ void ExportSegmentationMesh(const MeshExportSettings &sets, itk::Command *cmd) throw(itk::ExceptionObject); /** * This method is used to selectively override labels in a target * segmentation image with the current drawing color. It uses the * current coverage mode to determine whether to override the pixel * or to keep it */ LabelType DrawOverLabel(LabelType iTarget); /** * Really simple replacement of one label with another. Returns the * number of voxels changed. */ size_t ReplaceLabel(LabelType drawing, LabelType drawover); /* * Cut the segmentation using a plane and relabed the segmentation * on the side of that plane */ void RelabelSegmentationWithCutPlane( const Vector3d &normal, double intercept); /** * Compute the intersection of the segmentation with a ray */ int GetRayIntersectionWithSegmentation(const Vector3d &point, const Vector3d &ray, Vector3i &hit) const; /** * Load the main image from file. You can either specify that the main * image is of a given type (grey vs. rgb) or you can let the program * decide dynamically, based on the number of components in the file */ MainImageType LoadMainImage(const char *filename, MainImageType force_type); MainImageType LoadOverlayImage(const char *filename, MainImageType force_type); /** * This is the most high-level method to load a segmentation image. The * segmentation image can only be loaded after the grey image has been * loaded and it must have the same dimensions * * This function is deprecated and replaced by the more robust version * in the ImageIOWizardLogic class! * */ void LoadLabelImageFile(const char *filename); /** * Store the current state as an undo point, allowing the user to revert * to this state at a later point. The state in this context is just the * segmentation image in IRIS. */ void StoreUndoPoint(const char *text); /** * Clear all the undo points, e.g., after an operation that can not be * undone */ void ClearUndoPoints(); /** Check whether undo is possible */ bool IsUndoPossible(); /** Check whether undo is possible */ bool IsRedoPossible(); /** Undo (revert to last stored undo point) */ void Undo(); /** Redo (undo the undo) */ void Redo(); irisGetMacro(UndoManager, const UndoManagerType &); /** * Reorient the main image (and all overlays) */ void ReorientImage(vnl_matrix_fixed inDirection); private: // Image data objects GenericImageData *m_CurrentImageData; IRISImageData *m_IRISImageData; SNAPImageData *m_SNAPImageData; // Color label data ColorLabelTable *m_ColorLabelTable; // Global state object // TODO: Incorporate GlobalState into IRISApplication more nicely GlobalState *m_GlobalState; // SystemInterface used to get things from the system SystemInterface *m_SystemInterface; /** RAI between anatomy space and image space */ std::string m_DisplayToAnatomyRAI[3]; // Undo data manager. Perhaps this should really be in IRISImageData, but // there is a lot of stuff here that is ambiguous in this way. The manager // stores 'deltas', i.e., differences between states of the segmentation // image. These deltas are compressed, allowing us to store a bunch of // undo steps with little cost in performance or memory UndoManagerType m_UndoManager; }; itksnap/Logic/Framework/IRISImageData.cxx0000644000076500000240000000501311246331623017565 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISImageData.cxx,v $ Language: C++ Date: $Date: 2009/08/29 23:02:43 $ Version: $Revision: 1.8 $ 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. =========================================================================*/ // ITK Includes #include "IRISImageData.h" void IRISImageData ::SetSegmentationImage(LabelImageType *newLabelImage) { // Set the new segmentation image GenericImageData::SetSegmentationImage(newLabelImage); // Update the undo wrapper to match LabelImageWrapper::Iterator itUndo = m_UndoWrapper.GetImageIterator(); LabelImageWrapper::ConstIterator itSeg = m_LabelWrapper.GetImageConstIterator(); while(!itUndo.IsAtEnd()) { itUndo.Set(itSeg.Get()); ++itUndo; ++itSeg; } } void IRISImageData ::SetGreyImage(GreyImageType *newGreyImage, const ImageCoordinateGeometry &newGeometry, const GreyTypeToNativeFunctor &native) { GenericImageData::SetGreyImage( newGreyImage, newGeometry, native); m_UndoWrapper.InitializeToWrapper(&m_LabelWrapper, (LabelType) 0); } void IRISImageData ::SetRGBImage(RGBImageType *newRGBImage, const ImageCoordinateGeometry &newGeometry) { GenericImageData::SetRGBImage(newRGBImage, newGeometry); m_UndoWrapper.InitializeToWrapper(&m_LabelWrapper, (LabelType) 0); } void IRISImageData ::UnloadMainImage() { m_UndoWrapper.Reset(); GenericImageData::UnloadMainImage(); } itksnap/Logic/Framework/IRISImageData.h0000644000076500000240000000615211246331624017220 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISImageData.h,v $ Language: C++ Date: $Date: 2009/08/29 23:02:44 $ 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 __IRISImageData_h_ #define __IRISImageData_h_ #include "GenericImageData.h" /** * \class IRISImageData * \brief This class encapsulates the image data used by * the IRIS component of SnAP. */ class IRISImageData : public GenericImageData { public: IRISImageData(IRISApplication *parent) : GenericImageData(parent) {} virtual ~IRISImageData() {}; /** * Access the segmentation image (read only access allowed * to preserve state) */ LabelImageWrapper* GetUndoImage() { assert(m_MainImageWrapper->IsInitialized() && m_UndoWrapper.IsInitialized()); return &m_UndoWrapper; } /** * We override the parent's SetSegmentationImage in order to initialize the * undo wrapper to match. */ void SetSegmentationImage(LabelImageType *newLabelImage); void SetGreyImage( GreyImageType *newGreyImage, const ImageCoordinateGeometry &newGeometry, const GreyTypeToNativeFunctor &native); void SetRGBImage(RGBImageType *newRGBImage, const ImageCoordinateGeometry &newGeometry); virtual void UnloadMainImage(); protected: // Starting with SNAP 1.6, the IRISImageData object will store a second // copy of the segmentation image for the purpose of implementing fast // undo functionality. This image is used to support situations where we // want to allow multiple updates to the segmentation image between saving // 'undo points'. This is necessary for paintbrush operation, since it would // be too expensive to store an undo point for every movement of the paintbrush // (and it would be difficult for the user too). So this UndoWrapper stores the // segmentation image _at the last undo point_. See IRISApplication::StoreUndoPoint // for details. LabelImageWrapper m_UndoWrapper; }; #endif itksnap/Logic/Framework/SNAPImageData.cxx0000644000076500000240000003457711553073142017601 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPImageData.cxx,v $ Language: C++ Date: $Date: 2011/04/18 17:35:30 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #include "SNAPLevelSetDriver.h" #include "itkGroupSpatialObject.h" #include "itkEllipseSpatialObject.h" #include "itkSpatialObjectToImageFilter.h" #include "itkMaximumImageFilter.h" #include "itkDanielssonDistanceMapImageFilter.h" #include "itkSubtractImageFilter.h" #include "itkUnaryFunctorImageFilter.h" #include "GlobalState.h" #include "EdgePreprocessingImageFilter.h" #include "IRISVectorTypesToITKConversion.h" #include "LabelImageWrapper.h" #include "LevelSetImageWrapper.h" #include "SignedDistanceFilter.h" #include "SmoothBinaryThresholdImageFilter.h" #include "SpeedImageWrapper.h" #include "SNAPImageData.h" SNAPImageData ::SNAPImageData(IRISApplication *parent) : GenericImageData(parent) { // Update the list of linked wrappers m_MainWrappers.push_back(&m_SpeedWrapper); m_MainWrappers.push_back(&m_SnakeInitializationWrapper); m_MainWrappers.push_back(&m_SnakeWrapper); // Initialize the level set driver to NULL m_LevelSetDriver = NULL; // Set the initial label color m_SnakeColorLabel = 0; } SNAPImageData ::~SNAPImageData() { if(m_LevelSetDriver) delete m_LevelSetDriver; } void SNAPImageData ::InitializeSpeed() { // The Grey image wrapper should be present assert(m_GreyWrapper.IsInitialized()); // Intialize the speed based on the current grey image m_SpeedWrapper.InitializeToWrapper(&m_GreyWrapper, 0.0f); } void SNAPImageData ::DoEdgePreprocessing(const EdgePreprocessingSettings &settings, itk::Command *progressCallback) { // Define an edge filter to use for preprocessing typedef EdgePreprocessingImageFilter< GreyImageWrapper::ImageType,SpeedImageWrapper::ImageType> FilterType; // Configure the edge filter FilterType::Pointer filter = FilterType::New(); // Pass the settings to the filter filter->SetEdgePreprocessingSettings(settings); // Set the filter's input filter->SetInput(m_GreyWrapper.GetImage()); // Provide a progress callback (if one is provided) if(progressCallback) filter->AddObserver(itk::ProgressEvent(),progressCallback); // Run the filter on the whole image filter->UpdateLargestPossibleRegion(); // Pass the output of the filter to the speed wrapper m_SpeedWrapper.SetImage(filter->GetOutput()); // Dismantle this pipeline m_SpeedWrapper.GetImage()->DisconnectPipeline(); } void SNAPImageData ::DoInOutPreprocessing(const ThresholdSettings &settings, itk::Command *progressCallback) { // Define an edge filter to use for preprocessing typedef SmoothBinaryThresholdImageFilter< GreyImageWrapper::ImageType,SpeedImageWrapper::ImageType> FilterType; // Create an edge filter for whole-image preprocessing FilterType::Pointer filter = FilterType::New(); // Pass the settings to the filter filter->SetThresholdSettings(settings); // Set the filter's input filter->SetInput(m_GreyWrapper.GetImage()); // Provide a progress callback (if one is provided) if(progressCallback) filter->AddObserver(itk::ProgressEvent(),progressCallback); // Run the filter filter->UpdateLargestPossibleRegion(); // Pass the output of the filter to the speed wrapper m_SpeedWrapper.SetImage(filter->GetOutput()); // Dismantle this pipeline m_SpeedWrapper.GetImage()->DisconnectPipeline(); } SpeedImageWrapper* SNAPImageData ::GetSpeed() { // Make sure it exists assert(m_SpeedWrapper.IsInitialized()); return &m_SpeedWrapper; } bool SNAPImageData ::IsSpeedLoaded() { return m_SpeedWrapper.IsInitialized(); } LevelSetImageWrapper* SNAPImageData ::GetSnakeInitialization() { assert(m_SnakeInitializationWrapper.IsInitialized()); return &m_SnakeInitializationWrapper; } bool SNAPImageData ::IsSnakeInitializationLoaded() { return (m_SnakeInitializationWrapper.IsInitialized()); } LevelSetImageWrapper* SNAPImageData ::GetSnake() { assert(m_SnakeWrapper.IsInitialized()); return &m_SnakeWrapper; } bool SNAPImageData ::IsSnakeLoaded() { return (m_SnakeWrapper.IsInitialized()); } bool SNAPImageData ::InitializeSegmentation( const SnakeParameters ¶meters, const std::vector &bubbles, unsigned int labelColor) { assert(m_SpeedWrapper.IsInitialized()); // Inside/outside values const float INSIDE_VALUE = -1.0, OUTSIDE_VALUE = 1.0; // Store the label color m_SnakeColorLabel = labelColor; // Types of images used here typedef itk::OrientedImage FloatImageType; // Initialize the level set initialization wrapper, set pixels to OUTSIDE_VALUE m_SnakeInitializationWrapper.InitializeToWrapper(&m_GreyWrapper, OUTSIDE_VALUE); // Create the initial level set image by merging the segmentation data from // IRIS region with the bubbles LabelImageType::Pointer imgInput = m_LabelWrapper.GetImage(); FloatImageType::Pointer imgLevelSet = m_SnakeInitializationWrapper.GetImage(); // Get the target region. This really should be a region relative to the IRIS image // data, not an image into a needless copy of an IRIS region. LabelImageType::RegionType region = imgInput->GetBufferedRegion(); // Create iterators to perform the copy typedef itk::ImageRegionConstIterator SourceIterator; typedef itk::ImageRegionIteratorWithIndex TargetIterator; SourceIterator itSource(imgInput,region); TargetIterator itTarget(imgLevelSet,region); // During the copy loop, compute the extents of the initialization Vector3l bbLower(reinterpret_cast(region.GetSize().GetSize())); Vector3l bbUpper(reinterpret_cast(region.GetIndex().GetIndex())); unsigned long nInitVoxels = 0; // Convert the input label image into a binary function whose 0 level set // is the boundary of the current label's region while(!itSource.IsAtEnd()) { if(itSource.Value() == m_SnakeColorLabel) { // Expand the bounding box accordingly Vector3l point(itTarget.GetIndex().GetIndex()); bbLower = vector_min(bbLower,point); bbUpper = vector_max(bbUpper,point); // Increase the number of initialization voxels nInitVoxels++; // Set the target value to inside itTarget.Value() = INSIDE_VALUE; } // Go to the next pixel ++itTarget; ++itSource; } // Fill in the bubbles by computing their for(unsigned int iBubble=0; iBubble < bubbles.size(); iBubble++) { // Compute the extents of the bubble typedef itk::Point PointType; PointType ptLower,ptUpper,ptCenter; // Compute the physical position of the bubble center imgLevelSet->TransformIndexToPhysicalPoint( to_itkIndex(bubbles[iBubble].center),ptCenter); // Extents of the bounding box FloatImageType::IndexType idxLower = to_itkIndex(bubbles[iBubble].center); FloatImageType::IndexType idxUpper = to_itkIndex(bubbles[iBubble].center); // Map all vertices in a cube of radius r around the physical center of // the bubble into index space, and compute a bounding box for(int jx=-1; jx<=1; jx+=2) for(int jy=-1; jy<=1; jy+=2) for(int jz=-1; jz<=1; jz+=2) { PointType ptTest; ptTest[0] = ptCenter[0] + jx * bubbles[iBubble].radius; ptTest[1] = ptCenter[1] + jy * bubbles[iBubble].radius; ptTest[2] = ptCenter[2] + jz * bubbles[iBubble].radius; FloatImageType::IndexType idxTest; imgLevelSet->TransformPhysicalPointToIndex(ptTest,idxTest); for(unsigned int k=0; k<3; k++) { if(idxLower[k] > idxTest[k]) idxLower[k] = idxTest[k]; if(idxUpper[k] < idxTest[k]) idxUpper[k] = idxTest[k]; } } // Create a region FloatImageType::SizeType szBubble; szBubble[0] = 1 + idxUpper[0] - idxLower[0]; szBubble[1] = 1 + idxUpper[1] - idxLower[1]; szBubble[2] = 1 + idxUpper[2] - idxLower[2]; FloatImageType::RegionType regBubble(idxLower,szBubble); regBubble.Crop(region); // Stretch the overall bounding box if necessary bbLower = vector_min(bbLower,Vector3l(idxLower.GetIndex())); bbUpper = vector_max(bbUpper,Vector3l(idxUpper.GetIndex())); // Create an iterator with an index to fill out the bubble TargetIterator itThisBubble(imgLevelSet, regBubble); // Need the squared radius for this float r2 = bubbles[iBubble].radius * bubbles[iBubble].radius; // Fill in the bubble while(!itThisBubble.IsAtEnd()) { PointType pt; imgLevelSet->TransformIndexToPhysicalPoint(itThisBubble.GetIndex(),pt); if(pt.SquaredEuclideanDistanceTo(ptCenter) <= r2) { itThisBubble.Value() = INSIDE_VALUE; nInitVoxels++; } ++itThisBubble; } } // At this point, we should have an initialization image and a bounding // box in bbLower and bbUpper. End the routine if there are no initialization // voxels if (nInitVoxels == 0) { m_SnakeInitializationWrapper.Reset(); return false; } // Make sure that the correct color label is being used m_SnakeInitializationWrapper.SetColorLabel(m_ColorLabel); // Initialize the snake driver InitalizeSnakeDriver(parameters); // Success return true; } void SNAPImageData ::SetExternalAdvectionField( FloatImageType *imgX, FloatImageType *imgY, FloatImageType *imgZ) { m_ExternalAdvectionField = VectorImageType::New(); m_ExternalAdvectionField->SetRegions( m_SpeedWrapper.GetImage()->GetBufferedRegion()); m_ExternalAdvectionField->Allocate(); m_ExternalAdvectionField->SetSpacing( m_GreyWrapper.GetImage()->GetSpacing()); m_ExternalAdvectionField->SetOrigin( m_GreyWrapper.GetImage()->GetOrigin()); typedef itk::ImageRegionConstIterator Iterator; Iterator itX(imgX,imgX->GetBufferedRegion()); Iterator itY(imgY,imgY->GetBufferedRegion()); Iterator itZ(imgZ,imgZ->GetBufferedRegion()); typedef itk::ImageRegionIterator Vectorator; Vectorator itTarget( m_ExternalAdvectionField, m_ExternalAdvectionField->GetBufferedRegion()); while(!itTarget.IsAtEnd()) { VectorType v; v[0] = itX.Get(); v[1] = itY.Get(); v[2] = itZ.Get(); itTarget.Set(v); ++itTarget; ++itX; ++itY; ++itZ; } } void SNAPImageData ::InitalizeSnakeDriver(const SnakeParameters &p) { // Create a new level set driver, deleting the current one if it's there if (m_LevelSetDriver) { delete m_LevelSetDriver; } // This is a good place to check that the parameters are valid if(p.GetSnakeType() == SnakeParameters::REGION_SNAKE) { // There is no advection assert(p.GetAdvectionWeight() == 0); // There is no curvature speed assert(p.GetCurvatureSpeedExponent() == -1); // Propagation is modulated by probability assert(p.GetPropagationSpeedExponent() == 1); // There is no smoothing speed assert(p.GetLaplacianSpeedExponent() == 0); } // Copy the configuration parameters m_CurrentSnakeParameters = p; // Initialize the snake driver and pass the parameters m_LevelSetDriver = new SNAPLevelSetDriver3d( m_SnakeInitializationWrapper.GetImage(), m_SpeedWrapper.GetImage(), m_CurrentSnakeParameters, m_ExternalAdvectionField); // Initialize the level set wrapper with the image from the level set // driver and other settings from the other wrappers m_SnakeWrapper.InitializeToWrapper( &m_GreyWrapper,m_LevelSetDriver->GetCurrentState()); m_SnakeWrapper.GetImage()->SetOrigin( m_GreyWrapper.GetImage()->GetOrigin() ); m_SnakeWrapper.GetImage()->SetSpacing( m_GreyWrapper.GetImage()->GetSpacing() ); // Make sure that the correct color label is being used m_SnakeWrapper.SetColorLabel(m_ColorLabel); } void SNAPImageData ::RunSegmentation(unsigned int nIterations) { // Should be in level set mode assert(m_LevelSetDriver); // Pass through to the level set driver m_LevelSetDriver->Run(nIterations); } void SNAPImageData ::RestartSegmentation() { // Should be in level set mode assert(m_LevelSetDriver); // Pass through to the level set driver m_LevelSetDriver->Restart(); // Update the image pointed to by the snake wrapper m_SnakeWrapper.SetImage(m_LevelSetDriver->GetCurrentState()); } void SNAPImageData ::TerminateSegmentation() { // Should be in level set mode assert(m_LevelSetDriver); // Delete the level set driver and all the problems that go along with it delete m_LevelSetDriver; m_LevelSetDriver = NULL; } void SNAPImageData ::SetSegmentationParameters(const SnakeParameters ¶meters) { // Should be in level set mode assert(m_LevelSetDriver); // Pass through to the level set driver m_LevelSetDriver->SetSnakeParameters(parameters); } unsigned int SNAPImageData:: GetElapsedSegmentationIterations() const { return m_LevelSetDriver->GetElapsedIterations(); } SNAPImageData::LevelSetImageType * SNAPImageData ::GetLevelSetImage() { assert(m_LevelSetDriver); return m_LevelSetDriver->GetCurrentState(); } SNAPLevelSetFunction * SNAPImageData ::GetLevelSetFunction() { return m_LevelSetDriver->GetLevelSetFunction(); } itksnap/Logic/Framework/SNAPImageData.h0000644000076500000240000002154511136422002017203 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPImageData.h,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ 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 __SNAPImageData_h_ #define __SNAPImageData_h_ #include "SNAPCommon.h" #include "IRISException.h" #include "GreyImageWrapper.h" #include "GlobalState.h" #include "ColorLabel.h" #include "ImageCoordinateGeometry.h" #include "IRISImageData.h" #include "SnakeParameters.h" #include "SpeedImageWrapper.h" #include "LevelSetImageWrapper.h" #include "EdgePreprocessingSettings.h" #include "ThresholdSettings.h" #include #include "SNAPLevelSetFunction.h" template class SNAPLevelSetDriver; /** * \class SNAPImageData * \brief Wrapper around the SNAP automatic segmentation pipelines. * * This class encapsulates several images used in the SNAP application, * including the speed image, the bubble-initialization image and the * segmentation result image. */ class SNAPImageData : public GenericImageData { public: // The type of the internal level set image typedef itk::OrientedImage FloatImageType; typedef FloatImageType LevelSetImageType; SNAPImageData(IRISApplication *m_Parent); ~SNAPImageData(); /** * Get the preprocessed (speed) image wrapper */ SpeedImageWrapper* GetSpeed(); /** A high level method to perform edge preprocessing on the grey image and * store the result in the speed image wrapper */ void DoEdgePreprocessing( const EdgePreprocessingSettings &settings,itk::Command *progressCallback = 0); /** A high level method to perform in-out preprocessing on the grey image and * store the result in the speed image wrapper */ void DoInOutPreprocessing( const ThresholdSettings &settings,itk::Command *progressCallback = 0); /** * Initialize the Speed image wrapper to blank data */ void InitializeSpeed(); /** * Clear the preprocessed (speed) image (discard data, etc) */ void ClearSpeed() { m_SpeedWrapper.Reset(); } /** * Check the preprocessed image for validity */ bool IsSpeedLoaded(); /** Get the current snake image wrapper */ LevelSetImageWrapper* GetSnake(); /** * Clear the current snake (discard data, etc) */ void ClearSnake() { m_SnakeWrapper.Reset(); } /** * Check the current snake for validity */ bool IsSnakeLoaded(); /** * This optional method allows us to load an external advection * field. This field can be used when image data includes some * directional components, i.e., DTI */ void SetExternalAdvectionField( FloatImageType *imgX, FloatImageType *imgY, FloatImageType *imgZ); /** This method reverts back to using gradient based advection fields */ void RemoveExternalAdvectionField() { m_ExternalAdvectionField = NULL; } /** Set the color label used for the segmentation */ irisSetMacro(ColorLabel, ColorLabel); irisGetMacro(ColorLabel, ColorLabel); /** =========== Methods dealing with the segmentation pipeline ============ */ /** * This method computes the two-sided distance transform from the array of * bubbles passed on and, if the segmentation image is not blank, the pixels * in that image. This method also initializes the level set driver, ie, the * pipeline driving the segmentation process. This method may take a while * to run because of the distance transforms. * * @return If there were no voxels of labelColor in the union of the * segmentation image with the bubbles, false will be returned and * initialization will not be completed. Otherwise, true will be returned. */ bool InitializeSegmentation(const SnakeParameters ¶meters, const std::vector &bubbles, unsigned int labelColor); /** Run the segmentation for a fixed number of iterations */ void RunSegmentation(unsigned int nIterations); /** Revert the segmentation to the beginning */ void RestartSegmentation(); /** Update the segmentation parameters, can be done either from the * segmentation pipeline callback or on the fly. This method is smart enough * to reinitialize the level set driver if the Solver parameter changes */ void SetSegmentationParameters(const SnakeParameters ¶meters); /** Check if the segmentation is active */ bool IsSegmentationActive() const { return m_LevelSetDriver != NULL; } /** Get the number of elapsed iterations */ unsigned int GetElapsedSegmentationIterations() const; /** Release the resources associated with the level set segmentation. This * method must be called once the segmentation pipeline has terminated, or * else it would create a nasty crash */ void TerminateSegmentation(); /** ====================================================================== */ /** * Merge the segmentation result with the segmentation contained in a * IRIS image data object. */ void MergeSnakeWithIRIS(IRISImageData *target) const; /** * Get the level set image currently being evolved */ LevelSetImageType *GetLevelSetImage(); /** This method is public for testing purposes. It will give a pointer to * the level set function used internally for segmentation */ SNAPLevelSetFunction *GetLevelSetFunction(); private: /** A functor for inverting an image */ class InvertFunctor { public: unsigned char operator()(unsigned char input) { return input == 0 ? 1 : 0; } }; /** Initialize the driver used to control the snake. This driver is used to * run the snake several iterations at a time, without resetting the filter * between iteration blocks. After executing each block of iterations, the * filter will call a callback routine, which is provided as a parameter to * this method. In a UI environment, that callback routine should check for * user input. */ void InitalizeSnakeDriver(const SnakeParameters ¶m); /** A callback used internally to communicate with the LevelSetDriver */ void IntermediatePauseCallback( itk::Object *object,const itk::EventObject &event); /** Another callback, used in non-interactive mode */ void TerminatingPauseCallback(); /** Type of fommands used for callbacks to the user of this class */ typedef itk::SmartPointer CommandPointer; /** A callback for idle cycle of the segmentation pipeline */ CommandPointer m_SegmentationIdleCallback; /** A callback made after an update in the segmentation pipeline */ CommandPointer m_SegmentationUpdateCallback; /** Get the snake initialization image */ LevelSetImageWrapper* GetSnakeInitialization(); /** Clear the snake initialization image (discard data, etc) */ void ClearSnakeInitialization() { m_SnakeInitializationWrapper.Reset(); } /** Check the snake initialization image for validity */ bool IsSnakeInitializationLoaded(); // Speed image adata SpeedImageWrapper m_SpeedWrapper; // Wrapper around the level set image LevelSetImageWrapper m_SnakeWrapper; // Snake initialization data (initial distance transform LevelSetImageWrapper m_SnakeInitializationWrapper; // Snake driver SNAPLevelSetDriver<3> *m_LevelSetDriver; // Label color used for the snake images LabelType m_SnakeColorLabel; // Current value of snake parameters SnakeParameters m_CurrentSnakeParameters; // Typedefs for defining the advection image that can be loaded externally typedef itk::FixedArray VectorType; typedef itk::OrientedImage< VectorType, 3> VectorImageType; typedef itk::SmartPointer VectorImagePointer; // The advection image VectorImagePointer m_ExternalAdvectionField; // The color label that is used for this segmentation ColorLabel m_ColorLabel; }; #endif itksnap/Logic/Framework/UndoDataManager.h0000644000076500000240000001001211107537226017677 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: UndoDataManager.h,v $ Language: C++ Date: $Date: 2008/11/15 12:20:38 $ 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 #include /** * \class UndoDataManager * \brief Manages data (delta updates) for undo/redo in itk-snap */ template class UndoDataManager { public: /** * The Delta class represents a difference between two images used in * the Undo system. It only supports linear traversal of images and * stores differences in an RLE (run length encoding) format. */ class Delta { public: Delta() { m_CurrentLength = 0; m_UniqueID = m_UniqueIDCounter++; } void Encode(const TPixel &value) { if(m_CurrentLength == 0) { m_LastValue = value; m_CurrentLength = 1; } else if(value == m_LastValue) { m_CurrentLength++; } else { m_Array.push_back(std::make_pair(m_CurrentLength, m_LastValue)); m_CurrentLength = 1; m_LastValue = value; } } void FinishEncoding() { if(m_CurrentLength > 0) m_Array.push_back(std::make_pair(m_CurrentLength, m_LastValue)); } size_t GetNumberOfRLEs() { return m_Array.size(); } TPixel GetRLEValue(size_t i) { return m_Array[i].second; } size_t GetRLELength(size_t i) { return m_Array[i].first; } unsigned long GetUniqueID() const { return m_UniqueID; } protected: typedef std::pair RLEPair; typedef std::vector RLEArray; RLEArray m_Array; size_t m_CurrentLength; TPixel m_LastValue; // Each delta is assigned a unique ID at creation unsigned long m_UniqueID; static unsigned long m_UniqueIDCounter; }; UndoDataManager(size_t nMinDeltas, size_t nMaxTotalSize); void AppendDelta(Delta *delta); void Clear(); bool IsUndoPossible(); Delta *GetDeltaForUndo(); bool IsRedoPossible(); Delta *GetDeltaForRedo(); size_t GetNumberOfDeltas() { return m_DeltaList.size(); } /** * A state descriptor. This descriptor is used to compare the * state of the undo queue between two time points. The idea is * to know whether an image has changed from the time it was * saved or not. The state is basically the recording of all * deltas from the starting point to the current position */ typedef std::list StateDescriptor; StateDescriptor GetState() const; private: typedef std::list DList; typedef typename DList::iterator DIterator; typedef typename DList::const_iterator DConstIterator; DList m_DeltaList; DIterator m_Position; size_t m_TotalSize, m_MinDeltas, m_MaxTotalSize; }; itksnap/Logic/Framework/UndoDataManager.txx0000644000076500000240000001021311107537226020276 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: UndoDataManager.txx,v $ Language: C++ Date: $Date: 2008/11/15 12:20:38 $ 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. =========================================================================*/ template unsigned long UndoDataManager::Delta::m_UniqueIDCounter = 0; template UndoDataManager ::UndoDataManager(size_t nMinDeltas, size_t nMaxTotalSize) { this->m_MinDeltas = nMinDeltas; this->m_MaxTotalSize = nMaxTotalSize; this->m_TotalSize = 0; m_Position = m_DeltaList.begin(); } template void UndoDataManager ::Clear() { // Delete all the deltas m_Position = m_DeltaList.begin(); while(m_Position != m_DeltaList.end()) { delete *m_Position; m_Position = m_DeltaList.erase(m_Position); } m_TotalSize = 0; } template void UndoDataManager ::AppendDelta(Delta *delta) { // If we are not currently pointing past the end of the delta // list, we should prune all the deltas from the current point // to the end. So that's the loop that we do while(m_Position != m_DeltaList.end()) { m_TotalSize -= (*m_Position)->GetNumberOfRLEs(); delete *m_Position; m_Position = m_DeltaList.erase(m_Position); } // Check whether we need to prune from the back DIterator itHead = m_DeltaList.begin(); while(m_DeltaList.size() > m_MinDeltas && m_TotalSize + delta->GetNumberOfRLEs() > m_MaxTotalSize) { m_TotalSize -= (*itHead)->GetNumberOfRLEs(); delete *itHead; itHead = m_DeltaList.erase(itHead); } // Now we have a well pruned list of deltas, and we can append // the current delta to it; m_DeltaList.push_back(delta); m_Position = m_DeltaList.end(); m_TotalSize += delta->GetNumberOfRLEs(); } template bool UndoDataManager ::IsUndoPossible() { return (m_DeltaList.size() > 0 && m_Position != m_DeltaList.begin()); } template typename UndoDataManager::Delta * UndoDataManager ::GetDeltaForUndo() { // Can't be at the beginning assert(IsUndoPossible()); // Move the position one delta to the beginning m_Position--; // Return the current delta return *m_Position; } template bool UndoDataManager ::IsRedoPossible() { return (m_DeltaList.size() > 0 && m_Position != m_DeltaList.end()); } template typename UndoDataManager::Delta * UndoDataManager ::GetDeltaForRedo() { // Can't be at the beginning assert(IsRedoPossible()); // Return the delta at the current position Delta *del = *m_Position; // Move the position one delta to the end m_Position++; // Return the current delta return del; } template typename UndoDataManager::StateDescriptor UndoDataManager ::GetState() const { StateDescriptor sd; for(DConstIterator it = m_DeltaList.begin(); it != m_Position; ++it) { const Delta *delta = *it; sd.push_back(delta->GetUniqueID()); } return sd; } itksnap/Logic/Framework/UndoDataManager_LabelType.cxx0000644000076500000240000000275610735614372022237 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: UndoDataManager_LabelType.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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. =========================================================================*/ #include "SNAPCommon.h" #include "UndoDataManager.h" #include "UndoDataManager.txx" template class UndoDataManager; itksnap/Logic/ImageWrapper/0000755000076500000240000000000011560342170015163 5ustar paulystaffitksnap/Logic/ImageWrapper/CVS/0000755000076500000240000000000011560342170015616 5ustar paulystaffitksnap/Logic/ImageWrapper/CVS/Entries0000644000076500000240000000220711560342170017153 0ustar paulystaff/GreyImageWrapper.cxx/1.19/Mon Jun 28 18:45:08 2010// /GreyImageWrapper.h/1.17/Sat Sep 19 08:15:09 2009// /GuidedNativeImageIO.cxx/1.11/Thu Oct 14 16:21:04 2010// /GuidedNativeImageIO.h/1.6/Thu Oct 14 16:21:04 2010// /ImageIORoutines.h/1.2/Sun Dec 30 04:05:14 2007// /ImageWrapper.h/1.14/Fri Nov 13 00:59:47 2009// /ImageWrapper.txx/1.11/Thu Oct 14 16:21:04 2010// /LabelImageWrapper.cxx/1.7/Sat Aug 29 23:18:42 2009// /LabelImageWrapper.h/1.5/Tue Aug 25 21:38:16 2009// /LabelToRGBAFilter.h/1.2/Mon Oct 26 16:22:52 2009// /LevelSetImageWrapper.cxx/1.6/Mon Jun 28 18:45:08 2010// /LevelSetImageWrapper.h/1.4/Tue Aug 25 21:38:16 2009// /RGBImageWrapper.cxx/1.7/Mon Jun 28 18:45:08 2010// /RGBImageWrapper.h/1.6/Tue Aug 25 21:38:16 2009// /ScalarImageWrapper.h/1.3/Fri Jan 23 20:09:38 2009// /ScalarImageWrapper.txx/1.4/Sat Jan 24 01:50:21 2009// /SpeedColorMap.cxx/1.2/Sun Dec 30 04:05:14 2007// /SpeedColorMap.h/1.3/Sun Dec 30 04:05:14 2007// /SpeedImageWrapper.cxx/1.5/Mon Jun 28 18:45:08 2010// /SpeedImageWrapper.h/1.5/Tue Aug 25 21:38:16 2009// /VectorImageWrapper.h/1.2/Fri Jan 23 20:09:38 2009// /VectorImageWrapper.txx/1.1/Wed Jun 6 22:27:21 2007// D itksnap/Logic/ImageWrapper/CVS/Repository0000644000076500000240000000003311560342170017714 0ustar paulystaffitksnap/Logic/ImageWrapper itksnap/Logic/ImageWrapper/CVS/Root0000644000076500000240000000010011560342170016453 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/ImageWrapper/GreyImageWrapper.cxx0000644000076500000240000001215411412166664021134 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GreyImageWrapper.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ Version: $Revision: 1.19 $ 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 "GreyImageWrapper.h" #include "UnaryFunctorCache.h" #include "ImageWrapper.txx" #include "ScalarImageWrapper.txx" #include "itkFunctionBase.h" #include "itkUnaryFunctorImageFilter.h" // Create an instance of ImageWrapper of appropriate type template class ImageWrapper; template class ScalarImageWrapper; GreyImageWrapper ::GreyImageWrapper() : ScalarImageWrapper () { // Initialize the intensity curve m_IntensityCurveVTK = IntensityCurveVTK::New(); m_IntensityCurveVTK->Initialize(); // Initialize the intensity functor m_IntensityFunctor.m_IntensityMap = m_IntensityCurveVTK; // Instantiate the cache m_IntensityMapCache = CacheType::New(); // Set the target of the cache m_IntensityMapCache->SetInputFunctor(&m_IntensityFunctor); // Instantiate the filters for(unsigned int i=0;i<3;i++) { m_IntensityFilter[i] = IntensityFilterType::New(); m_IntensityFilter[i]->SetFunctor(m_IntensityMapCache->GetCachingFunctor()); m_IntensityFilter[i]->SetInput(m_Slicer[i]->GetOutput()); } // By default, reference range is not used m_FlagUseReferenceIntensityRange = false; } GreyImageWrapper ::~GreyImageWrapper() { for (size_t i = 0; i < 3; ++i) { m_IntensityFilter[i] = NULL; } m_IntensityMapCache = NULL; m_IntensityCurveVTK = NULL; } void GreyImageWrapper ::SetReferenceIntensityRange(GreyType refMin, GreyType refMax) { m_FlagUseReferenceIntensityRange = true; m_ReferenceIntensityMin = refMin; m_ReferenceIntensityMax = refMax; } void GreyImageWrapper ::ClearReferenceIntensityRange() { m_FlagUseReferenceIntensityRange = false; } IntensityCurveInterface* GreyImageWrapper ::GetIntensityMapFunction() { return m_IntensityCurveVTK; } void GreyImageWrapper ::CopyIntensityMap(const GreyImageWrapper &s) { m_IntensityCurveVTK->Initialize( s.m_IntensityCurveVTK->GetControlPointCount()); for(size_t i = 0; i < m_IntensityCurveVTK->GetControlPointCount(); i++) { float t, x; s.m_IntensityCurveVTK->GetControlPoint(i, t, x); m_IntensityCurveVTK->UpdateControlPoint(i, t, x); } } void GreyImageWrapper ::UpdateIntensityMapFunction() { // Get the range of the image GreyType iMin = GetImageMin(); GreyType iMax = GetImageMax(); // Set the input range of the functor if(m_FlagUseReferenceIntensityRange) { m_IntensityFunctor.SetInputRange( m_ReferenceIntensityMin, m_ReferenceIntensityMax); } else { m_IntensityFunctor.SetInputRange(iMin, iMax); } // Set the active range of the cache m_IntensityMapCache->SetEvaluationRange(iMin,iMax); // Dirty the intensity filters for(unsigned int i=0;i<3;i++) m_IntensityFilter[i]->Modified(); } GreyImageWrapper::DisplaySlicePointer GreyImageWrapper ::GetDisplaySlice(unsigned int dim) const { return m_IntensityFilter[dim]->GetOutput(); } ColorMap GreyImageWrapper ::GetColorMap() const { return m_IntensityFunctor.m_Colormap; } void GreyImageWrapper ::SetColorMap(const ColorMap& colormap) { m_IntensityFunctor.m_Colormap = colormap; } void GreyImageWrapper ::Update() { // Dirty the intensity filters for(unsigned int i=0;i<3;i++) m_IntensityFilter[i]->Modified(); } void GreyImageWrapper::IntensityFunctor ::SetInputRange(GreyType intensityMin, GreyType intensityMax) { m_IntensityMin = intensityMin; m_IntensityFactor = 1.0f / (intensityMax-intensityMin); } GreyImageWrapper::DisplayPixelType GreyImageWrapper::IntensityFunctor ::operator()(const GreyType &in) const { // Map the input value to range of 0 to 1 double inZeroOne = (in - m_IntensityMin) * m_IntensityFactor; // Compute the intensity mapping double outZeroOne = m_IntensityMap->Evaluate(inZeroOne); // Map the output to a RGBA pixel return m_Colormap.MapIndexToRGBA(outZeroOne); } itksnap/Logic/ImageWrapper/GreyImageWrapper.h0000644000076500000240000001572111255111415020551 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GreyImageWrapper.h,v $ Language: C++ Date: $Date: 2009/09/19 08:15:09 $ Version: $Revision: 1.17 $ 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 __GreyImageWrapper_h_ #define __GreyImageWrapper_h_ #include "ScalarImageWrapper.h" #include "ColorMap.h" #include "IntensityCurveVTK.h" // #include "UnaryFunctorCache.h" // Forward references namespace itk { template class FunctionBase; template class UnaryFunctorImageFilter; }; template class UnaryFunctorCache; template class CachingUnaryFunctor; /** * \class GreyImageWrapper * \brief Image wrapper used for greyscale images in IRIS/SNAP. * * Adds ability to remap intensity from short to byte using an * arbitrary function when outputing slices. */ class GreyImageWrapper : public ScalarImageWrapper { public: /** * Get the intensity curve to be used for mapping image intensities * from GreyType to DisplayType. The curve is defined on the domain * [0, 1]. By default, the entire intensity range of the image is * mapped to the domain of the curve. However, in some situations * (e.g., when the image is a subregion of another image with respect * to which the curve was created), the domain of the curve should * correspond to a different intensity range. That can be specified * using the SetReferenceIntensityRange() function */ IntensityCurveInterface* GetIntensityMapFunction(); /** * Copy the intensity curve information from another grey image wrapper */ void CopyIntensityMap(const GreyImageWrapper &source); void UpdateIntensityMapFunction(); /** * Set the reference intensity range - a range of intensity that * is mapped to the domain of the intensity curve * @see GetIntensityMapFunction */ void SetReferenceIntensityRange(GreyType refMin, GreyType refMax); void ClearReferenceIntensityRange(); /** * Set the transformation to native intensity space */ irisSetMacro(NativeMapping, GreyTypeToNativeFunctor); irisGetMacro(NativeMapping, GreyTypeToNativeFunctor); /** * Get voxel intensity in native space */ double GetVoxelMappedToNative(const Vector3ui &vec) { return m_NativeMapping(this->GetVoxel(vec)); } /** * Get min/max voxel intensity in native space */ double GetImageMinNative() { return m_NativeMapping(this->GetImageMin()); } double GetImageMaxNative() { return m_NativeMapping(this->GetImageMax()); } /** * Get the display slice in a given direction. To change the * display slice, call parent's MoveToSlice() method */ DisplaySlicePointer GetDisplaySlice(unsigned int dim) const; /** * Get/Set the colormap */ ColorMap GetColorMap () const; void SetColorMap (const ColorMap& colormap); void Update(); /** Constructor initializes mappers */ GreyImageWrapper(); /** Destructor */ ~GreyImageWrapper(); private: /** * This object is passed on to the cache for intensity mapping */ class IntensityFunctor { public: /** Map a grey value */ DisplayPixelType operator()(const GreyType &value) const; // The storage for the float->float intensity map IntensityCurveInterface *m_IntensityMap; // Intensity mapping factors GreyType m_IntensityMin; float m_IntensityFactor; // Color map ColorMap m_Colormap; // Equality operators required, if variables defined!!! bool operator == (const IntensityFunctor &z) const { return m_IntensityMap == z.m_IntensityMap && m_IntensityFactor == z.m_IntensityFactor && m_IntensityMin == z.m_IntensityMin && m_Colormap == z.m_Colormap; } bool operator != (const IntensityFunctor &z) const { return !(*this == z); } /** * Set the range over which the input data is mapped to output data */ void SetInputRange(GreyType intensityMin,GreyType intensityMax); }; // Type of intensity function used to map 3D volume intensity into // 2D slice intensities typedef UnaryFunctorCache CacheType; typedef itk::SmartPointer CachePointer; typedef CachingUnaryFunctor CacheFunctor; // Filters applied to slices typedef itk::Image GreySliceType; typedef itk::UnaryFunctorImageFilter< GreySliceType,DisplaySliceType,CacheFunctor> IntensityFilterType; typedef itk::SmartPointer IntensityFilterPointer; /** * Reference intensity range. This is used for images that are subregions * of larger images. When evaluating the intensity of these images, the * intensity curve needs to be applied to the intensity range of the larger * image, not that of the region. */ GreyType m_ReferenceIntensityMin, m_ReferenceIntensityMax; bool m_FlagUseReferenceIntensityRange; /** * An instance of the private intensity mapper (this mapper wraps the * passed in float->float function to a new function that is * [min..max]->uchar) */ IntensityFunctor m_IntensityFunctor; /** * A cache used for the intensity mapping function */ CachePointer m_IntensityMapCache; /** * */ IntensityCurveVTK::Pointer m_IntensityCurveVTK; /** * Filters used to remap the intensity of the slices in this image * into unsigned char images */ IntensityFilterPointer m_IntensityFilter[3]; /** * The grey image is an image of shorts. But the real image on disk may * be an image of floats or doubles. So we store a transformation from * internal intensity values to 'native' intensity values */ GreyTypeToNativeFunctor m_NativeMapping; }; #endif // __GreyImageWrapper_h_ itksnap/Logic/ImageWrapper/GuidedNativeImageIO.cxx0000755000076500000240000007273411455626760021507 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GuidedNativeImageIO.cxx,v $ Language: C++ Date: $Date: 2010/10/14 16:21:04 $ 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. =========================================================================*/ #include "GuidedNativeImageIO.h" #include "IRISException.h" #include "SNAPCommon.h" #include "SNAPRegistryIO.h" #include "ImageCoordinateGeometry.h" #include "itkOrientedImage.h" #include "itkImageIOBase.h" #include "itkAnalyzeImageIO.h" #include "itkGiplImageIO.h" #include "itkMetaImageIO.h" #include "itkNrrdImageIO.h" #include "itkRawImageIO.h" #include "itkGDCMImageIO.h" #include "itkGE4ImageIO.h" #include "itkGE5ImageIO.h" #include "itkNiftiImageIO.h" #include "itkSiemensVisionImageIO.h" #include "itkVTKImageIO.h" #include "itkVoxBoCUBImageIO.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkImageSeriesReader.h" #include "itkImageIOFactory.h" #include "itkGDCMSeriesFileNames.h" #include "itkImageToVectorImageFilter.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkShiftScaleImageFilter.h" #include "itkNumericTraits.h" using namespace std; bool GuidedNativeImageIO::m_StaticDataInitialized = false; RegistryEnumMap GuidedNativeImageIO::m_EnumFileFormat; RegistryEnumMap GuidedNativeImageIO::m_EnumRawPixelType; const GuidedNativeImageIO::FileFormatDescriptor GuidedNativeImageIO ::m_FileFormatDescrictorArray[] = { {"MetaImage", "mha,mhd", true, true, true, true}, {"GIPL", "gipl,gipl.gz", true, false, true, true}, {"Raw Binary", "raw", false, false, true, true}, {"Analyze", "hdr,img,img.gz", true, false, true, true}, {"DICOM", "dcm", false, true, true, true}, {"GE Version 4", "ge4", false, false, true, true}, {"GE Version 5", "ge5", false, false, true, true}, {"NIFTI", "nii,nia,nii.gz,nia.gz", true, true, true, true}, {"Siemens Vision", "ima", false, false, true, true}, {"VTK", "vtk", true, false, true, true}, {"VoxBo CUB", "cub,cub.gz", true, false, true, true}, {"NRRD", "nrrd,nhdr", true, true, true, true}, {"INVALID FORMAT", "", false, false, false, false}}; GuidedNativeImageIO ::GuidedNativeImageIO() { if(!m_StaticDataInitialized) { for(int i = 0; i < FORMAT_COUNT; i++) m_EnumFileFormat.AddPair( (FileFormat)(FORMAT_MHA + i), m_FileFormatDescrictorArray[i].name.c_str()); m_EnumRawPixelType.AddPair(PIXELTYPE_CHAR, "CHAR"); m_EnumRawPixelType.AddPair(PIXELTYPE_UCHAR, "UCHAR"); m_EnumRawPixelType.AddPair(PIXELTYPE_SHORT, "SHORT"); m_EnumRawPixelType.AddPair(PIXELTYPE_USHORT, "USHORT"); m_EnumRawPixelType.AddPair(PIXELTYPE_INT, "INT"); m_EnumRawPixelType.AddPair(PIXELTYPE_UINT, "UINT"); m_EnumRawPixelType.AddPair(PIXELTYPE_FLOAT, "FLOAT"); m_EnumRawPixelType.AddPair(PIXELTYPE_DOUBLE, "DOUBLE"); m_EnumRawPixelType.AddPair(PIXELTYPE_COUNT, "INVALID PIXEL TYPE"); m_StaticDataInitialized = true; } m_NativeType = itk::ImageIOBase::UNKNOWNCOMPONENTTYPE; m_NativeComponents = 0; m_NativeTypeString = m_IOBase->GetComponentTypeAsString(m_NativeType); m_NativeFileName = ""; m_NativeByteOrder = itk::ImageIOBase::OrderNotApplicable; m_NativeSizeInBytes = 0; } GuidedNativeImageIO::FileFormat GuidedNativeImageIO ::GetFileFormat(Registry &folder, FileFormat dflt) { return folder.Entry("Format").GetEnum(m_EnumFileFormat, dflt); } void GuidedNativeImageIO ::SetFileFormat(Registry &folder, FileFormat format) { folder.Entry("Format").PutEnum(m_EnumFileFormat, format); } GuidedNativeImageIO::RawPixelType GuidedNativeImageIO ::GetPixelType(Registry &folder, RawPixelType dflt) { return folder.Entry("Raw.PixelType").GetEnum(m_EnumRawPixelType, dflt); } void GuidedNativeImageIO ::SetPixelType(Registry &folder, RawPixelType type) { folder.Entry("Raw.PixelType").PutEnum(m_EnumRawPixelType, type); } size_t GuidedNativeImageIO ::GetNumberOfComponentsInNativeImage() const { return m_NativeComponents; } template void GuidedNativeImageIO ::CreateRawImageIO(Registry &folder) { // Create the Raw IO typedef itk::RawImageIO IOType; typename IOType::Pointer rawIO = IOType::New(); // Set the header size rawIO->SetHeaderSize(folder["HeaderSize"][0]); // Read the dimensions and other stuff from the registry Vector3i dims = folder["Dimensions"][Vector3i(0)]; Vector3d spacing = folder["Spacing"][Vector3d(1.0)]; Vector3d origin = folder["Origin"][Vector3d(0.0)]; // Supply to the IO object for(unsigned int i = 0; i < 3; i++) { rawIO->SetDimensions(i, dims[i]); rawIO->SetSpacing(i, spacing[i]); rawIO->SetOrigin(i, origin[i]); } // Set the endianness if(folder["BigEndian"][true]) rawIO->SetByteOrderToBigEndian(); else rawIO->SetByteOrderToLittleEndian(); // Set the number of components rawIO->SetNumberOfComponents(folder["Components"][1]); // Set the other parameters rawIO->SetFileTypeToBinary(); // Return the pointer m_IOBase = rawIO; } void GuidedNativeImageIO ::CreateImageIO(const char *fname, Registry &folder, bool flag_read) { // Get the format specified in the folder m_FileFormat = GetFileFormat(folder); // Choose the approach based on the file format switch(m_FileFormat) { case FORMAT_MHA: m_IOBase = itk::MetaImageIO::New(); break; case FORMAT_NRRD: m_IOBase = itk::NrrdImageIO::New(); break; case FORMAT_ANALYZE: m_IOBase = itk::AnalyzeImageIO::New(); break; case FORMAT_GIPL: m_IOBase = itk::GiplImageIO::New(); break; case FORMAT_GE4: m_IOBase = itk::GE4ImageIO::New(); break; case FORMAT_GE5: m_IOBase = itk::GE5ImageIO::New(); break; case FORMAT_NIFTI: m_IOBase = itk::NiftiImageIO::New(); break; case FORMAT_SIEMENS: m_IOBase = itk::SiemensVisionImageIO::New(); break; case FORMAT_VTK: m_IOBase = itk::VTKImageIO::New(); break; case FORMAT_VOXBO_CUB: m_IOBase = itk::VoxBoCUBImageIO::New(); break; case FORMAT_DICOM: m_IOBase = itk::GDCMImageIO::New(); break; case FORMAT_RAW: { // Get the Raw header sub-folder Registry fldRaw = folder.Folder("Raw"); // Get the type of pixel from the registry RawPixelType type = folder.Entry("Raw.PixelType").GetEnum(m_EnumRawPixelType, PIXELTYPE_COUNT); // Use header page values to initialize the RAW io switch(type) { case PIXELTYPE_UCHAR: CreateRawImageIO(fldRaw); break; case PIXELTYPE_CHAR: CreateRawImageIO(fldRaw); break; case PIXELTYPE_USHORT: CreateRawImageIO(fldRaw); break; case PIXELTYPE_SHORT: CreateRawImageIO(fldRaw); break; case PIXELTYPE_UINT: CreateRawImageIO(fldRaw); break; case PIXELTYPE_INT: CreateRawImageIO(fldRaw); break; case PIXELTYPE_FLOAT: CreateRawImageIO(fldRaw); break; case PIXELTYPE_DOUBLE: CreateRawImageIO(fldRaw); break; default: throw itk::ExceptionObject("Unsupported Pixel Type when reading Raw File"); } } break; default: { // No IO base was specified in the registry folder. We will use ITK's factory // system to find an IO object that can open the file m_IOBase = itk::ImageIOFactory::CreateImageIO(fname, flag_read ? itk::ImageIOFactory::ReadMode : itk::ImageIOFactory::WriteMode); } } } void GuidedNativeImageIO ::ReadNativeImage(const char *FileName, Registry &folder) { // Create the header corresponding to the current image type CreateImageIO(FileName, folder, true); if(!m_IOBase) throw itk::ExceptionObject("Unsupported image file type"); // Read the information about the image if(m_FileFormat == FORMAT_DICOM) { // Check if the array of filenames has been provided for us m_DICOMFiles = folder.Folder("DICOM.SliceFiles").GetArray(std::string("NULL")); // If no filenames were specified, read the first series in the directory if(m_DICOMFiles.size() == 0) { // Create a names generator. The input must be a directory typedef itk::GDCMSeriesFileNames NamesGeneratorType; NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New(); nameGenerator->SetDirectory(FileName); // Get the list of series in the directory const itk::SerieUIDContainer &sids = nameGenerator->GetSeriesUIDs(); // There must be at least of series if(sids.size() == 0) throw itk::ExceptionObject("No DICOM series found in the DICOM directory"); // Read the first DICOM series in the directory m_DICOMFiles = nameGenerator->GetFileNames(sids.front().c_str()); } // Read the information from the first filename if(m_DICOMFiles.size() == 0) throw itk::ExceptionObject("No DICOM files found in the DICOM directory"); m_IOBase->SetFileName(m_DICOMFiles[0]); m_IOBase->ReadImageInformation(); } else { m_IOBase->SetFileName(FileName); m_IOBase->ReadImageInformation(); } // Based on the component type, read image in native mode switch(m_IOBase->GetComponentType()) { case itk::ImageIOBase::UCHAR: DoReadNative(FileName, folder); break; case itk::ImageIOBase::CHAR: DoReadNative(FileName, folder); break; case itk::ImageIOBase::USHORT: DoReadNative(FileName, folder); break; case itk::ImageIOBase::SHORT: DoReadNative(FileName, folder); break; case itk::ImageIOBase::UINT: DoReadNative(FileName, folder); break; case itk::ImageIOBase::INT: DoReadNative(FileName, folder); break; case itk::ImageIOBase::ULONG: DoReadNative(FileName, folder); break; case itk::ImageIOBase::LONG: DoReadNative(FileName, folder); break; case itk::ImageIOBase::FLOAT: DoReadNative(FileName, folder); break; case itk::ImageIOBase::DOUBLE: DoReadNative(FileName, folder); break; default: throw itk::ExceptionObject("Unknown Pixel Type when reading image"); } // Get rid of the IOBase, it may store useless data (in case of NIFTI) m_NativeType = m_IOBase->GetComponentType(); m_NativeComponents = m_IOBase->GetNumberOfComponents(); m_NativeTypeString = m_IOBase->GetComponentTypeAsString(m_NativeType); m_NativeFileName = m_IOBase->GetFileName(); m_NativeByteOrder = m_IOBase->GetByteOrder(); m_NativeSizeInBytes = m_IOBase->GetImageSizeInBytes(); m_IOBase = NULL; } template void GuidedNativeImageIO ::DoReadNative(const char *FileName, Registry &folder) { // Define the image type of interest typedef itk::VectorImage NativeImageType; // There is a special handler for the DICOM case! if(m_FileFormat == FORMAT_DICOM && m_DICOMFiles.size() > 1) { // It seems that ITK can't yet read DICOM into a VectorImage. typedef itk::OrientedImage GreyImageType; // Create an image series reader typedef itk::ImageSeriesReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); // Set the filenames and read reader->SetFileNames(m_DICOMFiles); // Set the IO // typename GDCMImageIO::Pointer dicomio = GDCMImageIO::New(); // dicomio->SetMaxSizeLoadEntry(0xffff); // m_IOBase = dicomio; reader->SetImageIO(m_IOBase); // Update reader->Update(); // Convert the image into VectorImage format typedef itk::ImageToVectorImageFilter FilterType; typename FilterType::Pointer flt = FilterType::New(); flt->SetInput(0, reader->GetOutput()); flt->Update(); m_NativeImage = flt->GetOutput(); m_NativeImage->SetDirection(flt->GetOutput()->GetDirection()); // Copy the metadata from the first scan in the series const typename ReaderType::DictionaryArrayType *darr = reader->GetMetaDataDictionaryArray(); if(darr->size() > 0) m_NativeImage->SetMetaDataDictionary(*((*darr)[0])); } else { // Non-DICOM: read from single image // We no longer use ImageFileReader here because of an issue: the // m_IOBase may have an open file handle (from call to ReadImageInfo) // so passing it in to the Reader would cause an IO error (this actually // happens for GIPL). So we copy some of the code from ImageFileReader // Create the native image typename NativeImageType::Pointer image = NativeImageType::New(); // Initialize the direction and spacing, etc typename NativeImageType::SizeType dim; dim.Fill(1); typename NativeImageType::PointType org; org.Fill(0.0); typename NativeImageType::SpacingType spc; spc.Fill(1.0); typename NativeImageType::DirectionType dir; dir.SetIdentity(); size_t nd = m_IOBase->GetNumberOfDimensions(); if(nd > 3) nd = 3; for(unsigned int i = 0; i < nd; i++) { spc[i] = m_IOBase->GetSpacing(i); org[i] = m_IOBase->GetOrigin(i); for(size_t j = 0; j < nd; j++) dir(j,i) = m_IOBase->GetDirection(i)[j]; dim[i] = m_IOBase->GetDimensions(i); } image->SetSpacing(spc); image->SetOrigin(org); image->SetDirection(dir); image->SetMetaDataDictionary(m_IOBase->GetMetaDataDictionary()); // Set the regions and allocate typename NativeImageType::RegionType region; typename NativeImageType::IndexType index = {{0, 0, 0}}; region.SetIndex(index); region.SetSize(dim); image->SetRegions(region); image->SetVectorLength(m_IOBase->GetNumberOfComponents()); image->Allocate(); // Set the IO region itk::ImageIORegion ioRegion(3); itk::ImageIORegionAdaptor<3>::Convert(region, ioRegion, index); m_IOBase->SetIORegion(ioRegion); // Read the image into the buffer m_IOBase->Read(image->GetBufferPointer()); m_NativeImage = image; /* typedef ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName(FileName); reader->SetImageIO(m_IOBase); reader->Update(); m_NativeImage = reader->GetOutput(); */ } // Disconnect the image from the readers, allowing them to be deleted // m_NativeImage->DisconnectPipeline(); // Sometimes images have negative voxel spacing, which SNAP does not recognize // Check if voxel spacings need to be regularized typename NativeImageType::DirectionType direction = m_NativeImage->GetDirection(); typename NativeImageType::SpacingType spacing = m_NativeImage->GetSpacing(); typename NativeImageType::DirectionType factor; factor.SetIdentity(); bool needRegularization = false; for (int i = 0; i < 3; ++i) { if (spacing[i] < 0) { spacing[i] *= -1.0; factor[i][i] *= -1.0; needRegularization = true; } } if (needRegularization) { direction *= factor; m_NativeImage->SetDirection(direction); m_NativeImage->SetSpacing(spacing); } } /* template std::string GuidedImageIO ::GetRAICode(ImageType *image, Registry &folder) { // See what's stored in the registry std::string rai_registry = folder["Orientation"][""]; if(ImageCoordinateGeometry::IsRAICodeValid(rai_registry.c_str())) return rai_registry; // Compute the RAI code from the direction cosines return ImageCoordinateGeometry:: ConvertDirectionMatrixToClosestRAICode( m_Image->GetDirection().GetVnlMatrix()); } */ template void GuidedNativeImageIO ::SaveImage(const char *FileName, Registry &folder, itk::Image *image) { // Create an Image IO based on the folder CreateImageIO(FileName, folder, false); // Save the image typedef itk::ImageFileWriter< itk::Image > WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName(FileName); if(m_IOBase) writer->SetImageIO(m_IOBase); writer->SetInput(image); writer->Update(); } /***************************************************************************** * ADAPTER OBJECTS TO CAST NATIVE IMAGE TO GIVEN IMAGE ****************************************************************************/ template typename RescaleNativeImageToScalar::OutputImageType * RescaleNativeImageToScalar::operator()(GuidedNativeImageIO *nativeIO) { // Get the native image pointer itk::ImageBase<3> *native = nativeIO->GetNativeImage(); // Allocate the output image m_Output = OutputImageType::New(); m_Output->CopyInformation(native); m_Output->SetMetaDataDictionary(native->GetMetaDataDictionary()); m_Output->SetRegions(native->GetBufferedRegion()); // Cast image from native format to TPixel itk::ImageIOBase::IOComponentType itype = nativeIO->GetComponentTypeInNativeImage(); switch(itype) { case itk::ImageIOBase::UCHAR: DoCast(native); break; case itk::ImageIOBase::CHAR: DoCast(native); break; case itk::ImageIOBase::USHORT: DoCast(native); break; case itk::ImageIOBase::SHORT: DoCast(native); break; case itk::ImageIOBase::UINT: DoCast(native); break; case itk::ImageIOBase::INT: DoCast(native); break; case itk::ImageIOBase::ULONG: DoCast(native); break; case itk::ImageIOBase::LONG: DoCast(native); break; case itk::ImageIOBase::FLOAT: DoCast(native); break; case itk::ImageIOBase::DOUBLE: DoCast(native); break; default: throw itk::ExceptionObject("Unknown Pixel Type when reading image"); } // Return the output image return m_Output; } template template void RescaleNativeImageToScalar ::DoCast(itk::ImageBase<3> *native) { // Get the native image typedef itk::VectorImage InputImageType; typedef itk::ImageRegionConstIterator InputIterator; typename InputImageType::Pointer input = dynamic_cast(native); assert(input); // Special case: native image is the same as target image if(typeid(TPixel) == typeid(TNative)) { typename OutputImageType::PixelContainer *inbuff = dynamic_cast(input->GetPixelContainer()); assert(inbuff); m_Output->SetPixelContainer(inbuff); m_NativeScale = 1.0; m_NativeShift = 0.0; return; } // Otherwise, allocate the buffer in the output image m_Output->Allocate(); // We must compute the range of the input data size_t nvoxels = input->GetBufferedRegion().GetNumberOfPixels(); size_t ncomp = input->GetNumberOfComponentsPerPixel(); double imin = itk::NumericTraits::max(); double imax = -itk::NumericTraits::max(); TPixel omax = itk::NumericTraits::max(); TPixel omin = itk::NumericTraits::min(); if(ncomp > 1) { for(InputIterator it(input, input->GetBufferedRegion()); !it.IsAtEnd(); ++it) { double mag = it.Get().GetSquaredNorm(); if(mag < imin) imin = mag; if(mag > imax) imax = mag; } imin = sqrt(imin); imax = sqrt(imax); } else { for(size_t i = 0; i < nvoxels; i++) { // Have to cast to double here double val = input->GetBufferPointer()[i]; if(val < imin) imin = val; if(val > imax) imax = val; } } // We must compute a scale and shift factor double scale = 1.0, shift = 0.0; // Now we have to be careful, depending on the type of the input voxel // For float and double, we map the input range into the output range if(!itk::NumericTraits::is_integer || ncomp > 1) { // Test whether the input image is actually an integer image cast to // floating point. In that case, there is no need for conversion bool isint = false; if(1.0 * omin <= imin && 1.0 * omax >= imax && ncomp == 1) { isint = true; for(size_t i = 0; i < nvoxels; i++) { TNative vin = input->GetBufferPointer()[i]; TNative vcmp = static_cast(static_cast(vin + 0.5)); if(vin != vcmp) { isint = false; break; } } } // If underlying data is really integer, no scale or shift is necessary // except that to round (so floating values like 0.9999999 get mapped to // 1 not to 0 if(isint) { scale = 1.0; shift = 0.0; } // If the min and max are the same, we map that value to zero else if(imin == imax) { scale = 1.0; shift = -imax; } else { // Compute the scaling factor to map image into output range scale = (1.0 * omax - 1.0 * omin) / (imax - imin); shift = omin / scale - imin; } } // For integer types we only need to take action if the range is outside // of the supported range. We cast to double to make sure the comparison // is valid else if(1.0 * imin < 1.0 * omin || 1.0 * imax > omax) { // Can we solve the problem by a shift only? if(1.0 * imax - 1.0 * imin <= 1.0 * omax - 1.0 * omin) { scale = 1.0; shift = 1.0 * omin - 1.0 * imin; } } // Map the values from input vector image to output image. Not using // iterators to increase speed and avoid unnecessary constructors TNative *bn = input->GetBufferPointer(); TPixel *bo = m_Output->GetBufferPointer(); if(ncomp == 1) { for(size_t i = 0; i < nvoxels; i++, bn++) { bo[i] = (TPixel) ((*bn + shift)*scale + 0.5); } } else { for(size_t i = 0; i < nvoxels; i++) { double val = 0.0; for(size_t k = 0; k < ncomp; k++, bn++) val += (*bn) * (*bn); bo[1] = (TPixel) ((sqrt(val) + shift)*scale + 0.5); } } // Store the shift and the scale needed to take the TPixel values // to the TNative values m_NativeScale = 1.0 / scale; m_NativeShift = - shift; } template typename CastNativeImageBase::OutputImageType * CastNativeImageBase ::operator()(GuidedNativeImageIO *nativeIO) { // Get the native image pointer itk::ImageBase<3> *native = nativeIO->GetNativeImage(); // Cast image from native format to TPixel itk::ImageIOBase::IOComponentType itype = nativeIO->GetComponentTypeInNativeImage(); switch(itype) { case itk::ImageIOBase::UCHAR: DoCast(native); break; case itk::ImageIOBase::CHAR: DoCast(native); break; case itk::ImageIOBase::USHORT: DoCast(native); break; case itk::ImageIOBase::SHORT: DoCast(native); break; case itk::ImageIOBase::UINT: DoCast(native); break; case itk::ImageIOBase::INT: DoCast(native); break; case itk::ImageIOBase::ULONG: DoCast(native); break; case itk::ImageIOBase::LONG: DoCast(native); break; case itk::ImageIOBase::FLOAT: DoCast(native); break; case itk::ImageIOBase::DOUBLE: DoCast(native); break; default: throw itk::ExceptionObject("Unknown Pixel Type when reading image"); } // Return the output image return m_Output; } template template void CastNativeImageBase ::DoCast(itk::ImageBase<3> *native) { // Get the native image typedef itk::VectorImage InputImageType; typename InputImageType::Pointer input = reinterpret_cast(native); assert(input); // Make sure the number of components matches TCastFunctor functor; // If the native image does not have three components, we crash if(input->GetNumberOfComponentsPerPixel() != functor.GetNumberOfDimensions()) throw IRISException( "Can not convert image to target format (%s).\n" "Image has %d components per pixel, but it should have %d components.", typeid(TPixel).name(), input->GetNumberOfComponentsPerPixel(), functor.GetNumberOfDimensions() ); // Allocate the output image m_Output = OutputImageType::New(); m_Output->CopyInformation(native); m_Output->SetRegions(native->GetBufferedRegion()); // Special case: native image is the same as target image if(typeid(TPixel) == typeid(TNative)) { typename OutputImageType::PixelContainer *inbuff = dynamic_cast(input->GetPixelContainer()); assert(inbuff); m_Output->SetPixelContainer(inbuff); return; } // Otherwise, allocate the buffer in the output image m_Output->Allocate(); // We simply cast every component of every pixel from input to output size_t nvoxels = native->GetBufferedRegion().GetNumberOfPixels(); size_t ncomp = functor.GetNumberOfDimensions(); TNative *bn = input->GetBufferPointer(); TPixel *bo = m_Output->GetBufferPointer(); for(size_t i = 0; i < nvoxels; i++, bn+=ncomp, bo++) functor(bn, bo); } /* template typename CastNativeImageToScalar::OutputImageType * CastNativeImageToScalar ::operator()(GuidedNativeImageIO *nativeIO) { // Get the native image pointer itk::ImageBase<3> *native = nativeIO->GetNativeImage(); // Allocate the output image m_Output = OutputImageType::New(); m_Output->CopyInformation(native); m_Output->SetRegions(native->GetBufferedRegion()); m_Output->Allocate(); // Cast image from native format to TPixel itk::ImageIOBase::IOComponentType itype = nativeIO->GetComponentTypeInNativeImage(); switch(itype) { case itk::ImageIOBase::UCHAR: DoCast(native); break; case itk::ImageIOBase::CHAR: DoCast(native); break; case itk::ImageIOBase::USHORT: DoCast(native); break; case itk::ImageIOBase::SHORT: DoCast(native); break; case itk::ImageIOBase::UINT: DoCast(native); break; case itk::ImageIOBase::INT: DoCast(native); break; case itk::ImageIOBase::ULONG: DoCast(native); break; case itk::ImageIOBase::LONG: DoCast(native); break; case itk::ImageIOBase::FLOAT: DoCast(native); break; case itk::ImageIOBase::DOUBLE: DoCast(native); break; default: throw itk::ExceptionObject("Unknown Pixel Type when reading image"); } // Return the output image return m_Output; } template template void CastNativeImageToScalar ::DoCast(itk::ImageBase<3> *native) { // Get the native image typedef itk::VectorImage InputImageType; typename InputImageType::Pointer input = reinterpret_cast(native); assert(input); // If the native image does not have one component, we crash if(input->GetNumberOfComponentsPerPixel() != 1) throw itk::ExceptionObject( "Specified image can not be read as a scalar image.\n" "It does not have 1 component per pixel."); // We simply cast every component of every pixel from input to output typedef itk::ImageRegionConstIterator InputIterator; typedef itk::ImageRegionIterator OutputIterator; InputIterator it(input, input->GetBufferedRegion()); OutputIterator oit(m_Output, m_Output->GetBufferedRegion()); for(; !it.IsAtEnd(); ++it, ++oit) { oit.Set((TPixel) it.Get()[0]); } } */ template class RescaleNativeImageToScalar; template class CastNativeImageBase >; template class CastNativeImageBase >; template class CastNativeImageBase >; /* template class CastNativeImageToRGB; template class CastNativeImageToScalar; template class CastNativeImageToScalar; */ template void GuidedNativeImageIO::SaveImage(const char *, Registry &, itk::Image *); template void GuidedNativeImageIO::SaveImage(const char *, Registry &, itk::Image *); template void GuidedNativeImageIO::SaveImage(const char *, Registry &, itk::Image *); template void GuidedNativeImageIO::SaveImage(const char *, Registry &, itk::Image *); itksnap/Logic/ImageWrapper/GuidedNativeImageIO.h0000755000076500000240000002425611455626760021130 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GuidedNativeImageIO.h,v $ Language: C++ Date: $Date: 2010/10/14 16:21:04 $ Version: $Revision: 1.6 $ 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 __GuidedNativeImageIO_h_ #define __GuidedNativeImageIO_h_ #include "Registry.h" #include "itkSmartPointer.h" #include "itkOrientedImage.h" #include "itkImageIOBase.h" namespace itk { template class Image; class ImageIOBase; } /** * \class GuidedNativeImageIO * \brief This class performs image IO based on user-supplied parameters such * as explicit IO type, and for some types such as raw, the necessary additional * information. */ class GuidedNativeImageIO { public: enum FileFormat { FORMAT_MHA=0, FORMAT_GIPL, FORMAT_RAW, FORMAT_ANALYZE, FORMAT_DICOM, FORMAT_GE4, FORMAT_GE5, FORMAT_NIFTI, FORMAT_SIEMENS, FORMAT_VTK, FORMAT_VOXBO_CUB, FORMAT_NRRD, FORMAT_COUNT}; enum RawPixelType { PIXELTYPE_UCHAR=0, PIXELTYPE_CHAR, PIXELTYPE_USHORT, PIXELTYPE_SHORT, PIXELTYPE_UINT, PIXELTYPE_INT, PIXELTYPE_FLOAT, PIXELTYPE_DOUBLE, PIXELTYPE_COUNT}; /** * A descriptor of file formats supported in ITK. Describes whether * the format can support different needs in SNAP */ struct FileFormatDescriptor { std::string name; std::string pattern; bool can_write; bool can_store_orientation; bool can_store_float; bool can_store_short; }; // Image type. This is only for 3D images. typedef itk::ImageIOBase IOBase; typedef itk::SmartPointer IOBasePointer; typedef itk::ImageBase<3> ImageBase; typedef itk::SmartPointer ImageBasePointer; GuidedNativeImageIO(); virtual ~GuidedNativeImageIO() { DeallocateNativeImage(); } /** * This method loads an image from the given filename. A registry can be * supplied to assist the loading. The registry specifies the desired IO * type, and for special types, such as Raw it can provide the parameters * such as header size and image dimensions. The image is read in native * format and stored inside of this object. In order to cast the image to * the format of interest, the user must cast the image to one of the * desired formats. */ void ReadNativeImage(const char *FileName, Registry &folder); /** * Get the number of components in the native image read by ReadNativeImage. */ size_t GetNumberOfComponentsInNativeImage() const; /** * Get the component type in the native image */ itk::ImageIOBase::IOComponentType GetComponentTypeInNativeImage() const { return m_NativeType; } itk::ImageIOBase::ByteOrder GetByteOrderInNativeImage() const { return m_NativeByteOrder; } std::string GetComponentTypeAsStringInNativeImage() const { return m_NativeTypeString; } std::string GetFileNameOfNativeImage() const { return m_NativeFileName; } unsigned long GetFileSizeOfNativeImage() const { return m_NativeSizeInBytes; } /** * This method returns the image internally stored in this object. This is * a pointer to an itk::VectorImage of some native format. Use one of the * Cast objects to cast it to an image of the type you want */ itk::ImageBase<3> *GetNativeImage() const { return m_NativeImage; } /** * Has a native image been loaded? */ bool IsNativeImageLoaded() const { return m_NativeImage.IsNotNull(); } /** * Discard the native image. Use this once you've cast the native image to * the format of interest. */ void DeallocateNativeImage() { m_IOBase = NULL; m_NativeImage = NULL; } /** * Get RAI code for an image. If there is nothing in the registry, this will * try getting the code from the image header. If there is no way to get the * image code, this will return an empty string */ // std::string GetRAICode(ImageType *image, Registry &folder); /** * Save an image using the Registry folder to specify parameters. This method * is templated over the image type. */ template void SaveImage(const char *FileName, Registry &folder, itk::Image *image); /** Parse registry to get file format */ static FileFormat GetFileFormat(Registry &folder, FileFormat dflt = FORMAT_COUNT); /** Set the file format in a registry */ static void SetFileFormat(Registry &folder, FileFormat format); /** Get format descriptor for a format */ static FileFormatDescriptor GetFileFormatDescriptor(FileFormat fmt) { return m_FileFormatDescrictorArray[fmt]; } /** Parse registry to get pixel type of raw file */ static RawPixelType GetPixelType(Registry &folder, RawPixelType dflt = PIXELTYPE_COUNT); /** Set the file format in a registry */ static void SetPixelType(Registry &folder, RawPixelType type); // Get the output of the last operation // irisGetMacro(IOBase, itk::ImageIOBase *); private: /** * Create an ImageIO object using a registry folder. Second parameter is * true for reading the file, false for writing the file */ void CreateImageIO(const char *fname, Registry &folder, bool read); /** Templated function to create RAW image IO */ template void CreateRawImageIO(Registry &folder); /** Templated function that reads a scalar image in its native datatype */ template void DoReadNative(const char *fname, Registry &folder); /** * This is a vector image in native format. It stores the data read from the * image file. The user must cast it to a desired type to use it. */ ImageBasePointer m_NativeImage; // The IO base used to read the files IOBasePointer m_IOBase; // This information is copied from IOBase in order to delete IOBase at the // earliest possible point, so as to conserve memory IOBase::IOComponentType m_NativeType; size_t m_NativeComponents; unsigned long m_NativeSizeInBytes; std::string m_NativeTypeString, m_NativeFileName; IOBase::ByteOrder m_NativeByteOrder; // The file format FileFormat m_FileFormat; // List of filenames for DICOM std::vector m_DICOMFiles; /** Registry mappings for these enums */ static bool m_StaticDataInitialized; static RegistryEnumMap m_EnumFileFormat; static RegistryEnumMap m_EnumRawPixelType; // File format descriptors static const FileFormatDescriptor m_FileFormatDescrictorArray[]; }; /** * \class RescaleNativeImageToScalar * \brief An adapter class that rescales a native-format image from * GuidedNativeImageIO to user-specified scalar type */ template class RescaleNativeImageToScalar { public: RescaleNativeImageToScalar() {} virtual ~RescaleNativeImageToScalar() {} typedef itk::OrientedImage OutputImageType; typedef itk::ImageBase<3> NativeImageType; // Constructor, takes pointer to native image OutputImageType *operator()(GuidedNativeImageIO *nativeIO); // Get the scale factor to map from scalar to native irisGetMacro(NativeScale, double); // Get the shift to map from scalar to native irisGetMacro(NativeShift, double); private: typename OutputImageType::Pointer m_Output; double m_NativeScale, m_NativeShift; // Method that does the casting template void DoCast(itk::ImageBase<3> *native); }; /** * \class CastNativeImageBase * \brief An adapter class that casts a native-format image from * GuidedNativeImageIO to an image of type TPixel. The actual casting * is done using the functor TCastFunctor. Use derived classes. */ template class CastNativeImageBase { public: typedef itk::OrientedImage OutputImageType; typedef itk::ImageBase<3> NativeImageType; // Constructor, takes pointer to native image OutputImageType *operator()(GuidedNativeImageIO *nativeIO); private: typename OutputImageType::Pointer m_Output; // Method that does the casting template void DoCast(itk::ImageBase<3> *native); }; // Functor used for scalar to scalar casting template class CastToScalarFunctor { public: template void operator()(TNative *src, TPixel *trg) { *trg = (TPixel) *src; } size_t GetNumberOfDimensions() { return 1; } }; // Functor used for scalar to array casting template class CastToArrayFunctor { public: template void operator()(TNative *src, TPixel *trg) { for(unsigned int i = 0; i < VDim; i++) (*trg)[i] = (typename TPixel::ComponentType) src[i]; } size_t GetNumberOfDimensions() { return VDim; } }; /** * \class CastNativeImageToRGB * \brief An adapter class that casts a native-format image from * GuidedNativeImageIO to an RGB image */ template class CastNativeImageToRGB : public CastNativeImageBase > {}; /** * \class CastNativeImageToScalar * \brief An adapter class that casts a native-format image from * GuidedNativeImageIO to a scalar image of given type */ template class CastNativeImageToScalar : public CastNativeImageBase > {}; #endif itksnap/Logic/ImageWrapper/ImageIORoutines.h0000644000076500000240000000525610735614372020360 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageIORoutines.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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 __ImageIORoutines_h_ #define __ImageIORoutines_h_ #include "itkImage.h" #include "itkImageIOBase.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" // This file contains a collection of useful IO routines /** Load an image from a file, throw exception if something happens */ template void LoadImageFromFile(const char *fname, typename itk::SmartPointer &target, itk::ImageIOBase *io = 0) throw(itk::ExceptionObject) { // Load the image using itk IO typedef itk::ImageFileReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName(fname); if(io) reader->SetImageIO(io); reader->Update(); target = reader->GetOutput(); } /** Save an image to a file, throw exception if something happens */ template void SaveImageToFile(const char *fname, TImageType *source, itk::ImageIOBase *io = 0) throw(itk::ExceptionObject) { // Load the image using itk IO typedef itk::ImageFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->SetFileName(fname); if(io) writer->SetImageIO(io); writer->SetInput(source); writer->Update(); } #endif // __ImageIORoutines_h_ itksnap/Logic/ImageWrapper/ImageWrapper.h0000644000076500000240000003117611277127603017736 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageWrapper.h,v $ Language: C++ Date: $Date: 2009/11/13 00:59:47 $ Version: $Revision: 1.14 $ 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 __ImageWrapper_h_ #define __ImageWrapper_h_ // Smart pointers have to be included from ITK, can't forward reference them #include "SNAPCommon.h" #include "ImageCoordinateTransform.h" #include #include #include // Forward declarations to IRIS classes template class IRISSlicer; class SNAPSegmentationROISettings; namespace itk { template class ImageBase; } /** * \class ImageWrapper * \brief Abstract parent class for all image wrappers * \see ImageWrapper */ class ImageWrapperBase { public: // Definition for the display slice type typedef itk::RGBAPixel DisplayPixelType; typedef itk::Image DisplaySliceType; typedef itk::SmartPointer DisplaySlicePointer; virtual ~ImageWrapperBase() { /*To avoid compiler warning.*/ } virtual const ImageCoordinateTransform &GetImageToDisplayTransform( unsigned int) const = 0; virtual void SetImageToDisplayTransform( unsigned int, const ImageCoordinateTransform &) = 0; virtual Vector3ui GetSliceIndex() const = 0; virtual void SetSliceIndex(const Vector3ui &) = 0; virtual itk::ImageBase<3> *GetImageBase() const = 0; virtual bool IsInitialized() const = 0; virtual Vector3ui GetSize() const = 0; virtual unsigned char GetAlpha() const = 0; virtual void SetAlpha(unsigned char) = 0; virtual void ToggleVisibility() = 0; virtual itk::ImageRegion<3> GetBufferedRegion() const = 0; virtual Vector3d TransformVoxelIndexToPosition(const Vector3ui &iVoxel) const = 0; virtual Vector3d TransformVoxelIndexToNIFTICoordinates(const Vector3d &iVoxel) const = 0; virtual Vector3d TransformNIFTICoordinatesToVoxelIndex(const Vector3d &vNifti) const = 0; virtual vnl_matrix_fixed GetNiftiSform() const = 0; virtual DisplaySlicePointer GetDisplaySlice(unsigned int dim) const = 0; // Delete internal data structures virtual void Reset() = 0; }; /** * \class ImageWrapper * \brief A wrapper around an itk::OrientedImage and related pipelines. * * Image Wrapper serves as a wrapper around an image pointer, and * is used to unify the treatment of different kinds of images in * SNaP. */ template class ImageWrapper : public ImageWrapperBase { public: // Basic type definitions typedef itk::OrientedImage ImageType; typedef typename itk::SmartPointer ImagePointer; // Slice image type typedef itk::Image SliceType; typedef typename itk::SmartPointer SlicePointer; // Slicer type typedef IRISSlicer SlicerType; typedef typename itk::SmartPointer SlicerPointer; // Iterator types typedef typename itk::ImageRegionIterator Iterator; typedef typename itk::ImageRegionConstIterator ConstIterator; /** * Default constructor. Creates an image wrapper with a blank internal * image */ ImageWrapper(); /** * Copy constructor. Copies the contents of the passed-in image wrapper. */ ImageWrapper(const ImageWrapper ©); /** Destructor */ virtual ~ImageWrapper(); /** * Initialize the image wrapper to match another image wrapper, setting the * image pixels to a default value. The slice indices and the transforms will * all be updated to match the source */ virtual void InitializeToWrapper( const ImageWrapperBase *source, const TPixel &value); /** * Initialize the image wrapper with a combination of another image wrapper and * an actual image. This will update the indices and transforms from the * source wrapper, otherwise, it's equivalent to SetImage() */ virtual void InitializeToWrapper( const ImageWrapperBase *source, ImageType *image); /** * Initialize the internal image to a blank image of size x,y,z. */ // virtual void InitializeToSize(unsigned int x, unsigned int y,unsigned int z); /** * Clear the data associated with storing an image */ virtual void Reset(); /** * Is the image initialized? */ virtual bool IsInitialized() const; /** * Get reference to a voxel at a given position. */ virtual TPixel &GetVoxelForUpdate(unsigned int x, unsigned int y, unsigned int z); virtual TPixel &GetVoxelForUpdate(const Vector3ui &index); /** * Get a constant reference to a voxel at a given position. */ virtual const TPixel &GetVoxel(unsigned int x, unsigned int y, unsigned int z) const; virtual const TPixel &GetVoxel(const Vector3ui &index) const; /** Return some image info independently of pixel type */ virtual itk::ImageBase<3> *GetImageBase() const { return m_Image.GetPointer(); } /** * Return the pointed to the ITK image encapsulated by this wrapper. */ virtual ImageType *GetImage() const { return m_Image; } /** * Get the slicer inside this wrapper */ virtual SlicerType *GetSlicer(unsigned int iDirection) const { return m_Slicer[iDirection]; } /** * Get the size of the image */ virtual Vector3ui GetSize() const; /** * Get the buffered region of the image */ virtual itk::ImageRegion<3> GetBufferedRegion() const { return m_Image->GetBufferedRegion(); } /** Get the current slice index */ virtual Vector3ui GetSliceIndex() const; /** * Set the current slice index in all three dimensions. The index should * be specified in the image coordinates, the slices will be generated * in accordance with the transforms that are specified */ virtual void SetSliceIndex(const Vector3ui &cursor); /** * Set the trasforms from image space to one of the three display slices (be * sure to set all three, or you'll get weird looking slices! */ virtual void SetImageToDisplayTransform( unsigned int iSlice,const ImageCoordinateTransform &transform); /** Get the coordinate transform for each display slice */ virtual const ImageCoordinateTransform &GetImageToDisplayTransform( unsigned int iSlice) const; /** * Use a default image-slice transformation, the first slice is along z, * the second along y, the third, along x, all directions of traversal are * positive. */ virtual void SetImageToDisplayTransformsToDefault(); /** * Get a slice of the image in a given direction */ virtual SliceType *GetSlice(unsigned int dimension); /** * This method exposes the scalar pointer in the image */ virtual TPixel *GetVoxelPointer() const; /** * Get the number of voxels */ size_t GetNumberOfVoxels() const; /** * Pring debugging info * TODO: Delete this or make is worthwhile */ virtual void PrintDebugInformation(); /** * Replace the image internally stored in this wrapper by another image. */ virtual void SetImage(ImagePointer newImage); /** * This method is used to perform a deep copy of a region of this image * into another image, potentially resampling the region to use a different * voxel size */ virtual ImagePointer DeepCopyRegion(const SNAPSegmentationROISettings &roi, itk::Command *progressCommand = NULL) const = 0; /** * Get an iterator for traversing the image. The iterator is initialized * to point to the beginning of the image */ virtual ConstIterator GetImageConstIterator() const; virtual Iterator GetImageIterator(); /** For each slicer, find out which image dimension does is slice along */ unsigned int GetDisplaySliceImageAxis(unsigned int slice); /** Transform a voxel index into a spatial position */ Vector3d TransformVoxelIndexToPosition(const Vector3ui &iVoxel) const; /** Transform a voxel index into NIFTI coordinates (RAS) */ Vector3d TransformVoxelIndexToNIFTICoordinates(const Vector3d &iVoxel) const; /** Transform NIFTI coordinates to a continuous voxel index */ Vector3d TransformNIFTICoordinatesToVoxelIndex(const Vector3d &vNifti) const; /** * Replace all voxels with intensity values iOld with values iNew. * \return number of voxels that had been modified */ virtual unsigned int ReplaceIntensity(TPixel iOld, TPixel iNew); /** * Swap intensity values iFirst and iSecond * \return number of voxels that had been modified */ virtual unsigned int SwapIntensities(TPixel iFirst, TPixel iSecond); /** Get the NIFTI s-form matrix for this image */ virtual vnl_matrix_fixed GetNiftiSform() const { return m_NiftiSform; } /** * This static function constructs a NIFTI matrix from the ITK direction * cosines matrix and Spacing and Origin vectors */ static vnl_matrix_fixed ConstructNiftiSform( vnl_matrix m_dir, vnl_vector v_origin, vnl_vector v_spacing) { // Set the NIFTI/RAS transform vnl_matrix m_ras_matrix; vnl_diag_matrix m_scale, m_lps_to_ras; vnl_vector v_ras_offset; // Compute the matrix m_scale.set(v_spacing); m_lps_to_ras.set(vnl_vector(3, 1.0)); m_lps_to_ras[0] = -1; m_lps_to_ras[1] = -1; m_ras_matrix = m_lps_to_ras * m_dir * m_scale; // Compute the vector v_ras_offset = m_lps_to_ras * v_origin; // Create the larger matrix vnl_vector vcol(4, 1.0); vcol.update(v_ras_offset); vnl_matrix_fixed m_sform; m_sform.set_identity(); m_sform.update(m_ras_matrix); m_sform.set_column(3, vcol); return m_sform; } static vnl_matrix_fixed ConstructVTKtoNiftiTransform( vnl_matrix m_dir, vnl_vector v_origin, vnl_vector v_spacing) { vnl_matrix_fixed vox2nii = ConstructNiftiSform(m_dir, v_origin, v_spacing); vnl_matrix_fixed vtk2vox; vtk2vox.set_identity(); for(size_t i = 0; i < 3; i++) { vtk2vox(i,i) = 1.0 / v_spacing[i]; vtk2vox(i,3) = - v_origin[i] / v_spacing[i]; } return vox2nii * vtk2vox; } /** * Get/Set the alpha */ irisSetMacro(Alpha, unsigned char); irisGetMacro(Alpha, unsigned char); virtual void ToggleVisibility(); protected: /** The image that we are wrapping */ ImagePointer m_Image; /** The associated slicer filters */ SlicerPointer m_Slicer[3]; /** The current cursor position (slice index) in image dimensions */ Vector3ui m_SliceIndex; /** * Is the image wrapper initialized? That is a prerequisite for all * operations. */ bool m_Initialized; /** Transparency */ unsigned char m_Alpha; /** A 'saved' value of alpha for when visibility is toggled */ unsigned char m_ToggleAlpha; /** Transform from image space to display space */ ImageCoordinateTransform m_ImageToDisplayTransform[3]; /** Transform from image space to display space */ ImageCoordinateTransform m_DisplayToImageTransform[3]; // Transform from image index to NIFTI world coordinates vnl_matrix_fixed m_NiftiSform, m_NiftiInvSform; /** * Handle a change in the image pointer (i.e., a load operation on the image or * an initialization operation) */ virtual void UpdateImagePointer(ImageType *); /** Common code for the different constructors */ void CommonInitialization(); }; #endif // __ImageWrapper_h_ itksnap/Logic/ImageWrapper/ImageWrapper.txx0000644000076500000240000003352011455626760020333 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageWrapper.txx,v $ Language: C++ Date: $Date: 2010/10/14 16:21:04 $ 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. ----- 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 __ImageWrapper_txx_ #define __ImageWrapper_txx_ #include "ImageWrapper.h" #include "itkImageRegionIterator.h" #include "itkImageSliceConstIteratorWithIndex.h" #include "itkNumericTraits.h" #include "itkRegionOfInterestImageFilter.h" #include "itkIdentityTransform.h" #include "IRISSlicer.h" #include "SNAPSegmentationROISettings.h" #include "itkCommand.h" #include #include template ImageWrapper ::ImageWrapper() { CommonInitialization(); } template ImageWrapper ::~ImageWrapper() { Reset(); } template void ImageWrapper ::CommonInitialization() { // Set initial state m_Initialized = false; // Create slicer objects m_Slicer[0] = SlicerType::New(); m_Slicer[1] = SlicerType::New(); m_Slicer[2] = SlicerType::New(); // Set the transform to identity, which will initialize the directions of the // slicers this->SetImageToDisplayTransformsToDefault(); } template ImageWrapper ::ImageWrapper(const ImageWrapper ©) { CommonInitialization(); // If the source contains an image, make a copy of that image if (copy.IsInitialized() && copy.GetImage()) { // Create and allocate the image ImagePointer newImage = ImageType::New(); newImage->SetRegions(copy.GetImage()->GetBufferedRegion()); newImage->Allocate(); // Copy the image contents TPixel *ptrTarget = newImage->GetBufferPointer(); TPixel *ptrSource = copy.GetImage()->GetBufferPointer(); memcpy(ptrTarget,ptrSource, sizeof(TPixel) * newImage->GetBufferedRegion().GetNumberOfPixels()); UpdateImagePointer(newImage); } } template void ImageWrapper ::PrintDebugInformation() { std::cout << "=== Image Properties ===" << std::endl; std::cout << " Dimensions : " << m_Image->GetLargestPossibleRegion().GetSize() << std::endl; std::cout << " Origin : " << m_Image->GetOrigin() << std::endl; std::cout << " Spacing : " << m_Image->GetSpacing() << std::endl; std::cout << "------------------------" << std::endl; } template void ImageWrapper ::UpdateImagePointer(ImageType *newImage) { // Check if the image size has changed bool hasSizeChanged = (!m_Image) || (newImage->GetLargestPossibleRegion().GetSize() != m_Image->GetLargestPossibleRegion().GetSize()); // Change the input of the slicers m_Slicer[0]->SetInput(newImage); m_Slicer[1]->SetInput(newImage); m_Slicer[2]->SetInput(newImage); // If so, the coordinate transform needs to be reinitialized to identity if(hasSizeChanged) { // Reset the transform to identity this->SetImageToDisplayTransformsToDefault(); // Reset the slice positions to zero this->SetSliceIndex(Vector3ui(0,0,0)); } // Update the image m_Image = newImage; // Mark the image as Modified to enforce correct sequence of // operations with MinMaxCalc m_Image->Modified(); // Set the NIFTI/RAS transform m_NiftiSform = ConstructNiftiSform( m_Image->GetDirection().GetVnlMatrix(), m_Image->GetOrigin().GetVnlVector(), m_Image->GetSpacing().GetVnlVector()); m_NiftiInvSform = vnl_inverse(m_NiftiSform); // We have been initialized m_Initialized = true; } template void ImageWrapper ::InitializeToWrapper(const ImageWrapperBase *source, ImageType *image) { // Call the common update method UpdateImagePointer(image); // Update the image-display transforms for(unsigned int d=0;d<3;d++) SetImageToDisplayTransform(d,source->GetImageToDisplayTransform(d)); // Update the slice index SetSliceIndex(source->GetSliceIndex()); } template void ImageWrapper ::InitializeToWrapper(const ImageWrapperBase *source, const TPixel &value) { // Allocate the image ImagePointer newImage = ImageType::New(); newImage->SetRegions(source->GetImageBase()->GetBufferedRegion().GetSize()); newImage->Allocate(); newImage->FillBuffer(value); newImage->SetOrigin(source->GetImageBase()->GetOrigin()); newImage->SetSpacing(source->GetImageBase()->GetSpacing()); newImage->SetDirection(source->GetImageBase()->GetDirection()); // Call the common update method UpdateImagePointer(newImage); // Update the image-display transforms for(unsigned int d=0;d<3;d++) SetImageToDisplayTransform(d,source->GetImageToDisplayTransform(d)); // Update the slice index SetSliceIndex(source->GetSliceIndex()); } template void ImageWrapper ::SetImage(ImagePointer newImage) { UpdateImagePointer(newImage); } template void ImageWrapper ::Reset() { if (m_Initialized) { m_Image->ReleaseData(); m_Image = NULL; } m_Initialized = false; m_Alpha = 128; m_ToggleAlpha = 128; } template bool ImageWrapper ::IsInitialized() const { return m_Initialized; } template inline const TPixel& ImageWrapper ::GetVoxel(const Vector3ui &index) const { return GetVoxel(index[0],index[1],index[2]); } template inline TPixel& ImageWrapper ::GetVoxelForUpdate(const Vector3ui &index) { return GetVoxelForUpdate(index[0],index[1],index[2]); } template inline TPixel& ImageWrapper ::GetVoxelForUpdate(unsigned int x, unsigned int y, unsigned int z) { itk::Index<3> index; index[0] = x; index[1] = y; index[2] = z; // Verify that the pixel is contained by the image at debug time assert(m_Image && m_Image->GetLargestPossibleRegion().IsInside(index)); // Return the pixel return m_Image->GetPixel(index); } template inline const TPixel& ImageWrapper ::GetVoxel(unsigned int x, unsigned int y, unsigned int z) const { itk::Index<3> index; index[0] = x; index[1] = y; index[2] = z; // Verify that the pixel is contained by the image at debug time assert(m_Image && m_Image->GetLargestPossibleRegion().IsInside(index)); // Return the pixel return m_Image->GetPixel(index); } template typename ImageWrapper::ConstIterator ImageWrapper ::GetImageConstIterator() const { ConstIterator it(m_Image,m_Image->GetLargestPossibleRegion()); it.GoToBegin(); return it; } template typename ImageWrapper::Iterator ImageWrapper ::GetImageIterator() { Iterator it(m_Image,m_Image->GetLargestPossibleRegion()); it.GoToBegin(); return it; } template Vector3ui ImageWrapper ::GetSliceIndex() const { return m_SliceIndex; } template void ImageWrapper ::SetSliceIndex(const Vector3ui &cursor) { // Save the cursor position m_SliceIndex = cursor; // Select the appropriate slice for each slicer for(unsigned int i=0;i<3;i++) { // Which axis does this slicer slice? unsigned int axis = m_Slicer[i]->GetSliceDirectionImageAxis(); // Set the slice using that axis m_Slicer[i]->SetSliceIndex(cursor[axis]); } } template void ImageWrapper ::SetImageToDisplayTransformsToDefault() { ImageCoordinateTransform id[3]; id[0].SetTransform(Vector3i(1,2,3),Vector3ui(0,0,0)); id[1].SetTransform(Vector3i(1,3,2),Vector3ui(0,0,0)); id[2].SetTransform(Vector3i(2,3,1),Vector3ui(0,0,0)); SetImageToDisplayTransform(0,id[0]); SetImageToDisplayTransform(1,id[1]); SetImageToDisplayTransform(2,id[2]); } template const ImageCoordinateTransform& ImageWrapper ::GetImageToDisplayTransform(unsigned int iSlice) const { return m_ImageToDisplayTransform[iSlice]; } template void ImageWrapper ::SetImageToDisplayTransform(unsigned int iSlice, const ImageCoordinateTransform &transform) { // Get the transform and its inverse m_ImageToDisplayTransform[iSlice] = transform; m_DisplayToImageTransform[iSlice] = transform.Inverse(); // Tell slicer in which directions to slice m_Slicer[iSlice]->SetSliceDirectionImageAxis( m_DisplayToImageTransform[iSlice].GetCoordinateIndexZeroBased(2)); m_Slicer[iSlice]->SetLineDirectionImageAxis( m_DisplayToImageTransform[iSlice].GetCoordinateIndexZeroBased(1)); m_Slicer[iSlice]->SetPixelDirectionImageAxis( m_DisplayToImageTransform[iSlice].GetCoordinateIndexZeroBased(0)); m_Slicer[iSlice]->SetPixelTraverseForward( m_DisplayToImageTransform[iSlice].GetCoordinateOrientation(0) > 0); m_Slicer[iSlice]->SetLineTraverseForward( m_DisplayToImageTransform[iSlice].GetCoordinateOrientation(1) > 0); } /** For each slicer, find out which image dimension does is slice along */ template unsigned int ImageWrapper ::GetDisplaySliceImageAxis(unsigned int iSlice) { return m_Slicer[iSlice]->GetSliceDirectionImageAxis(); } template typename ImageWrapper::SliceType* ImageWrapper ::GetSlice(unsigned int dimension) { return m_Slicer[dimension]->GetOutput(); } template TPixel * ImageWrapper ::GetVoxelPointer() const { return m_Image->GetBufferPointer(); } template size_t ImageWrapper ::GetNumberOfVoxels() const { return m_Image->GetBufferedRegion().GetNumberOfPixels(); } template Vector3ui ImageWrapper ::GetSize() const { // Cast the size to our vector format itk::Size<3> size = m_Image->GetLargestPossibleRegion().GetSize(); return Vector3ui( (unsigned int) size[0], (unsigned int) size[1], (unsigned int) size[2]); } template Vector3d ImageWrapper ::TransformVoxelIndexToPosition(const Vector3ui &iVoxel) const { // Use the ITK method to do this typename ImageType::IndexType xIndex; for(size_t d = 0; d < 3; d++) xIndex[d] = iVoxel[d]; itk::Point xPoint; m_Image->TransformIndexToPhysicalPoint(xIndex, xPoint); Vector3d xOut; for(unsigned int q = 0; q < 3; q++) xOut[q] = xPoint[q]; return xOut; } template Vector3d ImageWrapper ::TransformVoxelIndexToNIFTICoordinates(const Vector3d &iVoxel) const { // Create homogeneous vector vnl_vector_fixed x; for(size_t d = 0; d < 3; d++) x[d] = (double) iVoxel[d]; x[3] = 1.0; // Transform to NIFTI coords vnl_vector_fixed p = m_NiftiSform * x; // Return the component return Vector3d(p.data_block()); } template Vector3d ImageWrapper ::TransformNIFTICoordinatesToVoxelIndex(const Vector3d &vNifti) const { // Create homogeneous vector vnl_vector_fixed x; for(size_t d = 0; d < 3; d++) x[d] = (double) vNifti[d]; x[3] = 1.0; // Transform to NIFTI coords vnl_vector_fixed p = m_NiftiInvSform * x; // Return the component return Vector3d(p.data_block()); } template unsigned int ImageWrapper ::ReplaceIntensity(TPixel iOld, TPixel iNew) { // Counter for the number of replaced voxels unsigned int nReplaced = 0; // Replace the voxels for(Iterator it = GetImageIterator(); !it.IsAtEnd(); ++it) if(it.Value() == iOld) { it.Set(iNew); ++nReplaced; } // Flag that changes have been made if(nReplaced > 0) m_Image->Modified(); // Return the number of replacements return nReplaced; } template unsigned int ImageWrapper ::SwapIntensities(TPixel iFirst, TPixel iSecond) { // Counter for the number of replaced voxels unsigned int nReplaced = 0; // Replace the voxels for(Iterator it = GetImageIterator(); !it.IsAtEnd(); ++it) if(it.Value() == iFirst) { it.Set(iSecond); ++nReplaced; } else if(it.Value() == iSecond) { it.Set(iFirst); ++nReplaced; } // Flag that changes have been made if(nReplaced > 0) m_Image->Modified(); // Return the number of replacements return nReplaced; } template void ImageWrapper ::ToggleVisibility() { // If visible (alpha > 0), make invisible if(m_Alpha > 0) { m_ToggleAlpha = m_Alpha; m_Alpha = 0; } // If invisible, return to saved alpha value else { m_Alpha = m_ToggleAlpha; } } #endif // __ImageWrapper_txx_ itksnap/Logic/ImageWrapper/LabelImageWrapper.cxx0000644000076500000240000001024211246333522021233 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LabelImageWrapper.cxx,v $ Language: C++ Date: $Date: 2009/08/29 23:18:42 $ Version: $Revision: 1.7 $ 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 "ImageWrapper.txx" #include "ScalarImageWrapper.txx" #include "LabelImageWrapper.h" #include "ColorLabel.h" #include "ColorLabelTable.h" // Create an instance of ImageWrapper of appropriate type template class ImageWrapper; template class ScalarImageWrapper; LabelImageWrapper ::LabelImageWrapper() { // Instantiate the filters for(unsigned int i=0;i<3;i++) { m_RGBAFilter[i] = RGBAFilterType::New(); m_RGBAFilter[i]->SetInput(m_Slicer[i]->GetOutput()); } SetLabelColorTable(NULL); } LabelImageWrapper ::LabelImageWrapper(const LabelImageWrapper &source) : ScalarImageWrapper(source) { // Instantiate the filters for(unsigned int i=0;i<3;i++) { m_RGBAFilter[i] = RGBAFilterType::New(); m_RGBAFilter[i]->SetInput(m_Slicer[i]->GetOutput()); } // Initialize the color table as well SetLabelColorTable(source.GetLabelColorTable()); } LabelImageWrapper ::~LabelImageWrapper() { for (size_t i = 0; i < 3; ++i) { m_RGBAFilter[i] = NULL; } } ColorLabelTable * LabelImageWrapper ::GetLabelColorTable() const { return m_RGBAFilter[0]->GetColorTable(); } void LabelImageWrapper ::SetLabelColorTable(ColorLabelTable *table) { // Set the new table for(unsigned int i=0;i<3;i++) m_RGBAFilter[i]->SetColorTable(table); } void LabelImageWrapper ::UpdateColorMappingCache() { // Better have a color table assert(GetLabelColorTable()); // Dirty the intensity filters for(unsigned int i=0;i<3;i++) m_RGBAFilter[i]->Modified(); } LabelImageWrapper::DisplaySlicePointer LabelImageWrapper ::GetDisplaySlice(unsigned int dim) const { return m_RGBAFilter[dim]->GetOutput(); } /** * This definition is needed to use RGBA pixels for compilation */ typedef itk::RGBAPixel ColorPixel; namespace itk { template<> class NumericTraits { public: typedef ColorPixel ValueType; typedef ColorPixel PrintType; typedef ColorPixel AbsType; typedef ColorPixel AccumulateType; static const ColorPixel Zero; static const ColorPixel One; static ColorPixel NonpositiveMin() { return Zero; } static bool IsPositive(ColorPixel val) { return true; } static bool IsNonpositive(ColorPixel val) { return false; } static bool IsNegative(ColorPixel val) { return false; } static bool IsNonnegative(ColorPixel val) {return true; } private: static const unsigned char ZeroArray[4]; static const unsigned char OneArray[4]; }; } // End of namespace const unsigned char itk::NumericTraits::ZeroArray[4] = {0,0,0,0}; const ColorPixel itk::NumericTraits::Zero = ColorPixel(itk::NumericTraits::ZeroArray); const unsigned char itk::NumericTraits::OneArray[4] = {1,1,1,1}; const ColorPixel itk::NumericTraits::One = ColorPixel(itk::NumericTraits::OneArray); itksnap/Logic/ImageWrapper/LabelImageWrapper.h0000644000076500000240000001075111245054710020663 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LabelImageWrapper.h,v $ Language: C++ Date: $Date: 2009/08/25 21:38:16 $ 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 __LabelImageWrapper_h_ #define __LabelImageWrapper_h_ #include "ScalarImageWrapper.h" #include "UnaryFunctorCache.h" #include "LabelToRGBAFilter.h" // Forward references namespace itk { template class UnaryFunctorImageFilter; } class ColorLabel; class ColorLabelTable; /** * \class LabelImageWrapper * \brief ImageWrapper for segmentation images in SNAP/IRIS. * * An extension of the ImageWrapper class for dealing with segmentation * images. Using a table of color labels, it is possible to get RGBA * slices from these images. * * \sa ImageWrapper */ class LabelImageWrapper : public ScalarImageWrapper { public: /** * Set the table of color labels used to produce color slice images */ void SetLabelColorTable(ColorLabelTable *labels); /** * Get the color label table */ ColorLabelTable *GetLabelColorTable() const; /** * Tell the object to update it's color mapping cache * TODO: Implement this with ModifiedTime stuff */ void UpdateColorMappingCache(); /** * Get a color slice for display purposes */ DisplaySlicePointer GetDisplaySlice(unsigned int dim) const; /** Constructor initializes mapper */ LabelImageWrapper(); /** Constructor that copies another wrapper */ LabelImageWrapper(const LabelImageWrapper &source); /** Destructor */ ~LabelImageWrapper(); private: /** * Functor used for display caching. This class keeps a pointer to * the table of colors and maps colors to RGBA Pixels */ class IntensityFunctor { public: /** The pointer to the label table */ ColorLabelTable *m_ColorLabelTable; /** The operator that maps label to color */ DisplayPixelType operator()(const LabelType &x) const; // Dummy equality operators, since there is no data here bool operator == (const IntensityFunctor &) const { return true; } bool operator != (const IntensityFunctor &) const { return false; } }; // Type of intensity function used to map 3D volume intensity into // 2D slice intensities // typedef // UnaryFunctorCache CacheType; // typedef itk::SmartPointer CachePointer; // typedef CacheType::CachingFunctor CacheFunctor; // Filter applied to slices typedef itk::Image LabelSliceType; typedef LabelToRGBAFilter RGBAFilterType; typedef RGBAFilterType::Pointer RGBAFilterPointer; RGBAFilterPointer m_RGBAFilter[3]; // typedef // itk::UnaryFunctorImageFilter // IntensityFilterType; // typedef itk::SmartPointer IntensityFilterPointer; /** * An instance of the private intensity mapper (this mapper wraps the passed * in list of labels */ // IntensityFunctor m_IntensityFunctor; /** * A cache used for the intensity mapping function */ // CachePointer m_IntensityMapCache; /** * Filters used to remap the intensity of the slices in this image * into unsigned char images */ // IntensityFilterPointer m_IntensityFilter[3]; }; #endif // __LabelImageWrapper_h_ itksnap/Logic/ImageWrapper/LabelToRGBAFilter.h0000644000076500000240000000677011271346334020477 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: LabelToRGBAFilter.h,v $ Language: C++ Date: $Date: 2009/10/26 16:22:52 $ Version: $Revision: 1.2 $ 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 __LabelToRGBAFilter_h_ #define __LabelToRGBAFilter_h_ #include "SNAPCommon.h" #include "itkImage.h" #include "itkRGBAPixel.h" #include "itkImageToImageFilter.h" #include "ColorLabelTable.h" /** * \class LabelToRGBAFilter * \brief Simple filter that maps label image to RGB color image */ class LabelToRGBAFilter: public itk::ImageToImageFilter< itk::Image , itk::Image,2> > { public: /** Pixel Type of the input image */ typedef LabelType InputPixelType; typedef itk::Image InputImageType; typedef itk::SmartPointer InputImagePointer; /** Pixel Type of the output image */ typedef itk::RGBAPixel OutputPixelType; typedef itk::Image OutputImageType; typedef itk::SmartPointer OutputImagePointer; /** Standard class typedefs. */ typedef LabelToRGBAFilter Self; typedef itk::ImageToImageFilter Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, InputImageType::ImageDimension); /** Set color table macro */ irisSetMacro(ColorTable, ColorLabelTable *); /** Get color table */ irisGetMacro(ColorTable, ColorLabelTable *); protected: void PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << "LabelToRGBAFilter"; } /** Generate Data */ void GenerateData( void ) { // Here's the input and output InputImageType::ConstPointer inputPtr = this->GetInput(); OutputImageType::Pointer outputPtr = this->GetOutput(); // Get the number of pixels in the input size_t n = inputPtr->GetBufferedRegion().GetNumberOfPixels(); // Allocate output if needed if(outputPtr->GetBufferedRegion().GetNumberOfPixels() != n) { outputPtr->SetBufferedRegion(inputPtr->GetBufferedRegion()); outputPtr->Allocate(); } // Get the clear label const ColorLabel &clear = m_ColorTable->GetColorLabel(0); // Simple loop const LabelType *xin = inputPtr->GetBufferPointer(); OutputPixelType *xout = outputPtr->GetBufferPointer(); for(size_t i = 0; i < n; i++) { const ColorLabel &cl = m_ColorTable->GetColorLabel(xin[i]); if(cl.IsVisible()) cl.GetRGBAVector(xout[i].GetDataPointer()); else clear.GetRGBAVector(xout[i].GetDataPointer()); } } private: ColorLabelTable *m_ColorTable; }; #endif itksnap/Logic/ImageWrapper/LevelSetImageWrapper.cxx0000644000076500000240000000755511412166664021762 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LevelSetImageWrapper.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ Version: $Revision: 1.6 $ 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 "LevelSetImageWrapper.h" #include "ImageWrapper.txx" #include "ScalarImageWrapper.txx" #include "ColorLabel.h" // Create an instance of ImageWrapper of appropriate type template class ImageWrapper; template class ScalarImageWrapper; LevelSetImageWrapper::DisplayPixelType LevelSetImageWrapper::MappingFunctor ::operator()(float in) { // Negative values are 'inside' return in <= 0.0f ? m_InsidePixel : m_OutsidePixel; // Use this code to display the level set directly // TODO: Add an option to use this form of display /* LevelSetImageWrapper::DisplayPixelType pixel(m_OutsidePixel); if(in > -4.0 && in < 4.0) { if(in <= 0) { pixel[0] = (unsigned char) (255 + in * 64); pixel[1] = (unsigned char) (255 + in * 64); pixel[2] = 255; } else { pixel[0] = 255; pixel[1] = (unsigned char) (255 - in * 64); pixel[2] = (unsigned char) (255 - in * 64); } pixel[3] = m_InsidePixel[3]; } else if(in <= -4.0) { pixel[0] = 0; pixel[1] = 0; pixel[2] = 255; pixel[3] = 255; } else if(in >= 4.0) { pixel[0] = 255; pixel[1] = 0; pixel[2] = 0; pixel[3] = 255; } return pixel; */ } LevelSetImageWrapper ::LevelSetImageWrapper() : ScalarImageWrapper () { // Intialize display filters for(unsigned int i=0;i<3;i++) { // Create the intensity mapping filter m_DisplayFilter[i] = IntensityFilterType::New(); // Set the corresponding slice as the input image m_DisplayFilter[i]->SetInput(GetSlice(i)); } // Initialize to Edge mode m_MappingFunctor.m_InsidePixel.Fill(0); m_MappingFunctor.m_OutsidePixel.Fill(0); } LevelSetImageWrapper ::~LevelSetImageWrapper() { } LevelSetImageWrapper::DisplaySlicePointer LevelSetImageWrapper ::GetDisplaySlice(unsigned int iSlice) const { // Depending on the current mode, return the display slice or the // original slice from the parent return m_DisplayFilter[iSlice]->GetOutput(); } void LevelSetImageWrapper ::SetColorLabel(const ColorLabel &label) { m_MappingFunctor.m_InsidePixel[0] = label.GetRGB(0); m_MappingFunctor.m_InsidePixel[1] = label.GetRGB(1); m_MappingFunctor.m_InsidePixel[2] = label.GetRGB(2); // Figure out the alpha for display unsigned char alpha = label.IsVisible() ? (label.IsOpaque() ? 255 : label.GetAlpha()) : 0; m_MappingFunctor.m_InsidePixel[3] = alpha; // Intialize display filters for(unsigned int i=0;i<3;i++) { m_DisplayFilter[i]->SetFunctor(m_MappingFunctor); } } itksnap/Logic/ImageWrapper/LevelSetImageWrapper.h0000644000076500000240000000674511245054710021377 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LevelSetImageWrapper.h,v $ Language: C++ Date: $Date: 2009/08/25 21:38:16 $ 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 __LevelSetImageWrapper_h_ #define __LevelSetImageWrapper_h_ #include "ScalarImageWrapper.h" // Forward references to ITK namespace itk { template class UnaryFunctorImageFilter; }; class ColorLabel; /** * \class LevelSetImageWrapper * \brief Image wraper for level set images in SNAP * * The slices generated by this wrapper are processed such that * the interior (negative) regions of the level set image are mapped * to an RGB value and the exterior regions are black. * * \sa ImageWrapper */ class LevelSetImageWrapper : public ScalarImageWrapper { public: /** Set the color label for inside */ void SetColorLabel(const ColorLabel &label); /** * Get the display slice in a given direction. To change the * display slice, call parent's MoveToSlice() method */ DisplaySlicePointer GetDisplaySlice(unsigned int dim) const; /** Constructor initializes mappers */ LevelSetImageWrapper(); /** Destructor */ ~LevelSetImageWrapper(); private: /** * A very simple functor used to map intensities */ class MappingFunctor { public: DisplayPixelType operator()(float in); DisplayPixelType m_InsidePixel; DisplayPixelType m_OutsidePixel; // Dummy equality operators, since there is no data here bool operator == (const MappingFunctor &z) const { return m_InsidePixel == z.m_InsidePixel && m_OutsidePixel == z.m_OutsidePixel; } bool operator != (const MappingFunctor &z) const { return !(*this == z); } }; // Type of the display intensity mapping filter used when the // input is a in-out image typedef itk::UnaryFunctorImageFilter< ImageWrapper::SliceType,DisplaySliceType,MappingFunctor> IntensityFilterType; typedef itk::SmartPointer IntensityFilterPointer; /** * The filters used to remap internal level set image * to a color display image */ IntensityFilterPointer m_DisplayFilter[3]; /** The currently used overlay functor */ MappingFunctor m_MappingFunctor; }; #endif // __LevelSetImageWrapper_h_ itksnap/Logic/ImageWrapper/RGBImageWrapper.cxx0000644000076500000240000000544411412166664020644 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RGBImageWrapper.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ Version: $Revision: 1.7 $ 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 "RGBImageWrapper.h" #include "ImageWrapper.txx" #include "VectorImageWrapper.txx" // Create an instance of ImageWrapper of appropriate type template class ImageWrapper; template class VectorImageWrapper; RGBImageWrapper ::RGBImageWrapper() : VectorImageWrapper () { // Intialize display filters for(unsigned int i=0;i<3;i++) { // Create the intensity mapping filter m_DisplayFilter[i] = IntensityFilterType::New(); // Set the intensity functor m_DisplayFilter[i]->SetFunctor(m_IntensityFunctor); // Set the corresponding slice as the input image m_DisplayFilter[i]->SetInput(GetSlice(i)); } } RGBImageWrapper ::~RGBImageWrapper() { for (size_t i = 0; i < 3; ++i) { m_DisplayFilter[i] = NULL; } } RGBImageWrapper::DisplaySlicePointer RGBImageWrapper ::GetDisplaySlice(unsigned int iSlice) const { // Depending on the current mode, return the display slice or the // original slice from the parent //return m_IsModeInsideOutside ? // m_DisplayFilter[iSlice]->GetOutput() : // GetSlice(iSlice); return m_DisplayFilter[iSlice]->GetOutput(); } RGBImageWrapper::DisplayPixelType RGBImageWrapper::IntensityFunctor ::operator()(const RGBType &x) const { // Create a new pixel DisplayPixelType pixel; pixel[0] = x[0]; pixel[1] = x[1]; pixel[2] = x[2]; if (x[0] != 0 || x[1] != 0 || x[2] != 0) pixel[3] = 255; else pixel[3] = 0; return pixel; } itksnap/Logic/ImageWrapper/RGBImageWrapper.h0000644000076500000240000000503411245054710020254 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: RGBImageWrapper.h,v $ Language: C++ Date: $Date: 2009/08/25 21:38:16 $ Version: $Revision: 1.6 $ 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 __RGBImageWrapper_h_ #define __RGBImageWrapper_h_ #include "VectorImageWrapper.h" // Forward references to ITK namespace itk { template class UnaryFunctorImageFilter; template class ImageSource; }; // Disable 'inheritance by dominance' warining in VC6 #if defined(_WIN32) && defined(_MSC_VER) #pragma warning (disable: 4250) #endif /** * \class RGBImageWrapper * \brief Image wrapper for RGB images in SNAP * * \sa ImageWrapper */ class RGBImageWrapper : public VectorImageWrapper { public: // Basics typedef RGBImageWrapper Self; typedef VectorImageWrapper Superclass; typedef Superclass::ImageType ImageType; /** * Get the display slice in a given direction. To change the * display slice, call parent's MoveToSlice() method */ DisplaySlicePointer GetDisplaySlice(unsigned int dim) const; /** Constructor initializes mappers */ RGBImageWrapper(); /** Destructor */ ~RGBImageWrapper(); private: class IntensityFunctor { public: /** The operator that maps label to color */ DisplayPixelType operator()(const RGBType &x) const; // Equality operators required, if variables defined!!! bool operator == (const IntensityFunctor &z) const { return true; } bool operator != (const IntensityFunctor &z) const { return !(*this == z); } }; // Type of the display intensity mapping filter used when the // input is a in-out image typedef itk::Image RGBSliceType; typedef itk::UnaryFunctorImageFilter< RGBSliceType,DisplaySliceType,IntensityFunctor> IntensityFilterType; typedef itk::SmartPointer IntensityFilterPointer; IntensityFilterPointer m_DisplayFilter[3]; IntensityFunctor m_IntensityFunctor; }; #endif // __RGBImageWrapper_h_ itksnap/Logic/ImageWrapper/ScalarImageWrapper.h0000644000076500000240000001120711136422002021036 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ScalarImageWrapper.h,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. =========================================================================*/ #ifndef __ScalarImageWrapper_h_ #define __ScalarImageWrapper_h_ // Smart pointers have to be included from ITK, can't forward reference them #include "ImageWrapper.h" #include /** * \class ScalarImageWrapper * \brief A wrapper around an itk::Image and related pipelines. * * ScalarImage Wrapper serves as a wrapper around an image pointer, and * is used to unify the treatment of different kinds of scalar images in * SNaP. */ template class ScalarImageWrapper : public ImageWrapper { public: // Basic type definitions typedef itk::OrientedImage ImageType; typedef typename itk::SmartPointer ImagePointer; // Slice image type typedef itk::Image SliceType; typedef typename itk::SmartPointer SlicePointer; // Slicer type typedef IRISSlicer SlicerType; typedef typename itk::SmartPointer SlicerPointer; // MinMax calculator type typedef itk::MinimumMaximumImageCalculator MinMaxCalculatorType; typedef typename itk::SmartPointer MinMaxCalculatorPointer; // Iterator types typedef typename itk::ImageRegionIterator Iterator; typedef typename itk::ImageRegionConstIterator ConstIterator; /** * Default constructor. Creates an image wrapper with a blank internal * image */ ScalarImageWrapper() {}; /** * Copy constructor. Copies the contents of the passed-in image wrapper. */ ScalarImageWrapper(const ScalarImageWrapper ©); /** Destructor */ virtual ~ScalarImageWrapper() {}; /** * Get the minimum intensity value. Call ComputeImageIntensityRange() * first. */ virtual TPixel GetImageMin(); /** * Get the maximum intensity value. Call ComputeImageIntensityRange() * first. */ virtual TPixel GetImageMax(); /** * Get the scaling factor used to convert between intensities stored * in this image and the 'true' image intensities */ virtual double GetImageScaleFactor(); /** * Remap the intensity range of the image to a given range */ virtual void RemapIntensityToRange(TPixel min, TPixel max); /** * Remap the intensity range to max possible range */ virtual void RemapIntensityToMaximumRange(); /** * This method is used to perform a deep copy of a region of this image * into another image, potentially resampling the region to use a different * voxel size */ ImagePointer DeepCopyRegion(const SNAPSegmentationROISettings &roi, itk::Command *progressCommand = NULL) const; protected: /** * The min-max calculator used to hold min/max values. This should be * replaced by a filter, when the latter is more efficient */ MinMaxCalculatorPointer m_MinMaxCalc; /** The intensity scaling factor */ double m_ImageScaleFactor; /** * Compute the intensity range of the image if it's out of date. * This is done before calling GetImateMin, GetImateMax and GetImageScaleFactor methods. */ void CheckImageIntensityRange(); /** * Handle a change in the image pointer (i.e., a load operation on the image or * an initialization operation) */ virtual void UpdateImagePointer(ImageType *); }; #endif // __ScalarImageWrapper_h_ itksnap/Logic/ImageWrapper/ScalarImageWrapper.txx0000644000076500000240000002021311136471735021450 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: ScalarImageWrapper.txx,v $ Language: C++ Date: $Date: 2009/01/24 01:50:21 $ 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. =========================================================================*/ #ifndef __ScalarImageWrapper_txx_ #define __ScalarImageWrapper_txx_ #include "ScalarImageWrapper.h" #include "itkImageRegionIterator.h" #include "itkImageSliceConstIteratorWithIndex.h" #include "itkNumericTraits.h" #include "itkRegionOfInterestImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIdentityTransform.h" #include "itkResampleImageFilter.h" #include "itkNearestNeighborInterpolateImageFunction.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "IRISSlicer.h" #include "SNAPSegmentationROISettings.h" #include "itkCommand.h" #include template ScalarImageWrapper ::ScalarImageWrapper(const ScalarImageWrapper ©) { ImageWrapper::CommonInitialization(); // If the source contains an image, make a copy of that image if (copy.IsInitialized() && copy.GetImage()) { // Create and allocate the image ImagePointer newImage = ImageType::New(); newImage->SetRegions(copy.GetImage()->GetBufferedRegion()); newImage->Allocate(); // Copy the image contents TPixel *ptrTarget = newImage->GetBufferPointer(); TPixel *ptrSource = copy.GetImage()->GetBufferPointer(); memcpy(ptrTarget,ptrSource, sizeof(TPixel) * newImage->GetBufferedRegion().GetNumberOfPixels()); UpdateImagePointer(newImage); } } template void ScalarImageWrapper ::UpdateImagePointer(ImageType *newImage) { // Update the max-min pipeline once we have one setup m_MinMaxCalc = MinMaxCalculatorType::New(); m_MinMaxCalc->SetImage(newImage); ImageWrapper::UpdateImagePointer(newImage); } template typename ScalarImageWrapper::ImagePointer ScalarImageWrapper ::DeepCopyRegion(const SNAPSegmentationROISettings &roi, itk::Command *progressCommand) const { // The filter used to chop off the region of interest typedef itk::RegionOfInterestImageFilter ChopFilterType; typename ChopFilterType::Pointer fltChop = ChopFilterType::New(); // Check if there is a difference in voxel size, i.e., user wants resampling Vector3ul vOldSize(this->m_Image->GetLargestPossibleRegion().GetSize().GetSize()); Vector3d vOldSpacing(this->m_Image->GetSpacing().GetDataPointer()); if(roi.GetResampleFlag()) { // Compute the number of voxels in the output typedef typename itk::ImageRegion<3> RegionType; typedef typename itk::Size<3> SizeType; SizeType vNewSize; RegionType vNewROI; Vector3d vNewSpacing; for(unsigned int i = 0; i < 3; i++) { double scale = roi.GetVoxelScale()[i]; vNewSize.SetElement(i, (unsigned long) (vOldSize[i] / scale)); vNewROI.SetSize(i,(unsigned long) (roi.GetROI().GetSize(i) / scale)); vNewROI.SetIndex(i,(long) (roi.GetROI().GetIndex(i) / scale)); vNewSpacing[i] = scale * vOldSpacing[i]; } // Create a filter for resampling the image typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer fltSample = ResampleFilterType::New(); // Initialize the resampling filter fltSample->SetInput(this->m_Image); fltSample->SetTransform(itk::IdentityTransform::New()); // Typedefs for interpolators typedef itk::NearestNeighborInterpolateImageFunction< ImageType,double> NNInterpolatorType; typedef itk::LinearInterpolateImageFunction< ImageType,double> LinearInterpolatorType; typedef itk::BSplineInterpolateImageFunction< ImageType,double> CubicInterpolatorType; // More typedefs are needed for the sinc interpolator static const unsigned int VRadius = 5; typedef itk::Function::HammingWindowFunction WindowFunction; typedef itk::ConstantBoundaryCondition Condition; typedef itk::WindowedSincInterpolateImageFunction< ImageType, VRadius, WindowFunction, Condition, double> SincInterpolatorType; // Choose the interpolator switch(roi.GetInterpolationMethod()) { case SNAPSegmentationROISettings::NEAREST_NEIGHBOR : fltSample->SetInterpolator(NNInterpolatorType::New()); break; case SNAPSegmentationROISettings::TRILINEAR : fltSample->SetInterpolator(LinearInterpolatorType::New()); break; case SNAPSegmentationROISettings::TRICUBIC : fltSample->SetInterpolator(CubicInterpolatorType::New()); break; case SNAPSegmentationROISettings::SINC_WINDOW_05 : fltSample->SetInterpolator(SincInterpolatorType::New()); break; }; // Set the image sizes and spacing fltSample->SetSize(vNewSize); fltSample->SetOutputSpacing(vNewSpacing.data_block()); fltSample->SetOutputOrigin(this->m_Image->GetOrigin()); fltSample->SetOutputDirection(this->m_Image->GetDirection()); // Set the progress bar if(progressCommand) fltSample->AddObserver(itk::AnyEvent(),progressCommand); // Perform resampling fltSample->GetOutput()->SetRequestedRegion(vNewROI); fltSample->Update(); // Pipe into the chopper fltChop->SetInput(fltSample->GetOutput()); // Update the region of interest fltChop->SetRegionOfInterest(vNewROI); } else { // Pipe image into the chopper fltChop->SetInput(this->m_Image); // Set the region of interest fltChop->SetRegionOfInterest(roi.GetROI()); } // Update the pipeline fltChop->Update(); // Return the resulting image return fltChop->GetOutput(); } template void ScalarImageWrapper ::CheckImageIntensityRange() { // Image should be loaded assert(this->m_Image && m_MinMaxCalc); // Check if the image has been updated since the last time that // the min/max has been computed if (this->m_Image->GetMTime() > m_MinMaxCalc->GetMTime()) { m_MinMaxCalc->Compute(); m_MinMaxCalc->Modified(); m_ImageScaleFactor = 1.0 / (m_MinMaxCalc->GetMaximum() - m_MinMaxCalc->GetMinimum()); } } template TPixel ScalarImageWrapper ::GetImageMin() { // Make sure min/max are up-to-date CheckImageIntensityRange(); // Return the max or min return m_MinMaxCalc->GetMinimum(); } template TPixel ScalarImageWrapper ::GetImageMax() { // Make sure min/max are up-to-date CheckImageIntensityRange(); // Return the max or min return m_MinMaxCalc->GetMaximum(); } template double ScalarImageWrapper ::GetImageScaleFactor() { // Make sure min/max are up-to-date CheckImageIntensityRange(); // Return the max or min return m_ImageScaleFactor; } template void ScalarImageWrapper ::RemapIntensityToRange(TPixel min, TPixel max) { typedef itk::RescaleIntensityImageFilter FilterType; typedef typename FilterType::Pointer FilterPointer; // Create a filter to remap the intensities FilterPointer filter = FilterType::New(); filter->SetInput(this->m_Image); filter->SetOutputMinimum(min); filter->SetOutputMaximum(max); // Run the filter filter->Update(); // Store the output and point everything to it UpdateImagePointer(filter->GetOutput()); } template void ScalarImageWrapper ::RemapIntensityToMaximumRange() { RemapIntensityToRange(itk::NumericTraits::min(), itk::NumericTraits::max()); } #endif // __ScalarImageWrapper_txx_ itksnap/Logic/ImageWrapper/SpeedColorMap.cxx0000644000076500000240000000617210735614372020423 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SpeedColorMap.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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 "SpeedColorMap.h" SpeedColorMap ::SpeedColorMap() { OutputType allBlack[2]; SetColorMap(2, allBlack); } void SpeedColorMap ::SetColorMap(unsigned int n, OutputType *colors) { // Compute colors and color differences m_ColorEntry.resize(n); m_ColorEntryDelta.resize(n-1); for(unsigned int i = 0; i < n; i++) m_ColorEntry[i] = colors[i]; for(unsigned int j = 0; j < n-1; j++) for(unsigned int k = 0; k < 3; k++) m_ColorEntryDelta[j][k] = ((float) colors[j+1][k]) - ((float) colors[j][k]); // Compute scaling factors m_DeltaT = 0.5 * (n - 1.0f); m_Shift = -m_DeltaT; } SpeedColorMap SpeedColorMap ::GetPresetColorMap(ColorMapPreset xPreset) { unsigned char blue[] = {0 , 0 , 255 , 255 }; unsigned char red[] = {255 , 0 , 0 , 255 }; unsigned char white[] = {255 , 255 , 255 , 255 }; unsigned char gray[] = {128 , 128 , 128 , 255 }; unsigned char black[] = {0 , 0 , 0 , 255 }; unsigned char yellow[] = {255 , 255 , 0 , 255 }; SpeedColorMap xMap; switch(xPreset) { case COLORMAP_BLUE_BLACK_WHITE : xMap.SetColorMap( OutputType(blue), OutputType(black), OutputType(white)); break; case COLORMAP_BLACK_GRAY_WHITE : xMap.SetColorMap( OutputType(black), OutputType(gray), OutputType(white)); break; case COLORMAP_BLUE_WHITE_RED : xMap.SetColorMap( OutputType(blue), OutputType(white), OutputType(red)); break; case COLORMAP_BLACK_BLACK_WHITE : xMap.SetColorMap( OutputType(black), OutputType(black), OutputType(white)); break; case COLORMAP_BLACK_YELLOW_WHITE : xMap.SetColorMapPositive( OutputType(black), OutputType(yellow), OutputType(white)); break; } return xMap; } itksnap/Logic/ImageWrapper/SpeedColorMap.h0000644000076500000240000001032510735614372020043 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SpeedColorMap.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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 __SpeedColorMap_h_ #define __SpeedColorMap_h_ #include "itkRGBAPixel.h" #include "GlobalState.h" /** * \class SpeedColorMap * \brief A very simple functor used to map intensities from * the range (-1,1) to RGB color space. */ class SpeedColorMap { public: typedef itk::RGBAPixel OutputType; /** Constructor, sets default color map */ SpeedColorMap(); /** * Mapping operator, maps range [-1, 1] into colors. This operator must * be very fast, because it is called a lot of times in the rendering * pipeline. So we define it inline, and sacrifice generality for speed */ OutputType operator()(float t) { // We do bounds checking, just in case if(t < -0.99999f) return m_ColorEntry.front(); if(t >= 0.99999f) return m_ColorEntry.back(); // Project u into the array of intensities float u = t * m_DeltaT - m_Shift; unsigned int iu = (int) u; float a = u - iu; // Compute the correct color // (1 - a) c[iu] + a c[iu + 1] = c[iu] - a * (c[iu+1] - c[iu]) OutputType p = m_ColorEntry[iu]; p[0] = (unsigned char) (p[0] + a * m_ColorEntryDelta[iu][0]); p[1] = (unsigned char) (p[1] + a * m_ColorEntryDelta[iu][1]); p[2] = (unsigned char) (p[2] + a * m_ColorEntryDelta[iu][2]); // Return the point return p; } bool operator == (const SpeedColorMap &z) { return m_DeltaT == z.m_DeltaT && m_Shift == z.m_Shift && m_ColorMapSize == z.m_ColorMapSize && m_ColorEntry == z.m_ColorEntry; } bool operator != (const SpeedColorMap &z) { return !(*this == z); } /** Set the color map by specifying three points (-1, 0 and 1) */ void SetColorMap(OutputType inMinus, OutputType inZero, OutputType inPlus) { OutputType colors[] = {inMinus, inZero, inPlus}; SetColorMap(3, colors); } /** Set the color map for the positive range only */ void SetColorMapPositive(OutputType inZero, OutputType inHalf, OutputType inOne) { OutputType colors[] = {inZero, inZero, inZero, inHalf, inOne}; SetColorMap(5, colors); } /** Set the color map by specifying a set of n colors between -1 and 1 */ void SetColorMap(unsigned int n, OutputType *colors); /** Generate a color map for one of the presets */ static SpeedColorMap GetPresetColorMap(ColorMapPreset xPreset); bool operator!=( const SpeedColorMap & other ) const { bool value = ( ( this->m_DeltaT != other.m_DeltaT ) || ( this->m_Shift != other.m_Shift ) || ( this->m_ColorMapSize != other.m_ColorMapSize ) ); return value; } private: // The colors in the color map std::vector m_ColorEntry; // Differences between pairs of colors std::vector m_ColorEntryDelta; // Scaling factors used to reference color entries float m_DeltaT; float m_Shift; float m_ColorMapSize; }; #endif itksnap/Logic/ImageWrapper/SpeedImageWrapper.cxx0000644000076500000240000001241311412166664021264 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SpeedImageWrapper.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ 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 "SpeedImageWrapper.h" #include "ImageWrapper.txx" #include "ScalarImageWrapper.txx" SpeedImageWrapper ::SpeedImageWrapper() : ScalarImageWrapper () { // Intialize display filters for(unsigned int i=0;i<3;i++) { // Create the intensity mapping filter m_DisplayFilter[i] = IntensityFilterType::New(); // Set the corresponding slice as the input image m_DisplayFilter[i]->SetInput(GetSlice(i)); // Create the overlay mapping filter m_OverlayFilter[i] = OverlayFilterType::New(); // Set the corresponding slice as the input image m_OverlayFilter[i]->SetInput(GetSlice(i)); } // Initialize the overlay functor m_OverlayFunctor.m_Cutoff = 0.0f; // Initialize to Edge mode m_IsModeInsideOutside = false; } SpeedImageWrapper ::~SpeedImageWrapper() { } SpeedImageWrapper::DisplaySlicePointer SpeedImageWrapper ::GetDisplaySlice(unsigned int iSlice) const { // Depending on the current mode, return the display slice or the // original slice from the parent //return m_IsModeInsideOutside ? // m_DisplayFilter[iSlice]->GetOutput() : // GetSlice(iSlice); return m_DisplayFilter[iSlice]->GetOutput(); } SpeedImageWrapper::OverlaySliceType * SpeedImageWrapper ::GetOverlaySlice(unsigned int dim) { return m_OverlayFilter[dim]->GetOutput(); } void SpeedImageWrapper ::SetOverlayCutoff(float cutoff) { if(cutoff != m_OverlayFunctor.m_Cutoff) { // Update the variable m_OverlayFunctor.m_Cutoff = cutoff; // Update the filters for(unsigned int i=0;i<3;i++) m_OverlayFilter[i]->SetFunctor(m_OverlayFunctor); } } float SpeedImageWrapper ::GetOverlayCutoff() const { return m_OverlayFunctor.m_Cutoff; } void SpeedImageWrapper ::SetOverlayColor(const OverlayPixelType &color) { if(color != m_OverlayFunctor.m_Color) { // Update the variable m_OverlayFunctor.m_Color = color; // Update the filters for(unsigned int i=0;i<3;i++) m_OverlayFilter[i]->SetFunctor(m_OverlayFunctor); } } void SpeedImageWrapper ::UpdateImagePointer(ImageType *newImage) { // Call the parent's method ImageWrapper::UpdateImagePointer(newImage); // Update the sources for(unsigned int i=0;i<3;i++) if(m_PreviewSource[i]) m_Slicer[i]->SetInput(m_PreviewSource[i]); } SpeedImageWrapper::OverlayPixelType SpeedImageWrapper ::GetOverlayColor() const { return m_OverlayFunctor.m_Color; } SpeedImageWrapper::OverlayPixelType SpeedImageWrapper::OverlayFunctor ::operator()(float in) { // Initialize with a clear pixel const unsigned char clear[] = {0,0,0,0}; SpeedImageWrapper::OverlayPixelType rtn(clear); // Check the threshold and return appropriate value if(inSetInput(source); } void SpeedImageWrapper ::RemoveSliceSourcesForPreview() { for(unsigned int i=0;i<3;i++) { m_PreviewSource[i] = NULL; m_Slicer[i]->SetInput(m_Image); } } float SpeedImageWrapper ::GetPreviewVoxel(const Vector3ui &point) const { // Better be in preview mode assert(m_PreviewSource[0] && m_PreviewSource[1] && m_PreviewSource[2]); // Create an index itk::Index<3> index; index[0] = point[0]; index[1] = point[1]; index[2] = point[2]; // Make sure the slice is current m_Slicer[0]->Update(); // Make sure the voxel is in the computed region assert(m_Slicer[0]->GetInput()->GetBufferedRegion().IsInside(index)); // Return the pixel return m_Slicer[0]->GetInput()->GetPixel(index); } void SpeedImageWrapper ::SetColorMap(const SpeedColorMap &xColorMap) { // Store the color map m_ColorMap = xColorMap; // Assign it to the three filters m_DisplayFilter[0]->SetFunctor(m_ColorMap); m_DisplayFilter[1]->SetFunctor(m_ColorMap); m_DisplayFilter[2]->SetFunctor(m_ColorMap); } itksnap/Logic/ImageWrapper/SpeedImageWrapper.h0000644000076500000240000001614511245054710020707 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SpeedImageWrapper.h,v $ Language: C++ Date: $Date: 2009/08/25 21:38:16 $ 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 __SpeedImageWrapper_h_ #define __SpeedImageWrapper_h_ #include "ScalarImageWrapper.h" #include "SpeedImageWrapper.h" #include "SpeedColorMap.h" // Forward references to ITK namespace itk { template class UnaryFunctorImageFilter; template class ImageSource; }; // Disable 'inheritance by dominance' warining in VC6 #if defined(_WIN32) && defined(_MSC_VER) #pragma warning (disable: 4250) #endif /** * \class SpeedImageWrapper * \brief Image wraper for speed images in SNAP * * This wrapper remaps floating point slices to byte slices differently * depending on if it's in InOut snake mode (some speed values are negative) or * in Edge snake mode (speed values are nonnegative). * * \sa ImageWrapper */ class SpeedImageWrapper : public ScalarImageWrapper { public: // Basics typedef SpeedImageWrapper Self; typedef ScalarImageWrapper Superclass; typedef Superclass::ImageType ImageType; // The type definition for the image used to display overlays based on // speed images typedef itk::RGBAPixel OverlayPixelType; typedef itk::Image OverlaySliceType; typedef itk::SmartPointer OverlaySlicePointer; /** * Set the preview source for the slices. This means that the slices * will be generated not from the internal image but from external * images (outputs of some preprocessing filters) */ void SetSliceSourceForPreview(unsigned int slice,ImageType *source); /** * Unset the preview sources for all slices. The slices will be now * generated from the internal image */ void RemoveSliceSourcesForPreview(); /** Get a 'preview' voxel, i.e., a voxel from the previewing slices. For * the results to be valid, the voxel has to be on one of the previewing * slices, and this method is intended for getting the voxel at the * cross-hairs position */ float GetPreviewVoxel(const Vector3ui &point) const; /** * Indicate that this image is a In/Out speed image that has a * range of -1 to +1. */ void SetModeToInsideOutsideSnake() { m_IsModeInsideOutside = true; } /** * Indicate that this image is a Edge speed image that has a * range of 0 to 1. */ void SetModeToEdgeSnake() { m_IsModeInsideOutside = false; } /** * Check if the image is in the Inside/Outside or Edge mode */ bool IsModeInsideOutsideSnake() const { return m_IsModeInsideOutside; } /** * Check if the image is in the Inside/Outside or Edge mode */ bool IsModeEdgeOutsideSnake() const { return !m_IsModeInsideOutside; } /** * Get the display slice in a given direction. To change the * display slice, call parent's MoveToSlice() method */ DisplaySlicePointer GetDisplaySlice(unsigned int dim) const; /** * Get an overlay mask slice for displaying on top of the greylevel * segmentation image. Such slices are used for example to overlay * the thresholding result over the grey slice to give users feedback * of the segmentation */ OverlaySliceType *GetOverlaySlice(unsigned int dim); /** * Set the overlay cutoff range. The intensities above the cutoff will * be included in the overlay */ void SetOverlayCutoff(float cutoff); float GetOverlayCutoff() const; typedef itk::ImageSource PreviewFilterType; typedef itk::SmartPointer PreviewFilterPointer; /** * Set the color for overlay drawing */ void SetOverlayColor(const OverlayPixelType &color); OverlayPixelType GetOverlayColor() const; /** Set the color map to use for generating color output */ void SetColorMap(const SpeedColorMap &xColorMap); /** Constructor initializes mappers */ SpeedImageWrapper(); /** Destructor */ ~SpeedImageWrapper(); protected: /** We override this method in order to maintain the preview sources * when the image gets changed */ void UpdateImagePointer(ImageType *newImage); private: /** * A functor used for overlay mapping */ class OverlayFunctor { public: /** Operator used to map pixels to color */ OverlayPixelType operator()(float in); bool operator == (const OverlayFunctor &z) const { return m_Cutoff == z.m_Cutoff && m_Color == z.m_Color; } bool operator != (const OverlayFunctor &z) const { return !(*this == z); } /** Overlay cutoff */ float m_Cutoff; /** Overlay color */ OverlayPixelType m_Color; }; // Type of the display intensity mapping filter used when the // input is a in-out image typedef itk::UnaryFunctorImageFilter< ImageWrapper::SliceType,DisplaySliceType,SpeedColorMap> IntensityFilterType; typedef itk::SmartPointer IntensityFilterPointer; // A mapping filter used to construct overlay images. This filter assigns // an opaque color to pixels over the cutoff threshold typedef itk::UnaryFunctorImageFilter< ImageWrapper::SliceType,OverlaySliceType,OverlayFunctor> OverlayFilterType; typedef itk::SmartPointer OverlayFilterPointer; /** Whether or not the image is in edge or in-out mode */ bool m_IsModeInsideOutside; /** * The filters used to remap internal speed image that can be * in range of -1 to 1 to a display image in range 0 to 1 */ IntensityFilterPointer m_DisplayFilter[3]; /** * The filters used to create overlay slices */ OverlayFilterPointer m_OverlayFilter[3]; /** The currently used overlay functor */ OverlayFunctor m_OverlayFunctor; /** The currently used color map */ SpeedColorMap m_ColorMap; /** Preview sources */ ImagePointer m_PreviewSource[3]; }; #endif // __SpeedImageWrapper_h_ itksnap/Logic/ImageWrapper/VectorImageWrapper.h0000644000076500000240000000476211136422002021103 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: VectorImageWrapper.h,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ Version: $Revision: 1.2 $ 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 __VectorImageWrapper_h_ #define __VectorImageWrapper_h_ // Smart pointers have to be included from ITK, can't forward reference them #include "ImageWrapper.h" /** * \class VectorImageWrapper * \brief A wrapper around an itk::Image and related pipelines. * * VectorImage Wrapper serves as a wrapper around an image pointer, and * is used to unify the treatment of different kinds of vector images in * SNaP. */ template class VectorImageWrapper : public ImageWrapper { public: // Basic type definitions typedef itk::OrientedImage ImageType; typedef typename itk::SmartPointer ImagePointer; // Slice image type typedef itk::Image SliceType; typedef typename itk::SmartPointer SlicePointer; // Slicer type typedef IRISSlicer SlicerType; typedef typename itk::SmartPointer SlicerPointer; // Iterator types typedef typename itk::ImageRegionIterator Iterator; typedef typename itk::ImageRegionConstIterator ConstIterator; /** * Default constructor. Creates an image wrapper with a blank internal * image */ VectorImageWrapper() : ImageWrapper() {}; /** * Copy constructor. Copies the contents of the passed-in image wrapper. */ VectorImageWrapper(const VectorImageWrapper ©) : ImageWrapper(copy) {}; /** Destructor */ virtual ~VectorImageWrapper() {}; /** * This method is used to perform a deep copy of a region of this image * into another image, potentially resampling the region to use a different * voxel size */ ImagePointer DeepCopyRegion(const SNAPSegmentationROISettings &roi, itk::Command *progressCommand = NULL) const; protected: }; #endif // __VectorImageWrapper_h_ itksnap/Logic/ImageWrapper/VectorImageWrapper.txx0000644000076500000240000000275210631632311021501 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: VectorImageWrapper.txx,v $ Language: C++ Date: $Date: 2007/06/06 22:27:21 $ 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 __VectorImageWrapper_txx_ #define __VectorImageWrapper_txx_ #include "VectorImageWrapper.h" #include "itkImageRegionIterator.h" #include "itkImageSliceConstIteratorWithIndex.h" #include "itkNumericTraits.h" #include "itkRegionOfInterestImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIdentityTransform.h" #include "IRISSlicer.h" #include "SNAPSegmentationROISettings.h" #include "itkCommand.h" #include template typename VectorImageWrapper::ImagePointer VectorImageWrapper ::DeepCopyRegion(const SNAPSegmentationROISettings &roi, itk::Command *progressCommand) const { std::cout << "VectorImageWrapper::DeepCopyRegion" << std::endl; std::cout << std::flush; return NULL; } #endif // __VectorImageWrapper_txx_ itksnap/Logic/LevelSet/0000755000076500000240000000000011560342170014323 5ustar paulystaffitksnap/Logic/LevelSet/CVS/0000755000076500000240000000000011560342170014756 5ustar paulystaffitksnap/Logic/LevelSet/CVS/Entries0000644000076500000240000000144511560342170016316 0ustar paulystaff/LevelSetExtensionFilter.h/1.2/Sun Dec 30 04:05:14 2007// /SNAPAdvectionFieldImageFilter.h/1.3/Fri Jan 23 20:09:38 2009// /SNAPAdvectionFieldImageFilter.txx/1.2/Sun Dec 30 04:05:14 2007// /SNAPLevelSetDriver.cxx/1.2/Mon Jun 28 18:45:08 2010// /SNAPLevelSetDriver.h/1.4/Fri Jan 23 20:09:38 2009// /SNAPLevelSetDriver.txx/1.6/Mon Jun 28 18:45:08 2010// /SNAPLevelSetFunction.h/1.2/Sun Dec 30 04:05:14 2007// /SNAPLevelSetFunction.txx/1.5/Sat Sep 19 14:00:16 2009// /SNAPLevelSetStopAndGoFilter.h/1.3/Fri Oct 24 12:52:08 2008// /SNAPLevelSetStopAndGoFilter.txx/1.2/Sun Dec 30 04:05:14 2007// /SignedDistanceFilter.h/1.2/Sun Dec 30 04:05:14 2007// /SignedDistanceFilter.txx/1.3/Mon Jun 28 18:45:08 2010// /SnakeParameters.cxx/1.3/Tue Oct 19 19:17:00 2010// /SnakeParameters.h/1.2/Sun Dec 30 04:05:15 2007// D itksnap/Logic/LevelSet/CVS/Repository0000644000076500000240000000002711560342170017057 0ustar paulystaffitksnap/Logic/LevelSet itksnap/Logic/LevelSet/CVS/Root0000644000076500000240000000010011560342170015613 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/LevelSet/LevelSetExtensionFilter.h0000644000076500000240000000630410735614372021276 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LevelSetExtensionFilter.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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 __LevelSetExtensionFilter_h_ #define __LevelSetExtensionFilter_h_ #include "itkFiniteDifferenceFunction.h" #include "itkFiniteDifferenceImageFilter.h" #include "itkMutexLock.h" #include "itkConditionVariable.h" #include "itkBarrier.h" #include "itkCommand.h" /** A generic extension of a filter (intended to be a * FiniteDifferenceImageFilter) that let's use control it * in a VCR (play-stop-step-rewind) fashion */ template class LevelSetExtensionFilter : public TFilter { public: /** Standard class typedefs. */ typedef LevelSetExtensionFilter Self; typedef TFilter Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Run-time type information. */ itkTypeMacro(LevelSetExtensionFilter,TFilter); /** Capture information from the superclass. */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::OutputImageType OutputImageType; /** Dimensionality of input and output data is assumed to be the same. * It is inherited from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int,Superclass::ImageDimension); /** ITK new macro */ itkNewMacro(LevelSetExtensionFilter); /** The pixel type of the output image will be used in computations. * Inherited from the superclass. */ typedef typename Superclass::PixelType PixelType; typedef typename Superclass::TimeStepType TimeStepType; protected: LevelSetExtensionFilter() {} virtual ~LevelSetExtensionFilter() {} /** Just a print method */ void PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os,indent); } private: LevelSetExtensionFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented }; #endif // __LevelSetExtensionFilter_h_ itksnap/Logic/LevelSet/SignedDistanceFilter.h0000644000076500000240000001131210735614372020535 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SignedDistanceFilter.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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 __SignedDistanceFilter_h_ #define __SignedDistanceFilter_h_ #include "itkCommand.h" #include "itkProgressAccumulator.h" #include "itkUnaryFunctorImageFilter.h" #include "itkDanielssonDistanceMapImageFilter.h" #include "itkSubtractImageFilter.h" /** * \class SignedDistanceFilter * \brief This filter computes an inside/outside signed distance image * given a binary image of the 'inside' */ template class SignedDistanceFilter: public itk::ImageToImageFilter { public: /** Standard class typedefs. */ typedef SignedDistanceFilter Self; typedef itk::ImageToImageFilter Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Pixel Type of the input image */ typedef TInputImage InputImageType; typedef typename InputImageType::PixelType InputPixelType; typedef itk::SmartPointer InputImagePointer; /** Pixel Type of the output image */ typedef TOutputImage OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; typedef itk::SmartPointer OutputImagePointer; /** Type used for internal calculations */ typedef float RealType; typedef itk::Image InternalImageType; typedef itk::SmartPointer InternalImagePointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); protected: SignedDistanceFilter(); virtual ~SignedDistanceFilter() {}; void PrintSelf(std::ostream& os, itk::Indent indent) const; /** Generate Data */ void GenerateData( void ); private: /** A functor used to invert the input image */ class InvertFunctor { public: InputPixelType operator()(InputPixelType input) { return input == itk::NumericTraits::Zero ? itk::NumericTraits::One : itk::NumericTraits::Zero; } }; // Types used in the internal pipeline typedef itk::UnaryFunctorImageFilter InvertFilterType; typedef itk::DanielssonDistanceMapImageFilter< InputImageType,OutputImageType> DistanceFilterType; typedef itk::SubtractImageFilter SubtractFilterType; // Progress accumulator object typedef itk::ProgressAccumulator::Pointer AccumulatorPointer; // Define the actual filters typename InvertFilterType::Pointer m_InvertFilter; typename DistanceFilterType::Pointer m_InsideDistanceFilter; typename DistanceFilterType::Pointer m_OutsideDistanceFilter; typename SubtractFilterType::Pointer m_SubtractFilter; /** Progress tracking object */ AccumulatorPointer m_ProgressAccumulator; }; #ifndef ITK_MANUAL_INSTANTIATION #include "SignedDistanceFilter.txx" #endif #endif // __SignedDistanceFilter_h_ itksnap/Logic/LevelSet/SignedDistanceFilter.txx0000644000076500000240000000773311412166664021144 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SignedDistanceFilter.txx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ 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. =========================================================================*/ template SignedDistanceFilter ::SignedDistanceFilter() { // Create the inverter m_InvertFilter = InvertFilterType::New(); m_InvertFilter->SetInput(this->GetInput()); m_InvertFilter->ReleaseDataFlagOn(); // Create the exterior distance filter m_OutsideDistanceFilter = DistanceFilterType::New(); m_OutsideDistanceFilter->SetInputIsBinary(true); m_OutsideDistanceFilter->SetInput(this->GetInput()); m_OutsideDistanceFilter->ReleaseDataFlagOn(); // And the interior filter m_InsideDistanceFilter = DistanceFilterType::New(); m_InsideDistanceFilter->SetInput(m_InvertFilter->GetOutput()); m_InsideDistanceFilter->SetInputIsBinary(true); m_InsideDistanceFilter->ReleaseDataFlagOn(); // Filter to subtract the inside from the outside m_SubtractFilter = SubtractFilterType::New(); m_SubtractFilter->SetInput1(m_OutsideDistanceFilter->GetDistanceMap()); m_SubtractFilter->SetInput2(m_InsideDistanceFilter->GetDistanceMap()); // Create the progress accumulator m_ProgressAccumulator = itk::ProgressAccumulator::New(); m_ProgressAccumulator->SetMiniPipelineFilter(this); // Register the filters with the progress accumulator m_ProgressAccumulator->RegisterInternalFilter(m_InvertFilter,0.05f); m_ProgressAccumulator->RegisterInternalFilter(m_InsideDistanceFilter,0.45f); m_ProgressAccumulator->RegisterInternalFilter(m_OutsideDistanceFilter,0.45f); m_ProgressAccumulator->RegisterInternalFilter(m_SubtractFilter,0.05f); } template void SignedDistanceFilter ::GenerateData() { // Get the input and output pointers const typename InputImageType::ConstPointer inputImage = this->GetInput(); typename OutputImageType::Pointer outputImage = this->GetOutput(); // Initialize the progress counter m_ProgressAccumulator->ResetProgress(); // Allocate the output image outputImage->SetBufferedRegion(outputImage->GetRequestedRegion()); outputImage->Allocate(); // Set the inputs m_InvertFilter->SetInput(inputImage); m_OutsideDistanceFilter->SetInput(inputImage); // Call the filter's GenerateData() m_SubtractFilter->GraftOutput(outputImage); m_SubtractFilter->Update(); // graft the mini-pipeline output back onto this filter's output. // this is needed to get the appropriate regions passed back. GraftOutput( m_SubtractFilter->GetOutput() ); } template void SignedDistanceFilter ::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os,indent); } itksnap/Logic/LevelSet/SnakeParameters.cxx0000644000076500000240000000740411457367254020160 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParameters.cxx,v $ Language: C++ Date: $Date: 2010/10/19 19:17: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. =========================================================================*/ #include "SnakeParameters.h" SnakeParameters SnakeParameters ::GetDefaultEdgeParameters() { SnakeParameters p; p.m_AutomaticTimeStep = true; p.m_TimeStepFactor = 1.0f; p.m_Ground = 5.0; p.m_SnakeType = EDGE_SNAKE; p.m_Clamp = true; p.m_PropagationWeight = 1.0; p.m_PropagationSpeedExponent = 1; p.m_CurvatureWeight = 0.2; p.m_CurvatureSpeedExponent = 0; p.m_LaplacianWeight = 0.0f; p.m_LaplacianSpeedExponent = 0; p.m_AdvectionWeight = 0.5; p.m_AdvectionSpeedExponent = 0; p.m_Solver = PARALLEL_SPARSE_FIELD_SOLVER; return p; } SnakeParameters SnakeParameters ::GetDefaultInOutParameters() { SnakeParameters p; p.m_AutomaticTimeStep = true; p.m_TimeStepFactor = 1.0f; p.m_Ground = 5.0; p.m_SnakeType = REGION_SNAKE; p.m_Clamp = true; p.m_PropagationWeight = 1.0; p.m_PropagationSpeedExponent = 1; p.m_CurvatureWeight = 0.2; p.m_CurvatureSpeedExponent = -1; p.m_LaplacianWeight = 0.0f; p.m_LaplacianSpeedExponent = 0; p.m_AdvectionWeight = 0; p.m_AdvectionSpeedExponent = 0; p.m_Solver = PARALLEL_SPARSE_FIELD_SOLVER; return p; } SnakeParameters SnakeParameters ::GetDefaultAllZeroParameters() { SnakeParameters p; p.m_AutomaticTimeStep = true; p.m_TimeStepFactor = 0.1f; p.m_Ground = 5.0; p.m_SnakeType = REGION_SNAKE; p.m_Clamp = true; p.m_PropagationWeight = 0.0; p.m_PropagationSpeedExponent = 0; p.m_CurvatureWeight = 0.0; p.m_CurvatureSpeedExponent = -1; p.m_LaplacianWeight = 0.0f; p.m_LaplacianSpeedExponent = 0; p.m_AdvectionWeight = 0; p.m_AdvectionSpeedExponent = 0; p.m_Solver = PARALLEL_SPARSE_FIELD_SOLVER; return p; } bool SnakeParameters ::operator == (const SnakeParameters &p) const { return( m_AutomaticTimeStep == p.m_AutomaticTimeStep && (m_AutomaticTimeStep || (m_TimeStepFactor == p.m_TimeStepFactor)) && m_Ground == p.m_Ground && m_SnakeType == p.m_SnakeType && m_Clamp == p.m_Clamp && m_PropagationWeight == p.m_PropagationWeight && m_PropagationSpeedExponent == p.m_PropagationSpeedExponent && m_CurvatureWeight == p.m_CurvatureWeight && m_CurvatureSpeedExponent == p.m_CurvatureSpeedExponent && m_LaplacianWeight == p.m_LaplacianWeight && m_LaplacianSpeedExponent == p.m_LaplacianSpeedExponent && m_AdvectionWeight == p.m_AdvectionWeight && m_AdvectionSpeedExponent == p.m_AdvectionSpeedExponent && m_Solver == p.m_Solver); } itksnap/Logic/LevelSet/SnakeParameters.h0000644000076500000240000001122710735614373017576 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParameters.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 __SnakeParameters_h_ #define __SnakeParameters_h_ #include "SNAPCommon.h" /** * \class SnakeParameters * \brief Parameters for the Level Set evolution. * Most of these parameters correspond to the terms in LevelSetFunction. * * \sa itk::LevelSetFunction */ class SnakeParameters { public: virtual ~SnakeParameters() { /*To avoid compiler warning.*/ } enum ConstraintsType { SAPIRO, SCHLEGEL, TURELLO, USER }; enum SnakeType { EDGE_SNAKE, REGION_SNAKE }; enum SolverType { PARALLEL_SPARSE_FIELD_SOLVER, SPARSE_FIELD_SOLVER, NARROW_BAND_SOLVER, LEGACY_SOLVER, DENSE_SOLVER }; /** * Initialize parameters with default values for snake extraction * in Edge images */ static SnakeParameters GetDefaultEdgeParameters(); /** * Initialize parameters with default values for snake extraction * in Inside/Outside images */ static SnakeParameters GetDefaultInOutParameters(); /** * Initialize parameters with default values for snake extraction * in Inside/Outside images */ static SnakeParameters GetDefaultAllZeroParameters(); // Define a comparison operator bool operator ==(const SnakeParameters &p) const; /** Whether we wish to automatically compute optimal time step * in level snake propagation */ irisGetMacro(AutomaticTimeStep,bool); irisSetMacro(AutomaticTimeStep,bool); /** Time step factor in level snake propagation. This is is only used if the * automatic computation is off, and represents the factor by which the auto * time step is multiplied */ irisGetMacro(TimeStepFactor,float); irisSetMacro(TimeStepFactor,float); /** Clamp-to-ground parameter. Obsolete in ITK implementation, kept for backward compatibility and regression testing */ irisGetMacro(Ground,float); irisSetMacro(Ground,float); /** Whether to clamp or not. Obsolete in ITK implementation, kept for backward compatibility and regression testing */ irisGetMacro(Clamp,bool); irisSetMacro(Clamp,bool); /** Which solver to use to run the equation */ irisGetMacro(Solver,SolverType); irisSetMacro(Solver,SolverType); /** Type of equation (well known parameter sets) */ irisGetMacro(SnakeType,SnakeType); irisSetMacro(SnakeType,SnakeType); irisGetMacro(PropagationWeight,float); irisSetMacro(PropagationWeight,float); irisGetMacro(PropagationSpeedExponent,int); irisSetMacro(PropagationSpeedExponent,int); irisGetMacro(CurvatureWeight,float); irisSetMacro(CurvatureWeight,float); irisGetMacro(CurvatureSpeedExponent,int); irisSetMacro(CurvatureSpeedExponent,int); irisGetMacro(LaplacianWeight,float); irisSetMacro(LaplacianWeight,float); irisGetMacro(LaplacianSpeedExponent,int); irisSetMacro(LaplacianSpeedExponent,int); irisGetMacro(AdvectionWeight,float); irisSetMacro(AdvectionWeight,float); irisGetMacro(AdvectionSpeedExponent,int); irisSetMacro(AdvectionSpeedExponent,int); private: float m_TimeStepFactor; float m_Ground; SnakeType m_SnakeType; bool m_Clamp; bool m_AutomaticTimeStep; float m_PropagationWeight; int m_PropagationSpeedExponent; float m_CurvatureWeight; int m_CurvatureSpeedExponent; float m_LaplacianWeight; int m_LaplacianSpeedExponent; float m_AdvectionWeight; int m_AdvectionSpeedExponent; SolverType m_Solver; }; #endif // __SnakeParameters_h_ itksnap/Logic/LevelSet/SNAPAdvectionFieldImageFilter.h0000644000076500000240000000734111136422002022144 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPAdvectionFieldImageFilter.h,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. =========================================================================*/ #ifndef __SNAPAdvectionFieldImageFilter_h_ #define __SNAPAdvectionFieldImageFilter_h_ #include "itkCovariantVector.h" #include "itkOrientedImage.h" #include "itkImageToImageFilter.h" /** * \class SNAPAdvectionFieldImageFilter * \brief A filter used to compute the advection field in the SNAP level set * equation. */ template class SNAPAdvectionFieldImageFilter: public itk::ImageToImageFilter< TInputImage, itk::Image< itk::CovariantVector::ImageDimension>, ::itk::GetImageDimension::ImageDimension> > { public: /** Input image types */ typedef TInputImage InputImageType; typedef itk::SmartPointer InputImagePointer; /** Standard class typedefs. */ typedef SNAPAdvectionFieldImageFilter Self; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); /** Output image types */ typedef itk::CovariantVector< TOutputValueType, itkGetStaticConstMacro(ImageDimension)> VectorType; typedef itk::Image< VectorType, itkGetStaticConstMacro(ImageDimension)> OutputImageType; typedef itk::SmartPointer OutputImagePointer; typedef itk::ImageToImageFilter Superclass; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Set the power of g() by which the gradient is scaled */ itkSetMacro(Exponent,unsigned int); /** Get the power of g() by which the gradient is scaled */ itkGetMacro(Exponent,unsigned int); protected: SNAPAdvectionFieldImageFilter(); virtual ~SNAPAdvectionFieldImageFilter() {}; void PrintSelf(std::ostream& os, itk::Indent indent) const; /** Generate Data */ void GenerateData( void ); private: /** The g-scaling exponent */ unsigned int m_Exponent; }; #ifndef ITK_MANUAL_INSTANTIATION #include "SNAPAdvectionFieldImageFilter.txx" #endif #endif // __SNAPAdvectionFieldImageFilter_h_ itksnap/Logic/LevelSet/SNAPAdvectionFieldImageFilter.txx0000644000076500000240000000654010735614372022561 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPAdvectionFieldImageFilter.txx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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 "itkGradientImageFilter.h" #include "itkMultiplyImageFilter.h" template SNAPAdvectionFieldImageFilter ::SNAPAdvectionFieldImageFilter() { m_Exponent = 0; } template void SNAPAdvectionFieldImageFilter ::GenerateData() { // Get the input and output pointers typename InputImageType::ConstPointer imgInput = this->GetInput(); typename OutputImageType::Pointer imgOutput = this->GetOutput(); // Allocate the output image imgOutput->SetBufferedRegion(imgOutput->GetRequestedRegion()); imgOutput->Allocate(); // Create a new gradient filter typedef itk::GradientImageFilter< InputImageType,TOutputValueType,TOutputValueType> GradientFilter; typename GradientFilter::Pointer fltGradient = GradientFilter::New(); fltGradient->SetInput(imgInput); fltGradient->ReleaseDataFlagOn(); // A pointer to the pipeline tail typename itk::ImageSource::Pointer fltPipeEnd = fltGradient.GetPointer(); // Attach the appropriate number of multiplicative filters typedef itk::MultiplyImageFilter< OutputImageType,InputImageType,OutputImageType> MultiplyFilter; for(unsigned int i=0;iSetInput1(fltPipeEnd->GetOutput()); fltMulti->SetInput2(imgInput); fltMulti->ReleaseDataFlagOn(); fltPipeEnd = fltMulti; } // Call the filter's GenerateData() fltPipeEnd->GraftOutput(imgOutput); fltPipeEnd->Update(); // graft the mini-pipeline output back onto this filter's output. // this is needed to get the appropriate regions passed back. GraftOutput( fltPipeEnd->GetOutput() ); } template void SNAPAdvectionFieldImageFilter ::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os,indent); } itksnap/Logic/LevelSet/SNAPLevelSetDriver.cxx0000644000076500000240000002763311412166664020453 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: SNAPLevelSetDriver.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ Version: $Revision: 1.2 $ 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 "SNAPLevelSetDriver.h" #include "IRISVectorTypesToITKConversion.h" #include "itkCommand.h" #include "itkNarrowBandLevelSetImageFilter.h" #include "itkParallelSparseFieldLevelSetImageFilter.h" #include "itkDenseFiniteDifferenceImageFilter.h" #include "LevelSetExtensionFilter.h" // Disable some windows debug length messages #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #pragma warning ( disable : 4503 ) #endif template SNAPLevelSetDriver ::SNAPLevelSetDriver(FloatImageType *init, FloatImageType *speed, const SnakeParameters &sparms, VectorImageType *externalAdvection) { // Create the level set function m_LevelSetFunction = LevelSetFunctionType::New(); // m_LevelSetFunction = FastSNAPLevelSetFunction::New(); // Pass the speed image to the function m_LevelSetFunction->SetSpeedImage(speed); // Set the external advection if any if(externalAdvection) m_LevelSetFunction->SetAdvectionField(externalAdvection); // Remember the input and output images for later initialization m_InitializationImage = init; // Pass the parameters to the level set function AssignParametersToPhi(sparms,true); // Create the filter DoCreateLevelSetFilter(); } template void SNAPLevelSetDriver ::AssignParametersToPhi(const SnakeParameters &p, bool irisNotUsed(firstTime)) { // Set up the level set function // The sign of the advection term is flipped in our equation m_LevelSetFunction->SetAdvectionWeight( - p.GetAdvectionWeight()); m_LevelSetFunction->SetAdvectionSpeedExponent(p.GetAdvectionSpeedExponent()); // The curvature exponent for traditional/legacy reasons has a +1 value. m_LevelSetFunction->SetCurvatureSpeedExponent(p.GetCurvatureSpeedExponent()+1); m_LevelSetFunction->SetCurvatureWeight(p.GetCurvatureWeight()); m_LevelSetFunction->SetPropagationWeight(p.GetPropagationWeight()); m_LevelSetFunction->SetPropagationSpeedExponent(p.GetPropagationSpeedExponent()); m_LevelSetFunction->SetLaplacianSmoothingWeight(p.GetLaplacianWeight()); m_LevelSetFunction->SetLaplacianSmoothingSpeedExponent(p.GetLaplacianSpeedExponent()); // We only need to recompute the internal images if the exponents to those // images have changed m_LevelSetFunction->CalculateInternalImages(); // Call the initialize method m_LevelSetFunction->Initialize(to_itkSize(Vector3i(1))); // Set the time step m_LevelSetFunction->SetTimeStepFactor( p.GetAutomaticTimeStep() ? 1.0 : p.GetTimeStepFactor()); // Remember the parameters m_Parameters = p; } template void SNAPLevelSetDriver ::DoCreateLevelSetFilter() { // In this method we have the flexibility to create a level set filter // of any ITK solver type. This way, we can plug in different solvers: // NarrowBand, ParallelSparseField, even Dense. if(m_Parameters.GetSolver() == SnakeParameters::PARALLEL_SPARSE_FIELD_SOLVER) { // Define an extension to the appropriate filter class typedef ParallelSparseFieldLevelSetImageFilter< FloatImageType,FloatImageType> LevelSetFilterType; typedef LevelSetExtensionFilter ExtensionFilterType; // Create a new extended filter ExtensionFilterType::Pointer filter = ExtensionFilterType::New(); // Cast this specific filter down to the lowest common denominator that is // a filter m_LevelSetFilter = filter.GetPointer(); // Cast the specific filter to a generic interface, so we can call the // extended operations without knowing exactly what the filter is (this // is the beauty of polymorphism!) m_ExtensionView = filter.GetPointer(); // Perform the special configuration tasks on the filter filter->SetInput(m_InitializationImage); filter->SetNumberOfLayers(3); filter->SetIsoSurfaceValue(0.0f); filter->SetDifferenceFunction(m_LevelSetFunction); } else if(m_Parameters.GetSolver() == SnakeParameters::NARROW_BAND_SOLVER) { // Define an extension to the appropriate filter class typedef NarrowBandLevelSetImageFilter< FloatImageType,FloatImageType> LevelSetFilterType; typedef LevelSetExtensionFilter ExtensionFilterType; // Create a new extended filter ExtensionFilterType::Pointer filter = ExtensionFilterType::New(); // Cast this specific filter down to the lowest common denominator that is // a filter m_LevelSetFilter = filter.GetPointer(); // Cast the specific filter to a generic interface, so we can call the // extended operations without knowing exactly what the filter is (this // is the beauty of polymorphism!) m_ExtensionView = filter.GetPointer(); // Perform the special configuration tasks on the filter filter->SetSegmentationFunction(m_LevelSetFunction); filter->SetInput(m_InitializationImage); filter->SetNarrowBandTotalRadius(5); filter->SetNarrowBandInnerRadius(3); filter->SetFeatureImage(m_LevelSetFunction->GetSpeedImage()); } else if(m_Parameters.GetSolver() == SnakeParameters::DENSE_SOLVER) { // Define an extension to the appropriate filter class typedef DenseFiniteDifferenceImageFilter< FloatImageType,FloatImageType> LevelSetFilterType; typedef LevelSetExtensionFilter ExtensionFilterType; // Create a new extended filter ExtensionFilterType::Pointer filter = ExtensionFilterType::New(); // Cast this specific filter down to the lowest common denominator that is // a filter m_LevelSetFilter = filter.GetPointer(); // Cast the specific filter to a generic interface, so we can call the // extended operations without knowing exactly what the filter is (this // is the beauty of polymorphism!) m_ExtensionView = filter.GetPointer(); // Perform the special configuration tasks on the filter filter->SetInput(m_InitializationImage); filter->SetDifferenceFunction(m_LevelSetFunction); } } template void SNAPLevelSetDriver ::RequestRestart() { // Makes no sense to call this if not in update cycle assert(this->IsInUpdateLoop()); // Request a stop in the update m_ExtensionView->RequestStop(); // Tell the method that called Update that it needs to call DoRestart() m_CommandAfterUpdate = SelfCommandType::New(); m_CommandAfterUpdate->SetCallbackFunction(this,&SNAPLevelSetDriver::DoRestart); } template void SNAPLevelSetDriver ::DoRestart() { // To be on the safe side, just create a new filter (alternative is commented // out below, but seems to be unstable) DoCreateLevelSetFilter(); // Update the image currently in the filter // m_LevelSetFilter->SetInput(m_InitializationImage); // Reset the filter, so the next time it is Updated, it will run again, even // if the input image has not changed // m_LevelSetFilter->Modified(); } template void SNAPLevelSetDriver ::BeginUpdate(Command *pauseCallback) { // This call may not nest assert(!this->IsInUpdateLoop()); // This call loops until the filter returns from an update with no consequent // request specified while(true) { // Tell the filter how many iterations to perform m_ExtensionView->SetIterationsUntilPause(0); // Tell the filter to call back to the command passed in here. If the // command is NULL, the filter will return after executing the iterations m_ExtensionView->SetPauseCommand(pauseCallback); // Clear the post-update command m_CommandAfterUpdate = NULL; // Run the filter (at this point, we're stuck in this method, and rely on // the callback function to handle user interaction) m_LevelSetFilter->UpdateLargestPossibleRegion(); // The control returns here after the filter finishes updating. if(m_CommandAfterUpdate) { // The callback may have set the post-update command pointer, asking us to // execute another action before returning control to the parent m_CommandAfterUpdate->Execute(m_LevelSetFilter,AnyEvent()); } else { // There was no subsequent command requested, hence there was no // reason for aborting Update() other that the user wants us to quit and // return control break; } } } template bool SNAPLevelSetDriver ::IsInUpdateLoop() { return ((m_ExtensionView != NULL) && m_ExtensionView->IsUpdating()); } template void SNAPLevelSetDriver ::RequestEndUpdate() { // Makes no sense to call this if not in update cycle assert(this->IsInUpdateLoop()); // Tell the filter it has to stop m_ExtensionView->RequestStop(); } template void SNAPLevelSetDriver ::RequestIterations(int nIterations) { // This method should only be called once the filter is updating, from the // pause callback assert(this->IsInUpdateLoop()); // Since the filter is already running, so Run is being called from the // pause callback. In this case, we just tell the filter to run for more // iterations once the pause callback returns m_ExtensionView->SetIterationsUntilPause(nIterations); } template typename SNAPLevelSetDriver::FloatImageType * SNAPLevelSetDriver ::GetCurrentState() { // Fix the spacing of the level set filter's output m_LevelSetFilter->GetOutput()->SetSpacing(m_InitializationImage->GetSpacing()); // Return the filter's output return m_LevelSetFilter->GetOutput(); } template void SNAPLevelSetDriver ::CleanUp() { // This method should not be called within the pause callback, or else // we would trash memory assert(!this->IsInUpdateLoop()); // Basically, the filter is finished, and we can finally return // from running the filter. Let's clear the level set and the // function to free memory m_LevelSetFilter = NULL; m_LevelSetFunction = NULL; } template void SNAPLevelSetDriver ::SetSnakeParameters(const SnakeParameters &sparms) { // Parameter setting can be destructive or passive. If the solver has // has changed, then it's destructive, otherwise it's passive bool destructive = sparms.GetSolver() != m_Parameters.GetSolver(); // First of all, pass the parameters to the phi function, which may or // may not cause it to recompute it's images AssignParametersToPhi(sparms,false); if(destructive) { // We need to reinitialize the internal filter. However, if the filter // is already running, all we can do is schedule that update if(this->IsInUpdateLoop()) { // Tell the filter to stop when it regains control from the pause callback m_ExtensionView->RequestStop(); // Schedule a subsequent call to create a new filter m_CommandAfterUpdate = SelfCommandType::New(); m_CommandAfterUpdate->SetCallbackFunction( this,&SNAPLevelSetDriver::DoCreateLevelSetFilter); } else { // We are not in an update loop, but between updates, so just recreate the // level set filter DoCreateLevelSetFilter(); } } } itksnap/Logic/LevelSet/SNAPLevelSetDriver.h0000644000076500000240000001405711136422002020054 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPLevelSetDriver.h,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ 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 __SNAPLevelSetDriver_h_ #define __SNAPLevelSetDriver_h_ #include "SNAPCommon.h" #include "SnakeParameters.h" #include "SNAPLevelSetFunction.h" // #include "SNAPLevelSetStopAndGoFilter.h" template class LevelSetExtensionFilter; class LevelSetExtensionFilterInterface; namespace itk { template class ImageToImageFilter; template class FiniteDifferenceImageFilter; template class SimpleMemberCommand; template class MemberCommand; class Command; }; /** * \class SNAPLevelSetDriverBase * \brief An abstract interface that allows code to be written independently of * the dimensionality of the level set filter. For documentation of the methods, * see SNAPLevelSetDriver. */ class SNAPLevelSetDriverBase { public: virtual ~SNAPLevelSetDriverBase() { /*To avoid compiler warning.*/ } /** Set snake parameters */ virtual void SetSnakeParameters(const SnakeParameters &parms) = 0; /** Run the snake for a number of iterations */ virtual void Run(unsigned int nIterations) = 0; /** Restart the snake */ virtual void Restart() = 0; /** Clean up the snake's state */ virtual void CleanUp() = 0; }; /** * \class SNAPLevelSetDriver * \brief A generic interface between the SNAP application and ITK level set * framework. * * This interface allows the SNAP code to exist independently of the way stop-and-go * level set evolution is implemented in ITK. This gives the software a bit of * modularity. As far as SNAP cares, the public methods declared in this class are * the only ways to control level set evolution. */ template class SNAPLevelSetDriver : public SNAPLevelSetDriverBase { public: typedef SNAPLevelSetDriver Self; // A callback type typedef itk::SmartPointer CommandPointer; typedef itk::SimpleMemberCommand SelfCommandType; typedef itk::SmartPointer SelfCommandPointer; /** Floating point image type used internally */ typedef itk::OrientedImage FloatImageType; typedef typename itk::SmartPointer FloatImagePointer; /** Type definition for the level set function */ typedef SNAPLevelSetFunction LevelSetFunctionType; typedef typename LevelSetFunctionType::VectorImageType VectorImageType; /** Initialize the level set driver. Note that the type of snake (in/out * or edge) is determined entirely by the speed image and by the values * of the parameters. Moreover, the type of solver used is specified in * the parameters as well. The last parameter is the optional external * advection field, that can be used instead of the default advection * field that is based on the image gradient */ SNAPLevelSetDriver(FloatImageType *initialLevelSet, FloatImageType *speed, const SnakeParameters &parms, VectorImageType *externalAdvection = NULL); /** Virtual destructor */ virtual ~SNAPLevelSetDriver() {} /** Set snake parameters */ void SetSnakeParameters(const SnakeParameters &parms); /** Run the filter */ void Run(unsigned int nIterations); /** Restart the snake */ void Restart(); /** Get the level set function */ irisGetMacro(LevelSetFunction,LevelSetFunctionType *); /** Get the current state of the snake (level set and narrow band) */ FloatImageType *GetCurrentState(); /** Get the number of elapsed iterations */ unsigned int GetElapsedIterations() const; /** Clean up the snake's state */ void CleanUp(); private: /** An internal class used to invert an image */ class InvertFunctor { public: unsigned char operator()(unsigned char input) { return input == 0 ? 1 : 0; } }; /** Type definition for the level set filter */ typedef itk::FiniteDifferenceImageFilter FilterType; /** Level set filter wrapped by this object */ typename FilterType::Pointer m_LevelSetFilter; /** Level set function used by the level set filter */ typename LevelSetFunctionType::Pointer m_LevelSetFunction; /** An initialization image */ FloatImagePointer m_InitializationImage; /** Last accepted snake parameters */ SnakeParameters m_Parameters; /** Assign the values of snake parameters to a snake function */ void AssignParametersToPhi(const SnakeParameters &parms, bool firstTime); /** Internal routines */ void DoCreateLevelSetFilter(); }; // Type definitions typedef SNAPLevelSetDriver<3> SNAPLevelSetDriver3d; typedef SNAPLevelSetDriver<2> SNAPLevelSetDriver2d; #ifndef ITK_MANUAL_INSTANTIATION #include "SNAPLevelSetDriver.txx" #endif #endif // __SNAPLevelSetDriver_h_ itksnap/Logic/LevelSet/SNAPLevelSetDriver.txx0000644000076500000240000002452611412166664020472 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPLevelSetDriver.txx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ Version: $Revision: 1.6 $ 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 __SNAPLevelSetDriver_txx_ #define __SNAPLevelSetDriver_txx_ // Borland compiler is very lazy so we need to instantiate the template // by hand //#if defined(__BORLANDC__) //#include <../../../SNAPBorlandDummyTypes.h> //#endif #include "SNAPLevelSetDriver.h" #include "IRISVectorTypesToITKConversion.h" #include "itkCommand.h" #include "itkNarrowBandLevelSetImageFilter.h" #include "itkDenseFiniteDifferenceImageFilter.h" #include "LevelSetExtensionFilter.h" #if defined(USE_ITK36_ITK38_SPARSEFIELD_BUGFIX) #include "itkParallelSparseFieldLevelSetImageFilterBugFix.h" #else #include "itkSparseFieldLevelSetImageFilter.h" #endif // Disable some windows debug length messages #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #pragma warning ( disable : 4503 ) #endif // Create an inverting functor class InvertFunctor { public: unsigned char operator()(unsigned char input) { return input == 0 ? 1 : 0; } }; template SNAPLevelSetDriver ::SNAPLevelSetDriver(FloatImageType *init, FloatImageType *speed, const SnakeParameters &sparms, VectorImageType *externalAdvection) { // Create the level set function m_LevelSetFunction = LevelSetFunctionType::New(); // Pass the speed image to the function m_LevelSetFunction->SetSpeedImage(speed); // Set the external advection if any if(externalAdvection) m_LevelSetFunction->SetAdvectionField(externalAdvection); // Remember the input and output images for later initialization m_InitializationImage = init; // Pass the parameters to the level set function AssignParametersToPhi(sparms,true); // Create the filter DoCreateLevelSetFilter(); } template void SNAPLevelSetDriver ::AssignParametersToPhi(const SnakeParameters &p, bool irisNotUsed(firstTime)) { // Set up the level set function // The sign of the advection term is flipped in our equation m_LevelSetFunction->SetAdvectionWeight( - p.GetAdvectionWeight()); m_LevelSetFunction->SetAdvectionSpeedExponent(p.GetAdvectionSpeedExponent()); // The curvature exponent for traditional/legacy reasons has a +1 value. m_LevelSetFunction->SetCurvatureSpeedExponent(p.GetCurvatureSpeedExponent()+1); m_LevelSetFunction->SetCurvatureWeight(p.GetCurvatureWeight()); m_LevelSetFunction->SetPropagationWeight(p.GetPropagationWeight()); m_LevelSetFunction->SetPropagationSpeedExponent(p.GetPropagationSpeedExponent()); m_LevelSetFunction->SetLaplacianSmoothingWeight(p.GetLaplacianWeight()); m_LevelSetFunction->SetLaplacianSmoothingSpeedExponent(p.GetLaplacianSpeedExponent()); // We only need to recompute the internal images if the exponents to those // images have changed m_LevelSetFunction->CalculateInternalImages(); // Call the initialize method typename LevelSetFunctionType::RadiusType radius; radius.Fill(1); m_LevelSetFunction->Initialize(radius); // Set the time step m_LevelSetFunction->SetTimeStepFactor( p.GetAutomaticTimeStep() ? 1.0 : p.GetTimeStepFactor()); // Remember the parameters m_Parameters = p; } template void SNAPLevelSetDriver ::DoCreateLevelSetFilter() { // In this method we have the flexibility to create a level set filter // of any ITK solver type. This way, we can plug in different solvers: // NarrowBand, ParallelSparseField, even Dense. if(m_Parameters.GetSolver() == SnakeParameters::PARALLEL_SPARSE_FIELD_SOLVER) { // Define an extension to the appropriate filter class #if defined(USE_ITK36_ITK38_SPARSEFIELD_BUGFIX) typedef itk::ParallelSparseFieldLevelSetImageFilterBugFix< FloatImageType, FloatImageType> LevelSetFilterType; #else typedef itk::SparseFieldLevelSetImageFilter< FloatImageType, FloatImageType> LevelSetFilterType; #endif typedef typename LevelSetFilterType::Pointer LevelSetFilterPointer; LevelSetFilterPointer filter = LevelSetFilterType::New(); // Cast this specific filter down to the lowest common denominator that is // a filter m_LevelSetFilter = filter.GetPointer(); // Perform the special configuration tasks on the filter filter->SetInput(m_InitializationImage); filter->SetNumberOfLayers(3); filter->SetIsoSurfaceValue(0.0f); filter->SetDifferenceFunction(m_LevelSetFunction); } else if(m_Parameters.GetSolver() == SnakeParameters::NARROW_BAND_SOLVER) { // Define an extension to the appropriate filter class typedef itk::NarrowBandLevelSetImageFilter< FloatImageType,FloatImageType,float,FloatImageType> LevelSetFilterType; typedef LevelSetExtensionFilter ExtensionFilter; typename ExtensionFilter::Pointer filter = ExtensionFilter::New(); // Cast this specific filter down to the lowest common denominator that is // a filter m_LevelSetFilter = filter.GetPointer(); // Perform the special configuration tasks on the filter filter->SetSegmentationFunction(m_LevelSetFunction); filter->SetInput(m_InitializationImage); filter->SetNarrowBandTotalRadius(5); filter->SetNarrowBandInnerRadius(3); filter->SetFeatureImage(m_LevelSetFunction->GetSpeedImage()); } else if(m_Parameters.GetSolver() == SnakeParameters::DENSE_SOLVER) { // Define an extension to the appropriate filter class typedef itk::DenseFiniteDifferenceImageFilter< FloatImageType,FloatImageType> LevelSetFilterType; typedef LevelSetExtensionFilter ExtensionFilter; typename ExtensionFilter::Pointer filter = ExtensionFilter::New(); // Cast this specific filter down to the lowest common denominator that is // a filter m_LevelSetFilter = filter.GetPointer(); // Perform the special configuration tasks on the filter filter->SetInput(m_InitializationImage); filter->SetDifferenceFunction(m_LevelSetFunction); } else { throw itk::ExceptionObject(__FILE__,__LINE__,"Unknown level set solver requested"); } // This code is common to all filters. It causes the filter to initialize // the necessary memory and sets the iteration counter to 0 m_LevelSetFilter->SetManualReinitialization(true); m_LevelSetFilter->SetNumberOfIterations(0); // Update the largest possible region. The slicer may be changing the // requested region on this image, so it's important that we always // update the entire image m_LevelSetFilter->UpdateLargestPossibleRegion(); } template void SNAPLevelSetDriver ::Restart() { // Tell the filter to reinitialize next time that an update will // be performed, and set the number of iterations to 0 m_LevelSetFilter->SetStateToUninitialized(); m_LevelSetFilter->SetNumberOfIterations(0); // Update the largest possible region. The slicer may be changing the // requested region on this image, so it's important that we always // update the entire image m_LevelSetFilter->UpdateLargestPossibleRegion(); } template void SNAPLevelSetDriver ::Run(unsigned int nIterations) { // Increment the number of iterations unsigned int nElapsed = m_LevelSetFilter->GetElapsedIterations(); m_LevelSetFilter->SetNumberOfIterations(nElapsed + nIterations); // Update the largest possible region. The slicer may be changing the // requested region on this image, so it's important that we always // update the entire image m_LevelSetFilter->UpdateLargestPossibleRegion(); } template typename SNAPLevelSetDriver::FloatImageType * SNAPLevelSetDriver ::GetCurrentState() { // Fix the spacing of the level set filter's output (huh?) m_LevelSetFilter->GetOutput()->SetDirection(m_InitializationImage->GetDirection()); m_LevelSetFilter->GetOutput()->SetSpacing(m_InitializationImage->GetSpacing()); m_LevelSetFilter->GetOutput()->SetOrigin(m_InitializationImage->GetOrigin()); // Return the filter's output return m_LevelSetFilter->GetOutput(); } template unsigned int SNAPLevelSetDriver ::GetElapsedIterations() const { return m_LevelSetFilter->GetElapsedIterations(); } template void SNAPLevelSetDriver ::CleanUp() { // Basically, the filter is finished, and we can finally return // from running the filter. Let's clear the level set and the // function to free memory m_LevelSetFilter = NULL; m_LevelSetFunction = NULL; } template void SNAPLevelSetDriver ::SetSnakeParameters(const SnakeParameters &sparms) { // Parameter setting can be destructive or passive. If the solver has // has changed, then it's destructive, otherwise it's passive bool destructive = sparms.GetSolver() != m_Parameters.GetSolver(); // First of all, pass the parameters to the phi function, which may or // may not cause it to recompute it's images AssignParametersToPhi(sparms,false); // Create a new level set filter if(destructive) { DoCreateLevelSetFilter(); } } #endif itksnap/Logic/LevelSet/SNAPLevelSetFunction.h0000644000076500000240000003143010735614372020421 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPLevelSetFunction.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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 __SNAPLevelSetFunction_h_ #define __SNAPLevelSetFunction_h_ //#include "itkLevelSetFunction.h" #include "itkSegmentationLevelSetFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkVectorLinearInterpolateImageFunction.h" #include "itkVectorCastImageFilter.h" // #include "itkGradientImageFilter.h" #include "SNAPAdvectionFieldImageFilter.h" /** \class SNAPLevelSetFunction \brief A level set function that implements the generic level set equation decribed in [Ho et al., 2003] and used by the SnAP Application. This class defines a level set equation that is similar to the Geodesic Active Contours implementation (see GeodesicActiveContoursLevelSetFunction). However, it includes an additional Laplacian smoothing (i.e., diffusion) term and it modulates each of the terms by a speed function, which is an integer power of the speed function passed in by the user. Unlike the SegmentationLevelSetFunction, this class requires that the user pass in a speed function \f$ g({\mathbb x}) \f$. This is done because in the SnAP application the computation of the speed function is supervised by the user. The equation implemented by this function is of the form \f[ \phi_t = A g^a |\nabla \phi| + B g^b \nabla g \cdot \nabla \phi + C g^c \kappa \nabla | \phi | + D g^d \nabla^2 \phi \ . \f The right-hand-side consists, in order, of the propagation term, the advection term, the curvature term and the Laplacian smoothing term. Each of these terms has a constant weight associated with it. Each term is scaled by a power of the speed function \f$ g({\mathbb x}) \f$. Often the constants \f$ a, b, c, d \f$ are equal to 0, in which case there is no modulation of the terms by the speed function. Modulation helps slow down the snake at edges in the image. Before passing an instance of this class to a FiniteDifferenceImageFilter, set the weights and speed exponents for each of the four terms using the SetXXXWeight() and SetXXXSpeedExponent() methods. Then pass in a speed image using SetSpeedImage() and then compute the internal images by calling CalculateInternalImages(). */ template class ITK_EXPORT SNAPLevelSetFunction : public itk::SegmentationLevelSetFunction { public: /** Standard class typedefs. */ typedef SNAPLevelSetFunction Self; typedef itk::SegmentationLevelSetFunction Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods) */ itkTypeMacro( SNAPLevelSetFunction, itk::LevelSetFunction ); /** Extract some parameters from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int, Superclass::ImageDimension); /** Extract some parameters from the superclass. */ typedef typename Superclass::ImageType ImageType; typedef typename ImageType::Pointer ImagePointer; typedef typename Superclass::NeighborhoodType NeighborhoodType; typedef typename Superclass::ScalarValueType ScalarValueType; typedef typename Superclass::RadiusType RadiusType; typedef typename Superclass::FloatOffsetType FloatOffsetType; typedef typename Superclass::VectorType VectorType; typedef itk::Image VectorImageType; typedef typename VectorImageType::Pointer VectorImagePointer; typedef typename Superclass::TimeStepType TimeStepType; typedef typename Superclass::GlobalDataStruct GlobalDataStruct; /** Interpolators used to access the speed images */ typedef itk::LinearInterpolateImageFunction< ImageType> ImageInterpolatorType; typedef itk::VectorLinearInterpolateImageFunction< VectorImageType,float> VectorInterpolatorType; typedef typename ImageType::IndexType IndexType; typedef typename ImageInterpolatorType::ContinuousIndexType ContinuousIndexType; /** Set the speed image (a.k.a. function g()) on which the computation of the variuous internal speed images is based. The function g() should be near zero at edges of structures in the image and near one at flat regions */ virtual void SetSpeedImage(ImageType *pointer); /** Get the speed image g() */ virtual ImageType *GetSpeedImage() { return m_SpeedImage; } /** Set the external advection image (optional). This is only * needed if your advection image is not the gradient of the * speed image. We use this for DTI segmentation */ virtual void SetAdvectionField(VectorImageType *pointer) { m_UseExternalAdvectionField = true; m_AdvectionField = pointer; } /** Compute speed and advection images from feature image. */ virtual void CalculateInternalImages(); /** Local multiplier for the curvature term */ virtual ScalarValueType CurvatureSpeed( const NeighborhoodType &neighbourhood, const FloatOffsetType &offset, GlobalDataStruct * = 0 ) const; /** Local multiplier for the laplacian smoothing term */ virtual ScalarValueType LaplacianSmoothingSpeed( const NeighborhoodType &neighbourhood, const FloatOffsetType &offset, GlobalDataStruct * = 0 ) const; /** Local multiplier for the propagation term */ virtual ScalarValueType PropagationSpeed( const NeighborhoodType &neighbourhood, const FloatOffsetType &offset, GlobalDataStruct * = 0 ) const; /** Local multiplier for the advection term */ virtual VectorType AdvectionField( const NeighborhoodType &neighbourhood, const FloatOffsetType &offset, GlobalDataStruct * = 0 ) const; /** Set the exponent to which the speed image g() is taken when converted to the curvature speed */ void SetCurvatureSpeedExponent(int exponent) { m_CurvatureSpeedExponent = exponent; } /** Get the exponent to which the speed image g() is taken when converted to the curvature speed */ int GetCurvatureSpeedExponent() const { return m_CurvatureSpeedExponent; } /** Set the exponent to which the speed image g() is taken when computing the advection field */ void SetAdvectionSpeedExponent(int exponent) { m_AdvectionSpeedExponent = exponent; } /** Get the exponent to which the speed image g() is taken when computing the advection field */ int GetAdvectionSpeedExponent() const { return m_AdvectionSpeedExponent; } /** Set the exponent to which the speed image g() is taken when converted to the propagation speed */ void SetPropagationSpeedExponent(int exponent) { m_PropagationSpeedExponent = exponent; } /** Get the exponent to which the speed image g() is taken when converted to the propagation speed */ int GetPropagationSpeedExponent() const { return m_PropagationSpeedExponent; } /** Set the exponent to which the speed image g() is taken when converted to the laplacian smoothing speed */ void SetLaplacianSmoothingSpeedExponent(int exponent) { m_LaplacianSmoothingSpeedExponent = exponent; } /** Get the exponent to which the speed image g() is taken when converted to the laplacian smoothing speed */ int GetLaplacianSmoothingSpeedExponent() const { return m_LaplacianSmoothingSpeedExponent; } /** Set/Get the time step. For this filter the time-step is supplied by the user and remains fixed for all updates. */ void SetTimeStepFactor(const TimeStepType &t) { m_TimeStepFactor = t; } const TimeStepType &GetTimeStepFactor() const { return m_TimeStepFactor; } /** Returns the time step supplied by the user. If the time step value passed on to this filter is equal to zero, this method will use the automatic time step calculation from the parent class. If the value is non-zero, the fixed time step will be returned. */ virtual TimeStepType ComputeGlobalTimeStep(void *GlobalData) const { return m_TimeStepFactor == 0 ? Superclass::ComputeGlobalTimeStep(GlobalData) : m_TimeStepFactor * Superclass::ComputeGlobalTimeStep(GlobalData); } protected: SNAPLevelSetFunction(); ~SNAPLevelSetFunction(); void PrintSelf(std::ostream &s, itk::Indent indent) const; private: /** The exponent to which speed image g() is taken to compute the curvature speed */ int m_CurvatureSpeedExponent; /** The exponent to which speed image g() is taken to compute the advection field */ int m_AdvectionSpeedExponent; /** The exponent to which speed image g() is taken to compute the propagation speed */ int m_PropagationSpeedExponent; /** The exponent to which speed image g() is taken to compute the Laplacian smoothing speed */ int m_LaplacianSmoothingSpeedExponent; /** The speed image g() computed externally with user interaction */ ImagePointer m_SpeedImage; /** The curvature image derived from the speed image g() */ ImagePointer m_CurvatureSpeedImage; /** The propagation speed derived from the speed image g() */ ImagePointer m_PropagationSpeedImage; /** The smoothing speed derived from the speed image g() */ ImagePointer m_LaplacianSmoothingSpeedImage; /** The advection field (possibly scaled by speed image g() */ VectorImagePointer m_AdvectionField; /** Flag, specifyting that the advection image is loaded externally */ bool m_UseExternalAdvectionField; /** Gradient filter used to produce the advection field */ typedef SNAPAdvectionFieldImageFilter AdvectionFilterType; typename AdvectionFilterType::Pointer m_AdvectionFilter; /** Instances of the interpolators */ typename ImageInterpolatorType::Pointer m_PropagationSpeedInterpolator; typename ImageInterpolatorType::Pointer m_CurvatureSpeedInterpolator; typename ImageInterpolatorType::Pointer m_LaplacianSmoothingSpeedInterpolator; typename VectorInterpolatorType::Pointer m_AdvectionFieldInterpolator; /** The constant time step */ TimeStepType m_TimeStepFactor; /** A trivial functor to square the g() image */ class SquareFunctor { public: inline ScalarValueType operator()(ScalarValueType x) { return x * x; } // Dummy equality operators, since there is no data here bool operator == (const SquareFunctor &) const { return true; } bool operator != (const SquareFunctor &) const { return false; } }; /** A trivial functor to take the g() image to any power */ class PowFunctor { public: inline ScalarValueType operator()(ScalarValueType x) { return vcl_pow(x,power); } // Dummy equality operators, since there is no data here bool operator == (const PowFunctor &z) const { return power == z.power; } bool operator != (const PowFunctor &z) const { return !(*this == z); } int power; }; /** A casting functor to convert between vector types. */ itk::Functor::VectorCast< ITK_TYPENAME VectorInterpolatorType::OutputType, VectorType > m_VectorCast; }; #ifndef ITK_MANUAL_INSTANTIATION #include "SNAPLevelSetFunction.txx" #endif #endif itksnap/Logic/LevelSet/SNAPLevelSetFunction.txx0000644000076500000240000002500311255161760021010 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPLevelSetFunction.txx,v $ Language: C++ Date: $Date: 2009/09/19 14:00:16 $ 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 "itkGradientImageFilter.h" #include "itkUnaryFunctorImageFilter.h" #include "itkMultiplyImageFilter.h" #include "itkImageRegionIterator.h" #include "itkNumericTraits.h" #include template SNAPLevelSetFunction ::SNAPLevelSetFunction() : Superclass() { m_TimeStepFactor = 1.0f; m_CurvatureSpeedExponent = 0; m_AdvectionSpeedExponent = 0; m_PropagationSpeedExponent = 0; m_LaplacianSmoothingSpeedExponent = 0; m_UseExternalAdvectionField = false; m_PropagationSpeedInterpolator = ImageInterpolatorType::New(); m_CurvatureSpeedInterpolator = ImageInterpolatorType::New(); m_LaplacianSmoothingSpeedInterpolator = ImageInterpolatorType::New(); m_AdvectionFieldInterpolator = VectorInterpolatorType::New(); m_AdvectionFilter = AdvectionFilterType::New(); } template SNAPLevelSetFunction ::~SNAPLevelSetFunction() { } template void SNAPLevelSetFunction ::SetSpeedImage(ImageType *pointer) { m_SpeedImage = pointer; m_AdvectionFilter->SetInput(m_SpeedImage); } /* template void SNAPLevelSetFunction ::SetAdvectionField(VectorImageType *pointer) { m_UseExternalAdvectionField = true; m_AdvectionField = pointer; } */ template void SNAPLevelSetFunction ::CalculateInternalImages() { // Create a map of integers to image pointers. This map will cache the // different powers of g() that must be computed (hopefully none!) typedef std::map PowerMapType; PowerMapType powerMap; // Initialize the map for the default powers powerMap[0] = NULL; powerMap[1] = m_SpeedImage; // Create a list of the required powers int powers[4] = { this->GetPropagationSpeedExponent(), this->GetAdvectionSpeedExponent(), this->GetCurvatureSpeedExponent(), this->GetLaplacianSmoothingSpeedExponent() }; // What equation are we solving? // std::cout << "Solving Equation : = " << std::endl; // std::cout << " P-W = " << this->GetPropagationWeight() << std::endl; // std::cout << " P-E = " << this->GetPropagationSpeedExponent() << std::endl; // std::cout << " C-W = " << this->GetCurvatureWeight() << std::endl; // std::cout << " C-E = " << this->GetCurvatureSpeedExponent() << std::endl; // std::cout << " A-W = " << this->GetAdvectionWeight() << std::endl; // std::cout << " A-E = " << this->GetAdvectionSpeedExponent() << std::endl; // std::cout << " L-W = " << this->GetLaplacianSmoothingWeight() << std::endl; // std::cout << " L-E = " << this->GetLaplacianSmoothingSpeedExponent() << std::endl; // Create an image for each of these powers, if needed for(unsigned int iPower=0; iPower < 4; iPower++) { // For powers of 0 and 1, which are by far the most common, there is // nothing to compute if(powers[iPower] == 0 || powers[iPower] == 1) { continue; } // For power 2, we square the speed image. Since pow() is a dog, // let's handle this case explicitly else if(powers[iPower] == 2) { // Create a filter that will square the image typedef itk::UnaryFunctorImageFilter ExponentFilterType; // Run the filter typename ExponentFilterType::Pointer filter = ExponentFilterType::New(); filter->SetInput(m_SpeedImage); filter->Update(); // Stick the filter's output into the map powerMap[2] = filter->GetOutput(); } // For powers other than 3, let the user suffer through the pow()! else { // Create a filter that will square the image typedef itk::UnaryFunctorImageFilter ExponentFilterType; typename ExponentFilterType::Pointer filter = ExponentFilterType::New(); filter->SetInput(m_SpeedImage); // Create a functor with specified power PowFunctor functor; functor.power = powers[iPower]; filter->SetFunctor(functor); // Run the filter filter->Update(); // Stick the filter's output into the map powerMap[powers[iPower]] = filter->GetOutput(); } } // For all powers // Now that we have the powers, we can assign the speed images m_PropagationSpeedImage = powerMap[m_PropagationSpeedExponent]; m_CurvatureSpeedImage = powerMap[m_CurvatureSpeedExponent]; m_LaplacianSmoothingSpeedImage = powerMap[m_LaplacianSmoothingSpeedExponent]; // There is still the business of the advection image to attend to // Compute \f$ \nabla g() \f$ (will be cached from run to run) if(!m_UseExternalAdvectionField) { assert(m_AdvectionSpeedExponent >= 0); m_AdvectionFilter->SetExponent((unsigned int)m_AdvectionSpeedExponent); m_AdvectionFilter->Update(); m_AdvectionField = reinterpret_cast< VectorImageType* > ( m_AdvectionFilter->GetOutput()); } // Set up the image interpolators to point to the generated images if(m_PropagationSpeedExponent != 0) m_PropagationSpeedInterpolator->SetInputImage(m_PropagationSpeedImage); if(m_CurvatureSpeedExponent != 0) m_CurvatureSpeedInterpolator->SetInputImage(m_CurvatureSpeedImage); if(m_LaplacianSmoothingSpeedExponent != 0) m_LaplacianSmoothingSpeedInterpolator->SetInputImage( m_LaplacianSmoothingSpeedImage); // Set up the advection interpolator // if(m_AdvectionSpeedExponent != 0) m_AdvectionFieldInterpolator->SetInputImage(m_AdvectionField); } template typename SNAPLevelSetFunction::ScalarValueType SNAPLevelSetFunction ::CurvatureSpeed(const NeighborhoodType &neighborhood, const FloatOffsetType &offset, GlobalDataStruct *) const { // If the exponent is zero, there is nothing to return if(m_CurvatureSpeedExponent == 0) return itk::NumericTraits::One; // Otherwise, perform interpolation on the image IndexType idx = neighborhood.GetIndex(); ContinuousIndexType cdx; for (unsigned i = 0; i < ImageDimension; ++i) { cdx[i] = static_cast(idx[i]) - offset[i]; } if ( m_CurvatureSpeedInterpolator->IsInsideBuffer(cdx) ) { return (static_cast( m_CurvatureSpeedInterpolator->EvaluateAtContinuousIndex(cdx))); } else return ( static_cast(m_CurvatureSpeedImage->GetPixel(idx)) ); } template typename SNAPLevelSetFunction::ScalarValueType SNAPLevelSetFunction ::PropagationSpeed(const NeighborhoodType &neighborhood, const FloatOffsetType &offset, GlobalDataStruct *) const { // If the exponent is zero, there is nothing to return if(m_PropagationSpeedExponent == 0) return itk::NumericTraits::One; // Otherwise, perform interpolation on the image IndexType idx = neighborhood.GetIndex(); ContinuousIndexType cdx; for (unsigned i = 0; i < ImageDimension; ++i) { cdx[i] = static_cast(idx[i]) - offset[i]; } if ( m_PropagationSpeedInterpolator->IsInsideBuffer(cdx) ) { return (static_cast( m_PropagationSpeedInterpolator->EvaluateAtContinuousIndex(cdx))); } else return ( static_cast(m_PropagationSpeedImage->GetPixel(idx)) ); } template typename SNAPLevelSetFunction::ScalarValueType SNAPLevelSetFunction ::LaplacianSmoothingSpeed(const NeighborhoodType &neighborhood, const FloatOffsetType &offset, GlobalDataStruct *) const { // If the exponent is zero, there is nothing to return if(m_LaplacianSmoothingSpeedExponent == 0) return itk::NumericTraits::One; // Otherwise, perform interpolation on the image IndexType idx = neighborhood.GetIndex(); ContinuousIndexType cdx; for (unsigned i = 0; i < ImageDimension; ++i) { cdx[i] = static_cast(idx[i]) - offset[i]; } if ( m_LaplacianSmoothingSpeedInterpolator->IsInsideBuffer(cdx) ) { return (static_cast( m_LaplacianSmoothingSpeedInterpolator->EvaluateAtContinuousIndex(cdx))); } else return ( static_cast(m_LaplacianSmoothingSpeedImage->GetPixel(idx)) ); } template typename SNAPLevelSetFunction::VectorType SNAPLevelSetFunction ::AdvectionField(const NeighborhoodType &neighborhood, const FloatOffsetType &offset, GlobalDataStruct *) const { IndexType idx = neighborhood.GetIndex(); typedef typename VectorInterpolatorType::ContinuousIndexType VectorContinuousIndexType; VectorContinuousIndexType cdx; for (unsigned i = 0; i < ImageDimension; ++i) { cdx[i] = static_cast(idx[i]) - offset[i]; } if ( m_AdvectionFieldInterpolator->IsInsideBuffer(cdx) ) { return ( m_VectorCast( m_AdvectionFieldInterpolator->EvaluateAtContinuousIndex(cdx))); } else return ( m_AdvectionField->GetPixel(idx) ); } template void SNAPLevelSetFunction ::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); } itksnap/Logic/LevelSet/SNAPLevelSetStopAndGoFilter.h0000644000076500000240000000666511100342370021633 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPLevelSetStopAndGoFilter.h,v $ Language: C++ Date: $Date: 2008/10/24 12:52:08 $ 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 __SNAPLevelSetStopAndGoFilter_h_ #define __SNAPLevelSetStopAndGoFilter_h_ #include "itkParallelSparseFieldLevelSetImageFilter.h" /** * \class SNAPLevelSetStopAndGoFilter * \brief An extension of the ITK SparseFieldLevelSetImageFilter that allows * users to execute one iteration at a time. * * This class will no longer be necessary if the functionality is added to the * FiniteDifferenceImageFilter class in ITK. */ template class ITK_EXPORT SNAPLevelSetStopAndGoFilter : public itk::ParallelSparseFieldLevelSetImageFilter { public: /** Standard class typedefs. */ typedef SNAPLevelSetStopAndGoFilter Self; typedef itk::ParallelSparseFieldLevelSetImageFilter Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Some typedefs from the parent class */ typedef typename Superclass::TimeStepType TimeStepType; /** Run-time type information. */ itkTypeMacro(SNAPLevelSetStopAndGoFilter, itk::ParallelSparseFieldLevelSetImageFilter); /** New object of this type */ itkNewMacro(SNAPLevelSetStopAndGoFilter); /** Initialize the filter before calling Run */ void Start(); /** Method that runs the filter for a number of iterations */ void Run(unsigned int nIterations); /** Override the generate data method to do nothing */ void GenerateData(); protected: SNAPLevelSetStopAndGoFilter(); ~SNAPLevelSetStopAndGoFilter() {} void PrintSelf(std::ostream &s, itk::Indent indent) const; /** Dummy implementation. Since the filter does nothing in Update(), this * method should never be called anyway */ virtual bool Halt() { return true; } private: SNAPLevelSetStopAndGoFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented /** Whether or not the stop and go is going */ bool m_Started; }; #ifndef ITK_MANUAL_INSTANTIATION #include "SNAPLevelSetStopAndGoFilter.txx" #endif #endif // __SNAPLevelSetStopAndGoFilter_h_ itksnap/Logic/LevelSet/SNAPLevelSetStopAndGoFilter.txx0000644000076500000240000000627510735614372022245 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPLevelSetStopAndGoFilter.txx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:14 $ 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. =========================================================================*/ template void SNAPLevelSetStopAndGoFilter ::Start() { // Allocate the output image typename TOutputImage::Pointer output = this->GetOutput(); output->SetBufferedRegion(output->GetRequestedRegion()); output->Allocate(); // Copy the input image to the output image. Algorithms will operate // directly on the output image and the update buffer. this->CopyInputToOutput(); // Perform any other necessary pre-iteration initialization. this->Initialize(); // Allocate the internal update buffer. This takes place entirely within // the subclass, since this class cannot define an update buffer type. this->AllocateUpdateBuffer(); // Reset the number of elapsed iterations this->SetElapsedIterations(0); // We have been initialized m_Started = true; } template void SNAPLevelSetStopAndGoFilter ::Run(unsigned int nIterations) { // Better be initialized assert(m_Started); for(unsigned int i=0;iInitializeIteration(); TimeStepType dt = this->CalculateChange(); this->ApplyUpdate(dt); this->SetElapsedIterations( this->GetElapsedIterations() + 1); } } template void SNAPLevelSetStopAndGoFilter ::GenerateData() { if(!m_Started) Start(); } template SNAPLevelSetStopAndGoFilter ::SNAPLevelSetStopAndGoFilter() { m_Started = false; } template void SNAPLevelSetStopAndGoFilter ::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Running: " << m_Started << std::endl; } itksnap/Logic/Mesh/0000755000076500000240000000000011560342170013474 5ustar paulystaffitksnap/Logic/Mesh/AllPurposeProgressAccumulator.cxx0000644000076500000240000002643710735614373022261 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: AllPurposeProgressAccumulator.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 "AllPurposeProgressAccumulator.h" #include "vtkCallbackCommand.h" #include "itkCommand.h" #include "itkEventObject.h" #include AllPurposeProgressAccumulator ::AllPurposeProgressAccumulator() { SetProgress(0.0); m_Started = m_Ended = false; } void AllPurposeProgressAccumulator::ResetProgress() { // Set the progress and state of every source for(SourceIter it = m_Source.begin(); it != m_Source.end(); ++it) { // Reset the status of each run for(unsigned int i = 0; i < it->second.Runs.size(); i++) { it->second.Runs[i].Progress = 0.0; it->second.Runs[i].Started = false; it->second.Runs[i].Ended = false; } // Set the run counter to 0 it->second.RunId = 0; DebugPrint(it->first, "RESET"); } // Set the global progress and state SetProgress(0.0f); m_Started = m_Ended = false; } void AllPurposeProgressAccumulator ::CallbackStart(void *source) { // Make sure source is registered assert(m_Source.find(source) != m_Source.end()); DebugPrint(source, "START"); // Check whether this is a valid start event. To be valid, there must be at // least one run associated with source that is in state NoEvent and no run is // state StartEvent ProgressData &pd = m_Source[source]; // Make sure that there are more runs left for this source assert( pd.RunId < pd.Runs.size() ); RunData &run = pd.Runs[pd.RunId]; // Never should have a start on an event that's ended in theory, but it // happens at least in VTK if(run.Ended) return; // It is possible it seems to receive multiple start events... if(run.Started) return; // Register the start of a new run run.Started = true; // Check if this is the first run overall bool firstRun = !m_Started; // Compute total progress and overall event state ComputeTotalProgressAndState(); // Invoke overall event if(firstRun) InvokeEvent(itk::StartEvent()); else InvokeEvent(itk::ProgressEvent()); } void AllPurposeProgressAccumulator ::CallbackProgress(void *source, double progress) { DebugPrint(source, "PROGRESS", progress); // Make sure source is registered assert(m_Source.find(source) != m_Source.end()); // Get the progress data ProgressData &pd = m_Source[source]; // Make sure that there are more runs left for this source assert( pd.RunId < pd.Runs.size() ); RunData &run = pd.Runs[pd.RunId]; // Make sure that the current run is in the correct state assert(run.Started); // If the run has finished, and event is fired, ignore it // if(run.Ended) return; // Set the progress for this run, and recompute the total progress run.Progress = progress; // Compute total progress and overall event state ComputeTotalProgressAndState(); // Invoke overall event InvokeEvent(itk::ProgressEvent()); } void AllPurposeProgressAccumulator ::CallbackEnd(void *source, double progress) { // Make sure source is registered assert(m_Source.find(source) != m_Source.end()); DebugPrint(source, "END", progress); // Get the progress data ProgressData &pd = m_Source[source]; // Make sure that there are more runs left for this source assert( pd.RunId < pd.Runs.size() ); RunData &run = pd.Runs[pd.RunId]; // Make sure that the current run is in the correct state assert(run.Started); // It is possible to get two end events in a row... // if(run.Ended) return; // It is also possible to get an end event before we finish if(progress < 1.0) return; // Set the progress for this run, and recompute the total progress run.Ended = true; run.Progress = 1.0; // Compute total progress and overall event state ComputeTotalProgressAndState(); // Invoke overall event if(m_Ended) InvokeEvent(itk::EndEvent()); else InvokeEvent(itk::ProgressEvent()); } void AllPurposeProgressAccumulator ::StartNextRun(void *source) { // Make sure source is registered assert(m_Source.find(source) != m_Source.end()); // Get the progress data ProgressData &pd = m_Source[source]; // Make sure that there are more runs left for this source assert( pd.RunId < pd.Runs.size() ); // Move to the next run pd.RunId++; } void AllPurposeProgressAccumulator ::CallbackVTK( vtkObject *source, unsigned long eventId, void *clientdata, void *callData) { // Figure out the pointers vtkAlgorithmClass *alg = dynamic_cast(source); AllPurposeProgressAccumulator *self = static_cast(clientdata); // Call the appropriate handler if(eventId == vtkCommand::ProgressEvent) self->CallbackProgress(source, alg->GetProgress()); else if(eventId == vtkCommand::EndEvent) self->CallbackEnd(source, alg->GetProgress() ); else if(eventId == vtkCommand::StartEvent) self->CallbackStart(source); } void AllPurposeProgressAccumulator ::CallbackITK(itk::Object *source, const EventType &event) { // Cast to an ITK process object itk::ProcessObject *alg = dynamic_cast(source); // Call the appropriate handler if( typeid(event) == typeid(itk::ProgressEvent) ) CallbackProgress( source, alg->GetProgress() ); else if( typeid(event) == typeid(itk::StartEvent) ) CallbackStart(source); else if( typeid(event) == typeid(itk::EndEvent) ) CallbackEnd(source, alg->GetProgress() ); } void AllPurposeProgressAccumulator ::RegisterSource(vtkAlgorithmClass *source, float weight) { // See if the source is already registered if(m_Source.find(source) == m_Source.end()) { // Create a progress data for this source ProgressData pd; pd.RunId = 0; pd.Type = VTK; // Register callbacks with the source vtkCallbackCommand *cbc = vtkCallbackCommand::New(); cbc->SetCallback(AllPurposeProgressAccumulator::CallbackVTK); cbc->SetClientData(this); pd.ProgressTag = source->AddObserver(vtkCommand::ProgressEvent, cbc); pd.EndTag = source->AddObserver(vtkCommand::EndEvent, cbc); pd.StartTag = source->AddObserver(vtkCommand::StartEvent, cbc); cbc->Delete(); // Associate the source with the progress data m_Source[source] = pd; } // Generate the info for the current run RunData run; run.Weight = weight; run.Progress = 0.0; run.Started = run.Ended = false; m_Source[source].Runs.push_back(run); } void AllPurposeProgressAccumulator ::RegisterSource(itk::ProcessObject *source, float xWeight) { // See if the source is already registered if(m_Source.find(source) == m_Source.end()) { // Create a progress data for this source ProgressData pd; pd.RunId = 0; pd.Type = ITK; // Register callbacks with this source itk::MemberCommand::Pointer cmd = itk::MemberCommand::New(); cmd->SetCallbackFunction(this, &AllPurposeProgressAccumulator::CallbackITK); pd.ProgressTag = source->AddObserver(itk::ProgressEvent(), cmd); pd.EndTag = source->AddObserver(itk::EndEvent(), cmd); pd.StartTag = source->AddObserver(itk::StartEvent(), cmd); // Associate the source with the progress data m_Source[source] = pd; } // Generate the info for the current run RunData run; run.Weight = xWeight; run.Progress = 0.0; run.Started = run.Ended = false; m_Source[source].Runs.push_back(run); } void AllPurposeProgressAccumulator ::UnregisterSource(itk::ProcessObject *source) { // Unregister ourselves as an observer source->RemoveObserver(m_Source[source].ProgressTag); source->RemoveObserver(m_Source[source].EndTag); source->RemoveObserver(m_Source[source].StartTag); // Remove the entry from the hash table m_Source.erase(source); } void AllPurposeProgressAccumulator ::UnregisterSource(vtkAlgorithmClass *source) { // Unregister ourselves as an observer source->RemoveObserver(m_Source[source].ProgressTag); source->RemoveObserver(m_Source[source].EndTag); source->RemoveObserver(m_Source[source].StartTag); // Remove the entry from the hash table m_Source.erase(source); } void AllPurposeProgressAccumulator ::UnregisterAllSources() { // Unregister each source for(SourceIter it = m_Source.begin(); it != m_Source.end(); ++it) { if(it->second.Type == VTK) { vtkAlgorithmClass *vtk = static_cast(it->first); vtk->RemoveObserver(it->second.ProgressTag); vtk->RemoveObserver(it->second.StartTag); vtk->RemoveObserver(it->second.EndTag); } else if(it->second.Type == ITK) { ProcessObject *itk = static_cast(it->first); itk->RemoveObserver(it->second.ProgressTag); itk->RemoveObserver(it->second.StartTag); itk->RemoveObserver(it->second.EndTag); } } // Clear the hash table m_Source.clear(); // Reset the state as well ResetProgress(); } void AllPurposeProgressAccumulator ::ComputeTotalProgressAndState() { double totalProgress = 0.0, totalWeight = 0.0; m_Started = false; m_Ended = true; for(SourceIter it = m_Source.begin(); it != m_Source.end(); ++it) { for(unsigned int i = 0; i < it->second.Runs.size(); i++) { // Accumulate the weights double w = it->second.Runs[i].Weight; double p = it->second.Runs[i].Progress; totalWeight += w; totalProgress += w * p; // Accumulate the status if(it->second.Runs[i].Started) m_Started = true; if(!it->second.Runs[i].Ended) m_Ended = false; } } double progress = totalWeight > 0.0 ? totalProgress / totalWeight : 0.0; SetProgress( (float) progress ); } void AllPurposeProgressAccumulator ::DebugPrint(void *source, const char *state, double p) { /* if(m_Source[source].Type == ITK) { ProcessObject *itk = static_cast(source); cout << itk->GetNameOfClass(); } else { vtkAlgorithm *vtk = static_cast(source); cout << vtk->GetClassName(); } cout << " [" << source << "], Run " << m_Source[source].RunId << " of " << m_Source[source].Runs.size() << " : " << state << "; val = " << p << endl; */ } itksnap/Logic/Mesh/AllPurposeProgressAccumulator.h0000644000076500000240000001113311412166664021667 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: AllPurposeProgressAccumulator.h,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ Version: $Revision: 1.2 $ 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 __AllPurposeProgressAccumulator_h_ #define __AllPurposeProgressAccumulator_h_ #include #include #include "itkProcessObject.h" // Some ugly code here because VTK decided to change its architecture // from 4.2 to 4.4. #include "vtkConfigure.h" #if (VTK_MAJOR_VERSION == 4) && (VTK_MINOR_VERSION <= 4) #include "vtkProcessObject.h" #define vtkAlgorithmClass vtkProcessObject #else #include "vtkAlgorithm.h" #define vtkAlgorithmClass vtkAlgorithm #endif /** * \class AllPurposeProgressAccumulator * \brief This class combines progress reports from different sources into * a single progress value. * * This class works as follows. A VTK or ITK process object can register with * this progress accumulator one or more times with different weights. When the * process object runs, the progress reported by it is accumulated into a total * progress value. The weights you supply do not need to add up to one, they * will be normalized automatically * * Because a source may fire more than one Start-Progress-End sequence per * execution, you have to advance multiple runs for one source manually using * the method StartNextRun. */ class AllPurposeProgressAccumulator : public itk::ProcessObject { public: /** Standard class typedefs. */ typedef AllPurposeProgressAccumulator Self; typedef itk::Object Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; typedef itk::EventObject EventType; /** Standard New method. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(AllPurposeProgressAccumulator, Object); /** * Reset the progress meter. This should be done before the entire pipeline * is executed for a second time. The progress will be set to zero, and the * first Begin event received by this class will be forwared on to the * observers */ void ResetProgress() ; /** * Start the next run for a source that has been registered for multiple runs. * This should be done in a pipeline that uses the same source multiple times. */ void StartNextRun(void *source); /** * Add a VTK algorithm to the list of monitored objects */ void RegisterSource(vtkAlgorithmClass *source, float xWeight); /** * Add an ITK algorithm to the list of monitored objects */ void RegisterSource(itk::ProcessObject *source, float xWeight); /** Unregister a source (and all runs associated with it) */ void UnregisterSource(vtkAlgorithmClass *source); /** Unregister a source (and all runs associated with it) */ void UnregisterSource(itk::ProcessObject *source); /** Unregister all sources and all runs */ void UnregisterAllSources(); protected: AllPurposeProgressAccumulator(); private: // Source types enum SourceType { ITK, VTK }; // Data structure associated with each component that we listen to struct RunData { double Weight, Progress; bool Started, Ended; }; struct ProgressData { std::vector Runs; unsigned int RunId; unsigned long StartTag, EndTag, ProgressTag; SourceType Type; }; // Callbacks passed to ITK and VTK sources static void CallbackVTK( vtkObject *source, unsigned long eventId, void *clientdata, void *callData); void CallbackITK(itk::Object *comp, const EventType &event); // Common event-based callbacks void CallbackStart(void *source); void CallbackEnd(void *source, double progress); void CallbackProgress(void *source, double progress); // Compute total progress void ComputeTotalProgressAndState(); void DebugPrint(void *, const char *, double prog = 0.0); // A map of weights and filter pointers typedef std::map SourceMap; typedef SourceMap::iterator SourceIter; SourceMap m_Source; // The overall state of the entire pipeline bool m_Started, m_Ended; }; #endif itksnap/Logic/Mesh/CVS/0000755000076500000240000000000011560342170014127 5ustar paulystaffitksnap/Logic/Mesh/CVS/Entries0000644000076500000240000000133411560342170015464 0ustar paulystaff/AllPurposeProgressAccumulator.cxx/1.2/Sun Dec 30 04:05:15 2007// /AllPurposeProgressAccumulator.h/1.2/Mon Jun 28 18:45:08 2010// /GuidedMeshIO.cxx/1.2/Tue Jul 3 14:06:33 2007// /GuidedMeshIO.h/1.2/Sun Dec 30 04:43:03 2007// /IRISMeshPipeline.cxx/1.4/Mon Jun 28 18:45:08 2010// /IRISMeshPipeline.h/1.3/Fri Jan 23 20:09:38 2009// /LevelSetMeshPipeline.cxx/1.2/Sun Dec 30 04:05:15 2007// /LevelSetMeshPipeline.h/1.3/Fri Jan 23 20:09:38 2009// /MeshObject.cxx/1.4/Mon Jun 28 18:45:08 2010// /MeshObject.h/1.3/Sun Dec 30 04:05:15 2007// /MeshOptions.cxx/1.2/Sun Dec 30 04:05:15 2007// /MeshOptions.h/1.2/Sun Dec 30 04:05:15 2007// /VTKMeshPipeline.cxx/1.7/Mon Jun 28 18:45:08 2010// /VTKMeshPipeline.h/1.4/Fri Jan 23 20:09:38 2009// D itksnap/Logic/Mesh/CVS/Repository0000644000076500000240000000002311560342170016224 0ustar paulystaffitksnap/Logic/Mesh itksnap/Logic/Mesh/CVS/Root0000644000076500000240000000010011560342170014764 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/Mesh/GuidedMeshIO.cxx0000644000076500000240000000353610642453751016505 0ustar paulystaff#include "GuidedMeshIO.h" #include "vtkPolyDataWriter.h" #include "vtkSTLWriter.h" #include "vtkBYUWriter.h" #include "vtkTriangleFilter.h" #include "itkExceptionObject.h" GuidedMeshIO ::GuidedMeshIO() { m_EnumFileFormat.AddPair(FORMAT_VTK, "VTK Mesh"); m_EnumFileFormat.AddPair(FORMAT_BYU, "BYU Mesh"); m_EnumFileFormat.AddPair(FORMAT_STL, "STL Mesh"); m_EnumFileFormat.AddPair(FORMAT_COUNT, "INVALID FORMAT"); } GuidedMeshIO::FileFormat GuidedMeshIO ::GetFileFormat(Registry &folder, FileFormat dflt) { return folder.Entry("Format").GetEnum(m_EnumFileFormat, dflt); } void GuidedMeshIO ::SetFileFormat(Registry &folder, FileFormat format) { folder.Entry("Format").PutEnum(m_EnumFileFormat, format); } void GuidedMeshIO ::SaveMesh(const char *FileName, Registry &folder, vtkPolyData *mesh) { // Read the format specification from the registry folder FileFormat format = GetFileFormat(folder); // Create the appropriate mesh writer for the format if(format == FORMAT_VTK) { vtkPolyDataWriter *writer = vtkPolyDataWriter::New(); writer->SetInput(mesh); writer->SetFileName(FileName); writer->Update(); writer->Delete(); } else if(format == FORMAT_STL) { vtkTriangleFilter *tri = vtkTriangleFilter::New(); vtkSTLWriter *writer = vtkSTLWriter::New(); tri->SetInput(mesh); writer->SetInput(tri->GetOutput()); writer->SetFileName(FileName); writer->Update(); writer->Delete(); tri->Delete(); } else if(format == FORMAT_BYU) { vtkTriangleFilter *tri = vtkTriangleFilter::New(); vtkBYUWriter *writer = vtkBYUWriter::New(); tri->SetInput(mesh); writer->SetInput(tri->GetOutput()); writer->SetFileName(FileName); writer->Update(); writer->Delete(); tri->Delete(); } else throw itk::ExceptionObject("Illegal format specified for saving image"); } itksnap/Logic/Mesh/GuidedMeshIO.h0000644000076500000240000000511710735620727016131 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GuidedMeshIO.h,v $ Language: C++ Date: $Date: 2007/12/30 04:43:03 $ 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. ----- 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 __GuidedMeshIO_h_ #define __GuidedMeshIO_h_ #include "Registry.h" class vtkPolyData; /** \class GuidedMeshIO \brief Class that handles mesh file IO based on 'guidance' from registry files. */ class GuidedMeshIO { public: virtual ~GuidedMeshIO() { /*To avoid compiler warning.*/ } enum FileFormat { FORMAT_VTK=0, FORMAT_STL, FORMAT_BYU, FORMAT_COUNT }; /** Default constructor */ GuidedMeshIO(); /** Parse registry to get file format */ FileFormat GetFileFormat(Registry &folder, FileFormat dflt = FORMAT_COUNT); /** Set the file format in a registry */ void SetFileFormat(Registry &folder, FileFormat format); /** Registry mappings for these enums */ RegistryEnumMap m_EnumFileFormat; /** Save an image using the Registry folder to specify parameters */ void SaveMesh(const char *FileName, Registry &folder, vtkPolyData *mesh); }; #endif itksnap/Logic/Mesh/IRISMeshPipeline.cxx0000644000076500000240000001361411412166664017306 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISMeshPipeline.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #include "IRISMeshPipeline.h" // SNAP includes #include "IRISVectorTypesToITKConversion.h" #include "VTKMeshPipeline.h" // ITK includes #include "itkRegionOfInterestImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" using namespace std; IRISMeshPipeline ::IRISMeshPipeline() { // Initialize the region of interest filter m_ROIFilter = ROIFilter::New(); m_ROIFilter->ReleaseDataFlagOn(); // Define the binary thresholding filter that will map the image onto the // range -1 to 1 m_ThrehsoldFilter = ThresholdFilter::New(); m_ThrehsoldFilter->SetInput(m_ROIFilter->GetOutput()); m_ThrehsoldFilter->ReleaseDataFlagOn(); m_ThrehsoldFilter->SetInsideValue(1.0f); m_ThrehsoldFilter->SetOutsideValue(-1.0f); // Initialize the VTK Processing Pipeline m_VTKPipeline = new VTKMeshPipeline(); m_VTKPipeline->SetImage(m_ThrehsoldFilter->GetOutput()); } IRISMeshPipeline ::~IRISMeshPipeline() { delete m_VTKPipeline; } void IRISMeshPipeline ::SetMeshOptions(const MeshOptions &options) { // Store the options m_MeshOptions = options; // Apply the options to the internal pipeline m_VTKPipeline->SetMeshOptions(m_MeshOptions); } unsigned long IRISMeshPipeline ::ComputeBoundingBoxes() { unsigned int i; unsigned long nTotalVoxels = 0; // Get the dimensions of the image InputImageType::SizeType size = m_InputImage->GetLargestPossibleRegion().GetSize(); // These vectors represent the extents of the image. Vector3l extLower(0l); Vector3l extUpper = Vector3l(reinterpret_cast(size.GetSize())) - Vector3l(1l); // For each intensity present in the image, we will compute a bounding // box. Intialize a list of bounding boxes with 'inverted' boxes Vector3l bbMin[MAX_COLOR_LABELS]; Vector3l bbMax[MAX_COLOR_LABELS]; // Initialize the histogram and bounding boxes for(i=1;i InputIterator; InputIterator it(m_InputImage,m_InputImage->GetLargestPossibleRegion()); // Parse through the image using an iterator and compute the bounding boxes for(it.GoToBegin();!it.IsAtEnd();++it) { // Get the intensity at current pixel LabelType label = it.Value(); // For non-zero labels, compute the bounding box if(label != 0) { // Increment the histogram m_Histogram[label]++; // Get the current image index Vector3l point(it.GetIndex().GetIndex()); // Update the bounding box extents bbMin[label] = vector_min(bbMin[label],point); bbMax[label] = vector_max(bbMax[label],point); } } // Convert the bounding box to a region for(i=1;iGetProgressAccumulator(); } #include bool IRISMeshPipeline ::ComputeMesh(LabelType label, vtkPolyData *outMesh) { // The label must be present in the image if(m_Histogram[label] == 0) return false; // TODO: make this more elegant InputImageType::RegionType bbWiderRegion = m_BoundingBox[label]; bbWiderRegion.PadByRadius(5); bbWiderRegion.Crop(m_InputImage->GetLargestPossibleRegion()); // Pass the region to the ROI filter and propagate the filter m_ROIFilter->SetInput(m_InputImage); m_ROIFilter->SetRegionOfInterest(bbWiderRegion); m_ROIFilter->Update(); // Set the parameters for the thresholding filter m_ThrehsoldFilter->SetLowerThreshold(label); m_ThrehsoldFilter->SetUpperThreshold(label); m_ThrehsoldFilter->UpdateLargestPossibleRegion(); // Graft the polydata to the last filter in the pipeline m_VTKPipeline->SetImage(m_ThrehsoldFilter->GetOutput()); m_VTKPipeline->ComputeMesh(outMesh); // Done return true; } void IRISMeshPipeline ::SetImage(IRISMeshPipeline::InputImageType *image) { m_InputImage = image; } itksnap/Logic/Mesh/IRISMeshPipeline.h0000644000076500000240000001100511136422002016703 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISMeshPipeline.h,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. =========================================================================*/ #ifndef __IRISMeshPipeline_h_ #define __IRISMeshPipeline_h_ #include "SNAPCommon.h" #include "itkImageRegion.h" #include "itkSmartPointer.h" #include "MeshOptions.h" // Forward reference to itk classes namespace itk { template class OrientedImage; template class RegionOfInterestImageFilter; template class BinaryThresholdImageFilter; } // Forward references class VTKMeshPipeline; class vtkPolyData; class AllPurposeProgressAccumulator; /** * \class IRISMeshPipeline * \brief A small pipeline used to convert a segmentation image to a mesh in IRIS. * * This pipeline preprocesses each label in the segmentation image by blurring it. */ class IRISMeshPipeline { public: /** Input image type */ typedef itk::OrientedImage InputImageType; typedef itk::SmartPointer InputImagePointer; /** Set the input segmentation image */ void SetImage(InputImageType *input); /** Compute the bounding boxes for different regions. Prerequisite for * calling ComputeMesh(). Returns the total number of voxels in all boxes */ unsigned long ComputeBoundingBoxes(); unsigned long GetVoxelsInBoundingBox(LabelType label) const; /** Set the mesh options for this filter */ void SetMeshOptions(const MeshOptions &options); /** Can we compute a mesh for this label? */ bool CanComputeMesh(LabelType label) { return m_Histogram[label] > 0l; } /** Compute a mesh for a particular color label. Returns true if * the color label is not present in the image */ bool ComputeMesh(LabelType label, vtkPolyData *outData); /** Constructor, which builds the pipeline */ IRISMeshPipeline(); /** Deallocate the pipeline filters */ ~IRISMeshPipeline(); /** Get the progress accumulator from the VTK mesh pipeline */ AllPurposeProgressAccumulator *GetProgressAccumulator(); private: // Type definitions for the various filters used by this object typedef itk::OrientedImage InternalImageType; typedef itk::SmartPointer InternalImagePointer; typedef itk::RegionOfInterestImageFilter< InputImageType,InputImageType> ROIFilter; typedef itk::SmartPointer ROIFilterPointer; typedef itk::BinaryThresholdImageFilter< InputImageType,InternalImageType> ThresholdFilter; typedef itk::SmartPointer ThresholdFilterPointer; // Current set of mesh options MeshOptions m_MeshOptions; // The input image InputImagePointer m_InputImage; // The ROI extraction filter used for constructing a bounding box ROIFilterPointer m_ROIFilter; // The thresholding filter used to map intensity in the bounding box to // standardized range ThresholdFilterPointer m_ThrehsoldFilter; // Set of bounding boxes itk::ImageRegion<3> m_BoundingBox[MAX_COLOR_LABELS]; // Histogram of the image long m_Histogram[MAX_COLOR_LABELS]; // The VTK pipeline VTKMeshPipeline * m_VTKPipeline; }; #endif itksnap/Logic/Mesh/LevelSetMeshPipeline.cxx0000644000076500000240000000461010735614373020261 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LevelSetMeshPipeline.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #include "LevelSetMeshPipeline.h" #include "VTKMeshPipeline.h" LevelSetMeshPipeline ::LevelSetMeshPipeline() { // Initialize the VTK Exporter m_VTKPipeline = new VTKMeshPipeline(); // Initialize the options SetMeshOptions(m_MeshOptions); } LevelSetMeshPipeline ::~LevelSetMeshPipeline() { delete m_VTKPipeline; } void LevelSetMeshPipeline ::SetMeshOptions(const MeshOptions &options) { // Store the options m_MeshOptions = options; // Turn of the Gaussian smoothing m_MeshOptions.SetUseGaussianSmoothing(false); // Apply the options to the internal pipeline m_VTKPipeline->SetMeshOptions(m_MeshOptions); } void LevelSetMeshPipeline ::ComputeMesh(vtkPolyData *outMesh) { // Run the pipeline m_VTKPipeline->ComputeMesh(outMesh); } void LevelSetMeshPipeline ::SetImage(InputImageType *image) { // Hook the input into the pipeline m_VTKPipeline->SetImage(image); } itksnap/Logic/Mesh/LevelSetMeshPipeline.h0000644000076500000240000000570511136422002017672 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LevelSetMeshPipeline.h,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. =========================================================================*/ #ifndef __LevelSetMeshPipeline_h_ #define __LevelSetMeshPipeline_h_ #include "SNAPCommon.h" #include "itkSmartPointer.h" #include "MeshOptions.h" // Forward reference to itk classes namespace itk { template class OrientedImage; } // Forward reference to our own VTK pipeline class VTKMeshPipeline; class vtkPolyData; /** * \class LevelSetMeshPipeline * \brief A pipeline used to compute a mesh of the zero level set in SNAP. * * This pipeline takes a floating point image computed by the level * set filter and uses a contour algorithm to get a triangular mesh */ class LevelSetMeshPipeline { public: /** Input image type */ typedef itk::OrientedImage InputImageType; typedef itk::SmartPointer InputImagePointer; /** Set the input segmentation image */ void SetImage(InputImageType *input); /** Set the mesh options for this filter */ void SetMeshOptions(const MeshOptions &options); /** Compute the mesh for the segmentation level set */ void ComputeMesh(vtkPolyData *outData); /** Constructor, which builds the pipeline */ LevelSetMeshPipeline(); /** Deallocate the pipeline filters */ ~LevelSetMeshPipeline(); private: // Type definitions for the various filters used by this object typedef InputImageType InternalImageType; typedef itk::SmartPointer InternalImagePointer; // Current set of mesh options MeshOptions m_MeshOptions; // The input image InputImagePointer m_InputImage; // The VTK pipeline VTKMeshPipeline *m_VTKPipeline; }; #endif //__LevelSetMeshPipeline_h_ itksnap/Logic/Mesh/MeshObject.cxx0000644000076500000240000005123511412166664016261 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MeshObject.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #include "MeshObject.h" #include "SNAPOpenGL.h" // SNAP Includes #include "ColorLabel.h" #include "GlobalState.h" #include "ImageWrapper.h" #include "IRISApplication.h" #include "IRISMeshPipeline.h" #include "LevelSetMeshPipeline.h" #include "IRISVectorTypesToITKConversion.h" #include "IRISImageData.h" #include "SNAPImageData.h" #include "AllPurposeProgressAccumulator.h" // ITK includes #include "itkRegionOfInterestImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkRecursiveGaussianImageFilter.h" #include "itkVTKImageExport.h" // VTK includes #include #include #include #include #include #include #include #include #include #include #include #include // System includes #include using namespace std; MeshObject ::MeshObject() { m_DisplayListNumber = 0; m_DisplayListIndex = 0; m_Progress = AllPurposeProgressAccumulator::New(); } MeshObject ::~MeshObject() { } void MeshObject ::Initialize(IRISApplication *driver) { m_Driver = driver; m_GlobalState = m_Driver->GetGlobalState(); } // Call this when segmentation data is no longer valid void MeshObject ::Reset() { if (m_DisplayListNumber > 0) { glDeleteLists(m_DisplayListIndex, m_DisplayListNumber); m_DisplayListNumber = 0; } m_Labels.clear(); } void MeshObject ::GenerateVTKMeshes(itk::Command *command) { unsigned int i; // The mesh array should be empty assert(m_Meshes.size() == 0); // Reset the label array and the display list index Reset(); // The mesh is constructed differently depending on whether there is an // actively evolving level set or not SNAP mode or in IRIS mode if (m_GlobalState->GetSnakeActive() && m_Driver->GetSNAPImageData()->IsSnakeLoaded()) { // We are in SNAP. Use one of SNAP's images SNAPImageData *snapData = m_Driver->GetSNAPImageData(); // Create a pipeline for mesh generation LevelSetMeshPipeline *meshPipeline = new LevelSetMeshPipeline(); // Get the float distance transform from the level set mechanism meshPipeline->SetImage(snapData->GetLevelSetImage()); // Compute the mesh only for the current segmentation color vtkPolyData *mesh = vtkPolyData::New(); meshPipeline->ComputeMesh(mesh); m_Meshes.push_back(mesh); // Deallocate the filter delete meshPipeline; } else { // Create a pipeline for mesh generation IRISMeshPipeline *meshPipeline = new IRISMeshPipeline(); // Initialize the pipeline with the correct image if(!m_GlobalState->GetSnakeActive()) { // We are not currently in SNAP. Use the segmentation image with its // different colors meshPipeline->SetImage( m_Driver->GetCurrentImageData()->GetSegmentation()->GetImage()); } else { // We are in SNAP. Use one of SNAP's images SNAPImageData *snapData = m_Driver->GetSNAPImageData(); meshPipeline->SetImage(snapData->GetSegmentation()->GetImage()); } // Pass the settings on to the pipeline meshPipeline->SetMeshOptions(m_GlobalState->GetMeshOptions()); // Run the first step in this pipeline meshPipeline->ComputeBoundingBoxes(); // Add the listener to the progress accumulator unsigned long xObserverTag = m_Progress->AddObserver(itk::ProgressEvent(), command); // Initialize the progress meter for(i = 1; i < MAX_COLOR_LABELS; i++) { ColorLabel cl = m_Driver->GetColorLabelTable()->GetColorLabel(i); if(cl.IsVisibleIn3D() && meshPipeline->CanComputeMesh(i)) { m_Progress->RegisterSource( meshPipeline->GetProgressAccumulator(), 1.0 * meshPipeline->GetVoxelsInBoundingBox(i)); } } // Compute a list of meshes in the filter for(i = 1; i < MAX_COLOR_LABELS; i++) { ColorLabel cl = m_Driver->GetColorLabelTable()->GetColorLabel(i); if(cl.IsVisibleIn3D() && meshPipeline->CanComputeMesh(i)) { vtkPolyData *mesh = vtkPolyData::New(); meshPipeline->ComputeMesh(i,mesh); m_Meshes.push_back(mesh); m_Labels.push_back(i); // Advance the progress accumulator m_Progress->StartNextRun(meshPipeline->GetProgressAccumulator()); } } // Remove all the progress sources m_Progress->UnregisterAllSources(); // Remove progress observer m_Progress->RemoveObserver(xObserverTag); // Deallocate the filter delete meshPipeline; } } void MeshObject ::GenerateDisplayLists() { // Generate an array of display lists m_DisplayListNumber = m_Meshes.size(); m_DisplayListIndex = glGenLists(m_DisplayListNumber); // Create a display list for each of the objects in the pipeline for(unsigned int dl=0;dlGetStrips(); // Get the vertex information. vtkPoints *verts = m_Meshes[dl]->GetPoints(); // Get the normal information. vtkDataArray *norms = m_Meshes[dl]->GetPointData()->GetNormals(); // Build display list glNewList(m_DisplayListIndex + dl,GL_COMPILE); vtkIdType ntris = 0; vtkIdType npts; vtkIdType *pts; for ( triStrips->InitTraversal(); triStrips->GetNextCell(npts,pts); ) { ntris += npts-2; glBegin( GL_TRIANGLE_STRIP ); for (vtkIdType j = 0; j < npts; j++) { // Some ugly code to ensure VTK version compatibility double vx = verts->GetPoint(pts[j])[0]; double vy = verts->GetPoint(pts[j])[1]; double vz = verts->GetPoint(pts[j])[2]; double nx = norms->GetTuple(pts[j])[0]; double ny = norms->GetTuple(pts[j])[1]; double nz = norms->GetTuple(pts[j])[2]; // Specify normal. glNormal3d(nx, ny, nz); // Specify vertex. glVertex3d(vx, vy, vz); } glEnd(); } glEndList(); } } void MeshObject ::DiscardVTKMeshes() { for(size_t i = 0; i < m_Meshes.size(); i++) { // Delete the mesh m_Meshes[i]->Delete(); } m_Meshes.clear(); } size_t MeshObject ::GetNumberOfVTKMeshes() const { return m_Meshes.size(); } vtkPolyData * MeshObject ::GetVTKMesh(size_t iMesh) const { return m_Meshes[iMesh]; } LabelType MeshObject ::GetVTKMeshLabel(size_t iMesh) const { return m_Labels[iMesh]; } void MeshObject ::GenerateMesh(itk::Command *command) { GenerateVTKMeshes(command); GenerateDisplayLists(); DiscardVTKMeshes(); } /* void MeshObject ::GenerateMesh(itk::Command *command) { unsigned int i; // Reset the label array and the display list index Reset(); // An array of meshes to be generated vector meshes; // The mesh is constructed differently depending on whether there is an // actively evolving level set or not // SNAP mode or in IRIS mode if (m_GlobalState->GetSnakeActive() && m_Driver->GetSNAPImageData()->IsSnakeLoaded()) { // We are in SNAP. Use one of SNAP's images SNAPImageData *snapData = m_Driver->GetSNAPImageData(); // Create a pipeline for mesh generation LevelSetMeshPipeline *meshPipeline = new LevelSetMeshPipeline(); // Get the float distance transform from the level set mechanism meshPipeline->SetImage(snapData->GetLevelSetImage()); // Compute the mesh only for the current segmentation color vtkPolyData *mesh = vtkPolyData::New(); meshPipeline->ComputeMesh(mesh); meshes.push_back(mesh); // Deallocate the filter delete meshPipeline; } else { // Create a pipeline for mesh generation IRISMeshPipeline *meshPipeline = new IRISMeshPipeline(); // Initialize the pipeline with the correct image if(!m_GlobalState->GetSnakeActive()) { // We are not currently in SNAP. Use the segmentation image with its // different colors meshPipeline->SetImage( m_Driver->GetCurrentImageData()->GetSegmentation()->GetImage()); } else { // We are in SNAP. Use one of SNAP's images SNAPImageData *snapData = m_Driver->GetSNAPImageData(); meshPipeline->SetImage(snapData->GetSegmentation()->GetImage()); } // Pass the settings on to the pipeline meshPipeline->SetMeshOptions(m_GlobalState->GetMeshOptions()); // Run the first step in this pipeline meshPipeline->ComputeBoundingBoxes(); // Add the listener to the progress accumulator unsigned long xObserverTag = m_Progress->AddObserver(itk::ProgressEvent(), command); // Initialize the progress meter for(i = 1; i < MAX_COLOR_LABELS; i++) { ColorLabel cl = m_Driver->GetColorLabelTable()->GetColorLabel(i); if(cl.IsVisibleIn3D() && meshPipeline->CanComputeMesh(i)) { m_Progress->RegisterSource( meshPipeline->GetProgressAccumulator(), 1.0 * meshPipeline->GetVoxelsInBoundingBox(i)); } } // Compute a list of meshes in the filter for(i = 1; i < MAX_COLOR_LABELS; i++) { ColorLabel cl = m_Driver->GetColorLabelTable()->GetColorLabel(i); if(cl.IsVisibleIn3D() && meshPipeline->CanComputeMesh(i)) { vtkPolyData *mesh = vtkPolyData::New(); meshPipeline->ComputeMesh(i,mesh); meshes.push_back(mesh); m_Labels.push_back(i); // Advance the progress accumulator m_Progress->StartNextRun(meshPipeline->GetProgressAccumulator()); } } // Remove all the progress sources m_Progress->UnregisterAllSources(); // Remove progress observer m_Progress->RemoveObserver(xObserverTag); // Deallocate the filter delete meshPipeline; } // Generate an array of display lists m_DisplayListNumber = meshes.size(); m_DisplayListIndex = glGenLists(m_DisplayListNumber); // Create a display list for each of the objects in the pipeline for(unsigned int dl=0;dlGetStrips(); // Get the vertex information. vtkPoints *verts = meshes[dl]->GetPoints(); // Get the normal information. vtkDataArray *norms = meshes[dl]->GetPointData()->GetNormals(); // Build display list glNewList(m_DisplayListIndex + dl,GL_COMPILE); vtkIdType ntris = 0; vtkIdType npts; vtkIdType *pts; for ( triStrips->InitTraversal(); triStrips->GetNextCell(npts,pts); ) { ntris += npts-2; glBegin( GL_TRIANGLE_STRIP ); for (vtkIdType j = 0; j < npts; j++) { // Some ugly code to ensure VTK version compatibility double vx = verts->GetPoint(pts[j])[0]; double vy = verts->GetPoint(pts[j])[1]; double vz = verts->GetPoint(pts[j])[2]; double nx = norms->GetTuple(pts[j])[0]; double ny = norms->GetTuple(pts[j])[1]; double nz = norms->GetTuple(pts[j])[2]; // Specify normal. glNormal3d(nx, ny, nz); // Specify vertex. glVertex3d(vx, vy, vz); } glEnd(); } glEndList(); // Delete the mesh meshes[dl]->Delete(); } } */ /* * Apply color label, a shorthand */ bool MeshObject ::ApplyColorLabel(const ColorLabel &label) { if (label.IsVisible() && label.IsVisibleIn3D()) { // Adjust the label color to reduce the saturation. This in necessary // in order to see the highlights on the object double r = 0.75 * (label.GetRGB(0) / 255.0); double g = 0.75 * (label.GetRGB(1) / 255.0); double b = 0.75 * (label.GetRGB(2) / 255.0); double a = label.GetAlpha() / 255.0; if (label.IsOpaque()) glColor3d(r, g, b); else glColor4d(r, g, b, a); return true; } return false; } void MeshObject ::Display() { unsigned int i; // Define a shorthand pointer to the image data //IRISImageData *irisData = m_Driver->GetCurrentImageData(); // check if the snake is not active // if yes it means we are in IRIS // so we render all segmentations if (!m_GlobalState->GetSnakeActive()) { // First render all the fully opaque objects for (i=0; i < m_DisplayListNumber; i++) { const ColorLabel &cl = m_Driver->GetColorLabelTable()->GetColorLabel(m_Labels[i]); if (cl.IsOpaque() && ApplyColorLabel(cl)) { glCallList(m_DisplayListIndex + i); } } // Now render all the translucent objects, with depth buffer read-only // Ideally we should sort polygons from back to front // TODO: Utilize VTK! glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_FALSE); for (i=0; i < m_DisplayListNumber; i++) { const ColorLabel &cl = m_Driver->GetColorLabelTable()->GetColorLabel(m_Labels[i]); if (!cl.IsOpaque() && ApplyColorLabel(cl)) { glCallList(m_DisplayListIndex + i); } } glPopAttrib(); } // if the snake is active render only the current segmentation // added by Konstantin Bobkov else if(m_DisplayListNumber > 0) { // get current label int currentcolor = m_GlobalState->GetDrawingColorLabel(); // first check if the segmentation is fully opaque // and render it const ColorLabel &cl = m_Driver->GetColorLabelTable()->GetColorLabel(currentcolor); if (cl.IsOpaque() && ApplyColorLabel(cl)) { glCallList(m_DisplayListIndex); } // now check if the segmentation is translucent // and render it with depth buffer read-only glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_FALSE); if (!cl.IsOpaque() && ApplyColorLabel(cl)) { glCallList(m_DisplayListIndex); } glPopAttrib(); } } /* *$Log: MeshObject.cxx,v $ *Revision 1.4 2010/06/28 18:45:08 pyushkevich *Patch from Michael Hanke to allow ITK 3.18 builds * *Revision 1.3 2007/12/30 04:05:15 pyushkevich *GPL License * *Revision 1.2 2007/05/10 20:19:50 pyushkevich *Added VTK mesh export code and GUI * *Revision 1.1 2006/12/02 04:22:14 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.19 2005/12/08 18:20:45 hjohnson *COMP: Removed compiler warnings from SGI/linux/MacOSX compilers. * *Revision 1.18 2005/11/23 14:32:15 ibanez *BUG: 2404. Patch provided by Paul Yushkevish. * *Revision 1.17 2005/10/29 14:00:14 pauly *ENH: SNAP enhacements like color maps and progress bar for 3D rendering * *Revision 1.16 2005/04/21 14:46:29 pauly *ENH: Improved management and editing of color labels in SNAP * *Revision 1.15 2004/09/14 14:11:09 pauly *ENH: Added an activation manager to main UI class, improved snake code, various UI fixes and additions * *Revision 1.14 2004/08/26 19:43:23 pauly *ENH: Moved the Borland code into Common folder * *Revision 1.13 2004/08/03 23:26:32 ibanez *ENH: Modification for building in multple platforms. By Julien Jomier. * *Revision 1.12 2004/07/29 14:01:56 pauly *ENH: An interface for changing SNAP appearance settings * *Revision 1.11 2004/01/27 17:34:00 pauly *FIX: Compiling on Mac OSX, issue with GLU include file * *Revision 1.10 2004/01/20 00:17:42 pauly *FIX: VTK float compatibility * *Revision 1.9 2004/01/17 18:39:07 lorensen *ENH: changes to accomodate VTK api changes. * *Revision 1.8 2003/10/09 22:45:13 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.7 2003/10/02 14:54:53 pauly *ENH: Development during the September code freeze * *Revision 1.1 2003/09/11 13:50:29 pauly *FIX: Enabled loading of images with different orientations *ENH: Implemented image save and load operations * *Revision 1.5 2003/08/28 14:37:09 pauly *FIX: Clean 'unused parameter' and 'static keyword' warnings in gcc. *FIX: Label editor repaired * *Revision 1.4 2003/08/27 14:03:21 pauly *FIX: Made sure that -Wall option in gcc generates 0 warnings. *FIX: Removed 'comment within comment' problem in the cvs log. * *Revision 1.3 2003/08/27 04:57:46 pauly *FIX: A large number of bugs has been fixed for 1.4 release * *Revision 1.2 2003/08/14 13:37:07 pauly *FIX: Error with using int instead of vtkIdType in calling vtkCellArray::GetNextCell * *Revision 1.1 2003/07/12 04:52:25 pauly *Initial checkin of SNAP application to 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:29:17 pauly **** empty log message *** * *Revision 1.10 2003/07/11 21:25:12 pauly *Code cleanup for ITK checkin * *Revision 1.9 2003/07/10 14:30:26 pauly *Integrated ITK into SNAP level set segmentation * *Revision 1.8 2003/06/08 16:11:42 pauly *User interface changes *Automatic mesh updating in SNAP mode * *Revision 1.7 2003/05/12 02:51:10 pauly *Got code to compile on UNIX * *Revision 1.6 2003/05/05 12:30:18 pauly **** empty log message *** * *Revision 1.5 2003/04/29 14:01:42 pauly *Charlotte Trip * *Revision 1.4 2003/04/23 20:36:23 pauly **** empty log message *** * *Revision 1.3 2003/04/23 06:05:18 pauly **** empty log message *** * *Revision 1.2 2003/04/18 17:32:18 pauly **** empty log message *** * *Revision 1.1 2003/03/07 19:29:47 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.11 2002/05/08 17:34:18 moon *Fixed bug of using the voxel snake image, instead of hte float snake state, *for the marching cubes. Now the snake looks smooth instead of voxelized. * *Revision 1.10 2002/04/27 18:30:22 moon *Finished commenting * *Revision 1.9 2002/04/27 17:49:00 bobkov *Added comments * *Revision 1.8 2002/04/24 17:12:48 bobkov *modified display() method so that when num_lists is zero *the 3D window does not show anything * *Revision 1.7 2002/04/23 19:30:10 bobkov *modified display method so that the current color label is *used in the snake 3d window * *Revision 1.6 2002/04/13 17:45:32 moon *I put code in to check if a label exists in the seg image in GenerateMesh, *so that the vtk pipeline (SLOW!) wouldn't have to get executed on empty images. *(i.e. all 6 or whatever labels that are initialized at the start are "rendered" *even if only one label has actually been used) * *Revision 1.5 2002/04/10 21:20:42 moon *fixed bug when update mesh is pressed with no seg data * *Revision 1.4 2002/04/10 20:20:36 moon *fixed small problem with snake 3d window mesh generation with Konstantin *It was using full_data, and we switched it to use only roi_data. * *Revision 1.3 2002/04/09 22:00:23 bobkov * *Modified GenerateMesh method to display 3d snake segmentation * *Revision 1.2 2002/03/08 14:06:29 moon *Added Header and Log tags to all files **/ itksnap/Logic/Mesh/MeshObject.h0000644000076500000240000001726710735614373015717 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MeshObject.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 __MeshObject_h_ #define __MeshObject_h_ // Forward references class IRISApplication; class GlobalState; class IRISImageData; class ColorLabel; class AllPurposeProgressAccumulator; class vtkPolyData; namespace itk { class Command; } #include "SNAPCommon.h" #include "AllPurposeProgressAccumulator.h" #include /** * \class MeshObject * \brief A class representing a mesh generated from a segmentation. * * This class wraps around IRISMeshPipeline and LevelSetMeshPipeline. It's a very * high level class that generates a correct mesh based on the current state of the * application. */ class MeshObject { private: // The number of allocated display lists unsigned int m_DisplayListNumber; // The starting display list number unsigned int m_DisplayListIndex; // The labels associated with the allocated display lists std::vector m_Labels; // The VTK meshes (optionally available) std::vector m_Meshes; /** * This method applies the settings in a color label if color label * is displayable */ bool ApplyColorLabel(const ColorLabel &label); // Back pointer to the application object IRISApplication *m_Driver; // Pointer to the global state object GlobalState *m_GlobalState; // Progress accumulator for multi-object rendering itk::SmartPointer m_Progress; public: MeshObject(); MeshObject( const MeshObject& M ) { *this=M; } MeshObject& operator= ( const MeshObject& M ) { *this=M; return *this; } ~MeshObject(); // Reset the label array and the display list index. void Reset(); /** * Initialize the object with an IRISApplication pointer */ void Initialize(IRISApplication *driver); /* MeshObject::GenerateMesh(); * * DESCRIPTION: * This function generates a mesh which represents a 3D * segmentation. This operation is accomplished by retrieving * the image data from the IRISImageData structure and then * invoking a VTK isosurface extraction algorithm. To create * the surface it performs the following steps (GB, MS, TMR * are optional): * 1.Gaussian Blur on the imported image data * 2.Triangle generation using a contour algorithm * 3.Mesh Smoothing * 4.Triangle Mesh Reduction * 5.Triangle stripping * * Modified by Konstantin Bobkov and Nathan Moon (April 2002) * New modification of this method allows to generate * trianglemesh for the Snake Voxel Data. In this case the * Gaussian Blur step on the imported SnakeVoxData is not * performed * * PRECONDITIONS: * - Valid Segmented VoxData or SnakeVoxData * * POSTCONDITIONS: * - A displaylist (points) and a trianglemesh */ void GenerateMesh(itk::Command *command); /** * Generate VTK meshes from input data. This method is called by * GenerateMesh internally. But the user of this class can choose * to call this method directly if the meshes are needed for export */ void GenerateVTKMeshes(itk::Command *command); /** * Convert VTK meshes to display lists. This method is called inside * GenerateMesh. Normally, you would not use this method. */ void GenerateDisplayLists(); /** * Discard VTK meshes. This method is called by GenerateMesh intenally. * You should call this method after calling GenerateVTKMesh. */ void DiscardVTKMeshes(); /** * Get the number of VTK meshes available */ size_t GetNumberOfVTKMeshes() const; /** * Get the i-th VTK mesh */ vtkPolyData *GetVTKMesh(size_t iMesh) const; /** * Get the label associated with i-th mesh */ LabelType GetVTKMeshLabel(size_t iMesh) const; /* MeshObject::Display(); * * DESCRIPTION: * This function displays the mesh generated by the * GenerateMesh() method described above * * Modified by Konstantin Bobkov (April 2002) * New modification of this method allows to display the * currently generated trianglemesh for the Snake Voxel Data. * * * PRECONDITIONS: * - for each segmented object to be displayed the mesh is valid * * POSTCONDITIONS: * - each object in displaylist is displayed */ void Display(); }; #endif // __MeshObject_h_ /* *$Log: MeshObject.h,v $ *Revision 1.3 2007/12/30 04:05:15 pyushkevich *GPL License * *Revision 1.2 2007/05/10 20:19:50 pyushkevich *Added VTK mesh export code and GUI * *Revision 1.1 2006/12/02 04:22:15 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.8 2005/10/29 14:00:14 pauly *ENH: SNAP enhacements like color maps and progress bar for 3D rendering * *Revision 1.7 2003/10/09 22:45:13 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.6 2003/10/02 14:54:53 pauly *ENH: Development during the September code freeze * *Revision 1.1 2003/09/11 13:50:29 pauly *FIX: Enabled loading of images with different orientations *ENH: Implemented image save and load operations * *Revision 1.4 2003/08/28 14:37:09 pauly *FIX: Clean 'unused parameter' and 'static keyword' warnings in gcc. *FIX: Label editor repaired * *Revision 1.3 2003/08/27 14:03:21 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:46 pauly *FIX: A large number of bugs has been fixed for 1.4 release * *Revision 1.1 2003/07/12 04:52:25 pauly *Initial checkin of SNAP application to the InsightApplications tree * *Revision 1.7 2003/07/12 01:34:18 pauly *More final changes before ITK checkin * *Revision 1.6 2003/07/11 23:29:17 pauly **** empty log message *** * *Revision 1.5 2003/07/11 21:25:12 pauly *Code cleanup for ITK checkin * *Revision 1.4 2003/06/08 16:11:42 pauly *User interface changes *Automatic mesh updating in SNAP mode * *Revision 1.3 2003/05/05 12:30:18 pauly **** empty log message *** * *Revision 1.2 2003/04/18 17:32:18 pauly **** empty log message *** * *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.4 2002/04/27 17:49:00 bobkov *Added comments * *Revision 1.3 2002/04/11 23:09:15 bobkov *Commented GenerateMesh() method * *Revision 1.2 2002/03/08 14:06:29 moon *Added Header and Log tags to all files **/ itksnap/Logic/Mesh/MeshOptions.cxx0000644000076500000240000000754710735614373016517 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MeshOptions.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 "MeshOptions.h" MeshOptions ::MeshOptions() { // Begin render switches m_UseGaussianSmoothing = true; m_UseDecimation = false; m_UseMeshSmoothing = false; // Begin gsmooth params m_GaussianStandardDeviation = 0.8f; m_GaussianError = 0.03f; // Begin decimate params m_DecimateTargetReduction = 0.95f; m_DecimateInitialError = 0.002f; m_DecimateAspectRatio = 20.0f; m_DecimateErrorIncrement = 0.002f; m_DecimateMaximumIterations = 1; m_DecimateFeatureAngle = 45; m_DecimatePreserveTopology = true; // Begin msmooth params m_MeshSmoothingIterations = 1; m_MeshSmoothingRelaxationFactor = 0.01f; m_MeshSmoothingFeatureAngle = 45; m_MeshSmoothingConvergence = 0; m_MeshSmoothingFeatureEdgeSmoothing = false; m_MeshSmoothingBoundarySmoothing = false; } MeshOptions ::~MeshOptions() { } /* *$Log: MeshOptions.cxx,v $ *Revision 1.2 2007/12/30 04:05:15 pyushkevich *GPL License * *Revision 1.1 2006/12/02 04:22:15 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.5 2003/10/09 22:45:13 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.4 2003/10/02 14:54:53 pauly *ENH: Development during the September code freeze * *Revision 1.1 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:21 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:46 pauly *FIX: A large number of bugs has been fixed for 1.4 release * *Revision 1.1 2003/07/12 04:52:25 pauly *Initial checkin of SNAP application to 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:29:17 pauly **** empty log message *** * *Revision 1.4 2003/06/08 16:11:42 pauly *User interface changes *Automatic mesh updating in SNAP mode * *Revision 1.3 2003/04/23 06:05:18 pauly **** empty log message *** * *Revision 1.2 2003/04/16 05:04:17 pauly *Incorporated intensity modification into the snap pipeline *New IRISApplication *Random goodies * *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.2 2002/03/08 14:06:30 moon *Added Header and Log tags to all files **/ itksnap/Logic/Mesh/MeshOptions.h0000644000076500000240000001306610735614373016135 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MeshOptions.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 __MeshOptions_h_ #define __MeshOptions_h_ #include "SNAPCommon.h" /** * \class MeshOptions * \brief A set of options for mesh display in IRIS. */ class MeshOptions { public: MeshOptions(); virtual ~MeshOptions(); irisGetMacro(UseGaussianSmoothing,bool); irisSetMacro(UseGaussianSmoothing,bool); irisGetMacro(UseDecimation,bool); irisSetMacro(UseDecimation,bool); irisGetMacro(UseMeshSmoothing,bool); irisSetMacro(UseMeshSmoothing,bool); irisGetMacro(GaussianStandardDeviation,float); irisSetMacro(GaussianStandardDeviation,float); irisGetMacro(GaussianError,float); irisSetMacro(GaussianError,float); irisGetMacro(DecimateTargetReduction,float); irisSetMacro(DecimateTargetReduction,float); irisGetMacro(DecimateInitialError,float); irisSetMacro(DecimateInitialError,float); irisGetMacro(DecimateAspectRatio,float); irisSetMacro(DecimateAspectRatio,float); irisGetMacro(DecimateFeatureAngle,float); irisSetMacro(DecimateFeatureAngle,float); irisGetMacro(DecimateErrorIncrement,float); irisSetMacro(DecimateErrorIncrement,float); irisGetMacro(DecimateMaximumIterations,unsigned int); irisSetMacro(DecimateMaximumIterations,unsigned int); irisGetMacro(DecimatePreserveTopology,bool); irisSetMacro(DecimatePreserveTopology,bool); irisGetMacro(MeshSmoothingRelaxationFactor,float); irisSetMacro(MeshSmoothingRelaxationFactor,float); irisGetMacro(MeshSmoothingIterations,unsigned int); irisSetMacro(MeshSmoothingIterations,unsigned int); irisGetMacro(MeshSmoothingConvergence,float); irisSetMacro(MeshSmoothingConvergence,float); irisGetMacro(MeshSmoothingFeatureAngle,float); irisSetMacro(MeshSmoothingFeatureAngle,float); irisGetMacro(MeshSmoothingFeatureEdgeSmoothing,bool); irisSetMacro(MeshSmoothingFeatureEdgeSmoothing,bool); irisGetMacro(MeshSmoothingBoundarySmoothing,bool); irisSetMacro(MeshSmoothingBoundarySmoothing,bool); private: // Begin render switches bool m_UseGaussianSmoothing; bool m_UseDecimation; bool m_UseMeshSmoothing; // Begin gsmooth params float m_GaussianStandardDeviation; float m_GaussianError; // Begin decimate parameters float m_DecimateTargetReduction; float m_DecimateInitialError; float m_DecimateAspectRatio; float m_DecimateFeatureAngle; float m_DecimateErrorIncrement; unsigned int m_DecimateMaximumIterations; bool m_DecimatePreserveTopology; // Begin msmooth params float m_MeshSmoothingRelaxationFactor; unsigned int m_MeshSmoothingIterations; float m_MeshSmoothingConvergence; float m_MeshSmoothingFeatureAngle; bool m_MeshSmoothingFeatureEdgeSmoothing; bool m_MeshSmoothingBoundarySmoothing; }; #endif // __MeshOptions_h_ /* *$Log: MeshOptions.h,v $ *Revision 1.2 2007/12/30 04:05:15 pyushkevich *GPL License * *Revision 1.1 2006/12/02 04:22:15 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.5 2003/10/09 22:45:13 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.4 2003/10/02 14:54:53 pauly *ENH: Development during the September code freeze * *Revision 1.1 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:21 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:46 pauly *FIX: A large number of bugs has been fixed for 1.4 release * *Revision 1.1 2003/07/12 04:52:25 pauly *Initial checkin of SNAP application to the InsightApplications tree * *Revision 1.5 2003/07/12 01:34:18 pauly *More final changes before ITK checkin * *Revision 1.4 2003/07/11 23:29:17 pauly **** empty log message *** * *Revision 1.3 2003/07/11 21:25:12 pauly *Code cleanup for ITK checkin * *Revision 1.2 2003/06/08 16:11:42 pauly *User interface changes *Automatic mesh updating in SNAP mode * *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.2 2002/03/08 14:06:30 moon *Added Header and Log tags to all files **/ itksnap/Logic/Mesh/VTKMeshPipeline.cxx0000644000076500000240000002337311412166664017207 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: VTKMeshPipeline.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ Version: $Revision: 1.7 $ 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 "VTKMeshPipeline.h" #include "AllPurposeProgressAccumulator.h" #include "ImageWrapper.h" #include using namespace std; VTKMeshPipeline ::VTKMeshPipeline() { // Initialize the progress tracker m_Progress = AllPurposeProgressAccumulator::New(); // Initialize all the filters involved in the transaction, but do not // pipe the inputs and outputs between these filters. The piping is quite // complicated and depends on the set of options that the user wishes to // apply // Initialize the VTK Exporter m_VTKExporter = VTKExportType::New(); m_VTKExporter->ReleaseDataFlagOn(); // Initialize the VTK Importer m_VTKImporter = vtkImageImport::New(); m_VTKImporter->ReleaseDataFlagOn(); // Pipe the importer into the exporter (that's a lot of code) m_VTKImporter->SetUpdateInformationCallback( m_VTKExporter->GetUpdateInformationCallback()); m_VTKImporter->SetPipelineModifiedCallback( m_VTKExporter->GetPipelineModifiedCallback()); m_VTKImporter->SetWholeExtentCallback( m_VTKExporter->GetWholeExtentCallback()); m_VTKImporter->SetSpacingCallback( m_VTKExporter->GetSpacingCallback()); m_VTKImporter->SetOriginCallback( m_VTKExporter->GetOriginCallback()); m_VTKImporter->SetScalarTypeCallback( m_VTKExporter->GetScalarTypeCallback()); m_VTKImporter->SetNumberOfComponentsCallback( m_VTKExporter->GetNumberOfComponentsCallback()); m_VTKImporter->SetPropagateUpdateExtentCallback( m_VTKExporter->GetPropagateUpdateExtentCallback()); m_VTKImporter->SetUpdateDataCallback( m_VTKExporter->GetUpdateDataCallback()); m_VTKImporter->SetDataExtentCallback( m_VTKExporter->GetDataExtentCallback()); m_VTKImporter->SetBufferPointerCallback( m_VTKExporter->GetBufferPointerCallback()); m_VTKImporter->SetCallbackUserData( m_VTKExporter->GetCallbackUserData()); // Initialize the Gaussian filter m_VTKGaussianFilter = vtkImageGaussianSmooth::New(); m_VTKGaussianFilter->ReleaseDataFlagOn(); // Create and configure a filter for polygon smoothing m_PolygonSmoothingFilter = vtkSmoothPolyDataFilter::New(); m_PolygonSmoothingFilter->ReleaseDataFlagOn(); // Create and configure a filter for triangle strip generation m_StripperFilter = vtkStripper::New(); m_StripperFilter->ReleaseDataFlagOn(); // Create and configure the marching cubes filter m_MarchingCubesFilter = vtkMarchingCubes::New(); m_MarchingCubesFilter->ReleaseDataFlagOn(); m_MarchingCubesFilter->ComputeScalarsOff(); m_MarchingCubesFilter->ComputeGradientsOff(); m_MarchingCubesFilter->SetNumberOfContours(1); m_MarchingCubesFilter->SetValue(0,0.0f); // Create the transform filter m_TransformFilter = vtkTransformPolyDataFilter::New(); m_TransformFilter->ReleaseDataFlagOn(); // Create the transform m_Transform = vtkTransform::New(); // Create and configure a filter for triangle decimation m_DecimateFilter = vtkDecimatePro::New(); m_DecimateFilter->ReleaseDataFlagOn(); } VTKMeshPipeline ::~VTKMeshPipeline() { // Destroy the filters m_VTKImporter->Delete(); m_VTKGaussianFilter->Delete(); m_PolygonSmoothingFilter->Delete(); m_StripperFilter->Delete(); m_MarchingCubesFilter->Delete(); m_TransformFilter->Delete(); m_Transform->Delete(); m_DecimateFilter->Delete(); } void VTKMeshPipeline ::SetMeshOptions(const MeshOptions &options) { // Store the options m_MeshOptions = options; // Reset the weights in the progress meter m_Progress->UnregisterAllSources(); // Define the current pipeline end-point vtkImageData *pipeImageTail = m_VTKImporter->GetOutput(); vtkPolyData *pipePolyTail = NULL; // Route the pipeline according to the settings // 1. Check if Gaussian smoothing will be used if(options.GetUseGaussianSmoothing()) { // The Gaussian filter is enabled m_VTKGaussianFilter->SetInput(pipeImageTail); m_Progress->RegisterSource(m_VTKGaussianFilter, 10.0f); pipeImageTail = m_VTKGaussianFilter->GetOutput(); // Apply parameters to the Gaussian filter float sigma = options.GetGaussianStandardDeviation(); // Sigma is in millimeters const double *spacing = m_InputImage->GetSpacing().GetDataPointer(); m_VTKGaussianFilter->SetStandardDeviation( sigma / spacing[0], sigma / spacing[1], sigma / spacing[2]); m_VTKGaussianFilter->SetRadiusFactors( 3 * sigma / spacing[0], 3 * sigma / spacing[1], 3 * sigma / spacing[2]); } // 2. Set input to the appropriate contour filter // Marching cubes gets the tail m_MarchingCubesFilter->SetInput(pipeImageTail); m_Progress->RegisterSource(m_MarchingCubesFilter, 10.0f); pipePolyTail = m_MarchingCubesFilter->GetOutput(); // 2.5 Pipe marching cubes output to the transform m_TransformFilter->SetInput(pipePolyTail); m_Progress->RegisterSource(m_TransformFilter, 1.0f); pipePolyTail = m_TransformFilter->GetOutput(); // 3. Check if decimation is required if(options.GetUseDecimation()) { // Decimate filter gets the pipe tail m_DecimateFilter->SetInput(pipePolyTail); m_Progress->RegisterSource(m_DecimateFilter, 5.0); pipePolyTail = m_DecimateFilter->GetOutput(); // Apply parameters to the decimation filter m_DecimateFilter->SetTargetReduction( options.GetDecimateTargetReduction()); m_DecimateFilter->SetMaximumError( options.GetDecimateInitialError()); m_DecimateFilter->SetFeatureAngle( options.GetDecimateFeatureAngle()); m_DecimateFilter->SetPreserveTopology( options.GetDecimatePreserveTopology()); } // If decimate enabled // 4. Compute the normals (non-patented only) // 5. Include/exclude mesh smoothing filter if(options.GetUseMeshSmoothing()) { // Pipe smoothed output into the pipeline m_PolygonSmoothingFilter->SetInput(pipePolyTail); m_Progress->RegisterSource(m_PolygonSmoothingFilter, 3.0); pipePolyTail = m_PolygonSmoothingFilter->GetOutput(); // Apply parameters to the mesh smoothing filter m_PolygonSmoothingFilter->SetNumberOfIterations( options.GetMeshSmoothingIterations()); m_PolygonSmoothingFilter->SetRelaxationFactor( options.GetMeshSmoothingRelaxationFactor()); m_PolygonSmoothingFilter->SetFeatureAngle( options.GetMeshSmoothingFeatureAngle()); m_PolygonSmoothingFilter->SetFeatureEdgeSmoothing( options.GetMeshSmoothingFeatureEdgeSmoothing()); m_PolygonSmoothingFilter->SetBoundarySmoothing( options.GetMeshSmoothingBoundarySmoothing()); m_PolygonSmoothingFilter->SetConvergence( options.GetMeshSmoothingConvergence()); } // 6. Pipe in the final output into the stripper m_StripperFilter->SetInput(pipePolyTail); m_Progress->RegisterSource(m_StripperFilter, 2.0); } #include void VTKMeshPipeline ::ComputeMesh(vtkPolyData *outMesh) { // Reset the progress meter m_Progress->ResetProgress(); // Graft the polydata to the last filter in the pipeline m_StripperFilter->SetOutput(outMesh); // Connect importer and exporter m_VTKImporter->SetCallbackUserData( m_VTKExporter->GetCallbackUserData()); // Update the ITK portion of the pipeline m_VTKExporter->SetInput(m_InputImage); m_VTKImporter->Modified(); // Update the pipeline // m_StripperFilter->Update(); // Set the source of outMesh to null // outMesh->UpdateInformation(); // outMesh->Update(); // m_StripperFilter->UnRegister(m_StripperFilter->GetOutput()); m_StripperFilter->Update(); // In the case that the jacobian of the transform is negative, // flip the normals around if(m_Transform->GetMatrix()->Determinant() < 0) { vtkPointData *pd = m_StripperFilter->GetOutput()->GetPointData(); vtkDataArray *nrm = pd->GetNormals(); for(size_t i = 0; i < (size_t)nrm->GetNumberOfTuples(); i++) for(size_t j = 0; j < (size_t)nrm->GetNumberOfComponents(); j++) nrm->SetComponent(i,j,-nrm->GetComponent(i,j)); nrm->Modified(); } } void VTKMeshPipeline ::SetImage(ImageType *image) { // Store the image m_InputImage = image; // Compute the transform from VTK coordinates to NIFTI/RAS coordinates vnl_matrix_fixed vtk2nii = ImageWrapper::ConstructVTKtoNiftiTransform( image->GetDirection().GetVnlMatrix(), image->GetOrigin().GetVnlVector(), image->GetSpacing().GetVnlVector()); // Update the VTK transform to match m_Transform->SetMatrix(vtk2nii.data_block()); // Pass the transform to the transform filter m_TransformFilter->SetTransform(m_Transform); } itksnap/Logic/Mesh/VTKMeshPipeline.h0000644000076500000240000001033411136422002016605 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: VTKMeshPipeline.h,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ 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 __VTKMeshPipeline_h_ #define __VTKMeshPipeline_h_ #include "SNAPCommon.h" #include "MeshOptions.h" #include "AllPurposeProgressAccumulator.h" // ITK includes (this file is not widely included in SNAP, so it's OK // to include a bunch of headers here). #include #include // VTK includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef vtkFloatingPointType # define vtkFloatingPointType vtkFloatingPointType typedef float vtkFloatingPointType; #endif class VTKProgressAccumulator; /** * \class VTKMeshPipeline * \brief A small pipeline used to convert an ITK image with a level set into * a VTK contour, with optional blurring */ class VTKMeshPipeline { public: /** Input image type */ typedef itk::OrientedImage ImageType; typedef itk::SmartPointer ImagePointer; /** Set the input segmentation image */ void SetImage(ImageType *input); /** Set the mesh options for this filter */ void SetMeshOptions(const MeshOptions &options); /** Compute a mesh for a particular color label */ void ComputeMesh(vtkPolyData *outData); /** Get the progress accumulator */ AllPurposeProgressAccumulator *GetProgressAccumulator() { return m_Progress; } /** Constructor, which builds the pipeline */ VTKMeshPipeline(); /** Deallocate the pipeline filters */ ~VTKMeshPipeline(); private: // VTK-ITK Connection typedefs typedef itk::VTKImageExport VTKExportType; typedef itk::SmartPointer VTKExportPointer; // Current set of mesh options MeshOptions m_MeshOptions; // The input image ImagePointer m_InputImage; // The VTK exporter for the data VTKExportPointer m_VTKExporter; // The VTK importer for the data vtkImageImport *m_VTKImporter; // VTK Gaussian (because we care about the speed and not so much about // precision) vtkImageGaussianSmooth *m_VTKGaussianFilter; // The polygon smoothing filter vtkSmoothPolyDataFilter *m_PolygonSmoothingFilter; // Triangle stripper vtkStripper *m_StripperFilter; // Marching cubes filter vtkMarchingCubes * m_MarchingCubesFilter; // Transform filter used to map to RAS space vtkTransformPolyDataFilter *m_TransformFilter; // The transform used vtkTransform * m_Transform; // The triangle decimation driver vtkDecimatePro * m_DecimateFilter; // Progress event monitor AllPurposeProgressAccumulator::Pointer m_Progress; }; #endif // __VTKMeshPipeline_h_ itksnap/Logic/Preprocessing/0000755000076500000240000000000011560342170015423 5ustar paulystaffitksnap/Logic/Preprocessing/CVS/0000755000076500000240000000000011560342170016056 5ustar paulystaffitksnap/Logic/Preprocessing/CVS/Entries0000644000076500000240000000075611560342170017422 0ustar paulystaff/EdgePreprocessingImageFilter.h/1.4/Sat Jan 24 01:50:21 2009// /EdgePreprocessingImageFilter.txx/1.3/Mon Jun 28 18:45:08 2010// /EdgePreprocessingSettings.cxx/1.2/Sun Dec 30 04:05:15 2007// /EdgePreprocessingSettings.h/1.2/Sun Dec 30 04:05:15 2007// /SmoothBinaryThresholdImageFilter.h/1.2/Sun Dec 30 04:05:15 2007// /SmoothBinaryThresholdImageFilter.txx/1.3/Mon Jun 28 18:45:08 2010// /ThresholdSettings.cxx/1.2/Sun Dec 30 04:05:15 2007// /ThresholdSettings.h/1.2/Sun Dec 30 04:05:15 2007// D itksnap/Logic/Preprocessing/CVS/Repository0000644000076500000240000000003411560342170020155 0ustar paulystaffitksnap/Logic/Preprocessing itksnap/Logic/Preprocessing/CVS/Root0000644000076500000240000000010011560342170016713 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/Preprocessing/EdgePreprocessingImageFilter.h0000644000076500000240000001656511136471735023343 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: EdgePreprocessingImageFilter.h,v $ Language: C++ Date: $Date: 2009/01/24 01:50:21 $ 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 __EdgePreprocessingFilter_h_ #define __EdgePreprocessingFilter_h_ #include "itkCommand.h" #include "itkCastImageFilter.h" #include "itkOrientedImage.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkGradientMagnitudeImageFilter.h" #include "itkImageAdaptor.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkProgressAccumulator.h" #include "itkRescaleIntensityImageFilter.h" #include "itkUnaryFunctorImageFilter.h" #include "EdgePreprocessingSettings.h" /** * The g() function that remaps the gradient magnitude image to the * range 0 to 1. */ template class EdgeRemappingFunctor { public: typedef EdgeRemappingFunctor Self; void SetParameters(float intensityMin, float intensityMax, float exponent, float kappa) { m_KappaFactor = 1.0f / kappa; m_Exponent = exponent; m_IntensityBase = intensityMin; m_IntensityScale = 1.0f / (intensityMax - intensityMin); } inline TOutput operator()(const TInput &x) { float xNorm = (static_cast(x)-m_IntensityBase)*m_IntensityScale; float y = 1.0 / (1.0 + pow(xNorm * m_KappaFactor,m_Exponent)); return static_cast (y); } bool operator ==(const Self &z) { return m_KappaFactor == z.m_KappaFactor && m_IntensityBase == z.m_IntensityBase && m_IntensityScale == z.m_IntensityScale && m_Exponent == z.m_Exponent; } bool operator !=(const Self &z) { return !(*this == z); } private: float m_KappaFactor; float m_IntensityBase; float m_IntensityScale; float m_Exponent; }; /** * \class EdgePreprocessingImageFilter * \brief A filter used for edge preprocessing of images in the IRIS application. * * This functor implements a Gaussian blur, followed by a gradient magnitude * operator, followed by a 'contrast enhancement' intensity remapping filter. */ template class EdgePreprocessingImageFilter: public itk::ImageToImageFilter { public: /** Standard class typedefs. */ typedef EdgePreprocessingImageFilter Self; typedef itk::ImageToImageFilter Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Pixel Type of the input image */ typedef TInputImage InputImageType; typedef typename InputImageType::PixelType InputPixelType; typedef itk::SmartPointer InputImagePointer; /** Pixel Type of the output image */ typedef TOutputImage OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; typedef itk::SmartPointer OutputImagePointer; /** Type used for internal calculations */ typedef float RealType; typedef itk::OrientedImage InternalImageType; typedef itk::SmartPointer InternalImagePointer; /** Functor type used for thresholding */ typedef EdgeRemappingFunctor FunctorType; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); /** Assign new edge processing settings */ void SetEdgePreprocessingSettings(const EdgePreprocessingSettings &settings); protected: EdgePreprocessingImageFilter(); virtual ~EdgePreprocessingImageFilter() {}; void PrintSelf(std::ostream& os, itk::Indent indent) const; /** Generate Data */ void GenerateData( void ); /** * This method maps an input region to an output region. It's necessary to * reflect the way this filter pads the requested region */ void GenerateInputRequestedRegion(); private: /** The unary functor filter type used for remapping */ typedef itk::UnaryFunctorImageFilter< InternalImageType,TOutputImage,FunctorType> RemappingFilterType; typedef typename RemappingFilterType::Pointer RemappingFilterPointer; /** The min / max calculator used to compute gradient range */ typedef itk::MinimumMaximumImageCalculator< InternalImageType> CalculatorType; typedef typename CalculatorType::Pointer CalculatorPointer; /** Adaptor used to cast to float */ typedef itk::CastImageFilter< InputImageType,InternalImageType> CastFilterType; typedef typename CastFilterType::Pointer CastFilterPointer; /** Gaussian smoothing filter */ typedef itk::DiscreteGaussianImageFilter< InternalImageType,InternalImageType> GaussianFilterType; typedef typename GaussianFilterType::Pointer GaussianFilterPointer; /** Gradient magnitude filter */ typedef itk::GradientMagnitudeImageFilter< InternalImageType,InternalImageType> GradientFilterType; typedef typename GradientFilterType::Pointer GradientFilterPointer; /** Intensity rescaling filter */ typedef itk::RescaleIntensityImageFilter< InternalImageType,InternalImageType> RescaleFilterType; typedef typename RescaleFilterType::Pointer RescaleFilterPointer; /** Progress accumulator object */ typedef itk::ProgressAccumulator::Pointer AccumulatorPointer; CastFilterPointer m_CastFilter; RemappingFilterPointer m_RemappingFilter; GaussianFilterPointer m_GaussianFilter; GradientFilterPointer m_GradientFilter; CalculatorPointer m_Calculator; RescaleFilterPointer m_RescaleFilter; EdgePreprocessingSettings m_EdgePreprocessingSettings; /** Progress tracking object */ AccumulatorPointer m_ProgressAccumulator; }; #ifndef ITK_MANUAL_INSTANTIATION #include "EdgePreprocessingImageFilter.txx" #endif #endif // __EdgePreprocessingFilter_h_ itksnap/Logic/Preprocessing/EdgePreprocessingImageFilter.txx0000644000076500000240000001301411412166664023720 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: EdgePreprocessingImageFilter.txx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ 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. =========================================================================*/ template EdgePreprocessingImageFilter ::EdgePreprocessingImageFilter() { // Construct the adaptor m_CastFilter = CastFilterType::New(); m_CastFilter->ReleaseDataFlagOn(); m_CastFilter->SetInput(this->GetInput()); // Construct the Gaussian filter m_GaussianFilter = GaussianFilterType::New(); m_GaussianFilter->SetUseImageSpacing(false); m_GaussianFilter->ReleaseDataFlagOn(); m_GaussianFilter->SetInput(m_CastFilter->GetOutput()); // The gradient magnitude filter m_GradientFilter = GradientFilterType::New(); m_GradientFilter->ReleaseDataFlagOn(); m_GradientFilter->SetInput(m_GaussianFilter->GetOutput()); // The normalization filter m_RescaleFilter = RescaleFilterType::New(); m_RescaleFilter->ReleaseDataFlagOn(); m_RescaleFilter->SetOutputMinimum(0.0f); m_RescaleFilter->SetOutputMaximum(1.0f); m_RescaleFilter->SetInput(m_GradientFilter->GetOutput()); // Construct the Remapping filter m_RemappingFilter = RemappingFilterType::New(); m_RemappingFilter->ReleaseDataFlagOn(); m_RemappingFilter->SetInput(m_RescaleFilter->GetOutput()); // Create the progress accumulator m_ProgressAccumulator = itk::ProgressAccumulator::New(); m_ProgressAccumulator->SetMiniPipelineFilter(this); // Register the filters with the progress accumulator m_ProgressAccumulator->RegisterInternalFilter(m_CastFilter,0.1f); m_ProgressAccumulator->RegisterInternalFilter(m_GaussianFilter,0.6f); m_ProgressAccumulator->RegisterInternalFilter(m_GradientFilter,0.1f); m_ProgressAccumulator->RegisterInternalFilter(m_RescaleFilter,0.1f); m_ProgressAccumulator->RegisterInternalFilter(m_RemappingFilter,0.1f); } template void EdgePreprocessingImageFilter ::GenerateData() { // Get the input and output pointers const typename InputImageType::ConstPointer inputImage = this->GetInput(); typename OutputImageType::Pointer outputImage = this->GetOutput(); // Initialize the progress counter m_ProgressAccumulator->ResetProgress(); // Allocate the output image outputImage->SetBufferedRegion(outputImage->GetRequestedRegion()); outputImage->Allocate(); // Pipe in the input image m_CastFilter->SetInput(inputImage); // Set the variance Vector3f variance( m_EdgePreprocessingSettings.GetGaussianBlurScale() * m_EdgePreprocessingSettings.GetGaussianBlurScale()); m_GaussianFilter->SetVariance(variance.data_block()); // Construct the functor FunctorType functor; functor.SetParameters(0.0f,1.0f, m_EdgePreprocessingSettings.GetRemappingExponent(), m_EdgePreprocessingSettings.GetRemappingSteepness()); // Assign the functor to the filter m_RemappingFilter->SetFunctor(functor); // Call the filter's GenerateData() m_RemappingFilter->GraftOutput(outputImage); m_RemappingFilter->Update(); // graft the mini-pipeline output back onto this filter's output. // this is needed to get the appropriate regions passed back. GraftOutput( m_RemappingFilter->GetOutput() ); } template void EdgePreprocessingImageFilter ::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os,indent); } template void EdgePreprocessingImageFilter ::SetEdgePreprocessingSettings(const EdgePreprocessingSettings &settings) { if(!(settings == m_EdgePreprocessingSettings)) { m_EdgePreprocessingSettings = settings; this->Modified(); } } template void EdgePreprocessingImageFilter ::GenerateInputRequestedRegion() { // Make sure we call the parent's implementation Superclass::GenerateInputRequestedRegion(); // Get pointers to the input and output InputImagePointer inputPtr = const_cast< TInputImage * >( this->GetInput() ); OutputImagePointer outputPtr = this->GetOutput(); // Use the largest possible region (hack) inputPtr->SetRequestedRegion( inputPtr->GetLargestPossibleRegion()); } itksnap/Logic/Preprocessing/EdgePreprocessingSettings.cxx0000644000076500000240000000376010735614373023320 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: EdgePreprocessingSettings.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 "EdgePreprocessingSettings.h" bool EdgePreprocessingSettings ::operator == (const EdgePreprocessingSettings &other) const { return memcmp(this,&other,sizeof(EdgePreprocessingSettings)) == 0; } EdgePreprocessingSettings:: EdgePreprocessingSettings() { SetGaussianBlurScale(1.0f); SetRemappingSteepness(0.1f); SetRemappingExponent(2.0f); } EdgePreprocessingSettings:: ~EdgePreprocessingSettings() { /*Needs to be defined to avoid compiler warning about needing a virtual destructor*/}; EdgePreprocessingSettings EdgePreprocessingSettings ::MakeDefaultSettings() { EdgePreprocessingSettings settings; return settings; } itksnap/Logic/Preprocessing/EdgePreprocessingSettings.h0000644000076500000240000000462110735614373022742 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: EdgePreprocessingSettings.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 __EdgePreprocessingSettings_h_ #define __EdgePreprocessingSettings_h_ #include "SNAPCommon.h" // Forward references class GreyImageWrapper; /** * This class encapsulates the thresholding settings used by the program. * for In/Out snake initialization */ class EdgePreprocessingSettings { public: irisGetMacro(GaussianBlurScale,float); irisSetMacro(GaussianBlurScale,float); irisGetMacro(RemappingSteepness,float); irisSetMacro(RemappingSteepness,float); irisGetMacro(RemappingExponent,float); irisSetMacro(RemappingExponent,float); /** Compare two sets of settings */ bool operator == (const EdgePreprocessingSettings &other) const; /** * Create a default instance of the settings based on an image wrapper */ static EdgePreprocessingSettings MakeDefaultSettings(); // Constructor EdgePreprocessingSettings(); // Destructor virtual ~EdgePreprocessingSettings(); private: float m_GaussianBlurScale; float m_RemappingSteepness; float m_RemappingExponent; }; #endif // __EdgePreprocessingSettings_h_ itksnap/Logic/Preprocessing/SmoothBinaryThresholdImageFilter.h0000644000076500000240000001647610735614373024230 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SmoothBinaryThresholdImageFilter.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 __SmoothBinaryThresholdImageFilter_h_ #define __SmoothBinaryThresholdImageFilter_h_ #include "itkCommand.h" #include "itkUnaryFunctorImageFilter.h" #include "itkMinimumMaximumImageCalculator.h" #include "itkProgressAccumulator.h" #include "ThresholdSettings.h" /** * A functor used for the smooth threshold operation on images. * Used in conjuction with itk::UnaryFunctorImageFilter. */ template class SmoothBinaryThresholdFunctor { public: typedef SmoothBinaryThresholdFunctor Self; /** * Initialize the function */ void SetParameters(TInput,TInput, const ThresholdSettings &settings) { // At least one threshold should be used assert(settings.IsLowerThresholdEnabled() || settings.IsUpperThresholdEnabled()); // Store the upper and lower bounds m_LowerThreshold = settings.GetLowerThreshold(); m_UpperThreshold = settings.GetUpperThreshold(); // Handle the bad case: everything is mapped to zero if(m_LowerThreshold >= m_UpperThreshold) { m_ScalingFactor = m_FactorUpper = m_FactorLower = m_Shift = 0.0; return; } // Compute the largest scaling for U-L such that the function is greater // than 1-eps float eps = pow((float)10,-(float)settings.GetSmoothness()); float maxScaling = (m_UpperThreshold - m_LowerThreshold) / log((2-eps)/eps); // Set the factor by which the input is multiplied // m_ScalingFactor = settings.GetSmoothness(); m_ScalingFactor = 1 / maxScaling; // Combine the usage and inversion flags to get the scaling factors m_FactorLower = settings.IsLowerThresholdEnabled() ? 1.0f : 0.0f; m_FactorUpper = settings.IsUpperThresholdEnabled() ? 1.0f : 0.0f; // Compute the shift m_Shift = 1.0f - (m_FactorLower + m_FactorUpper); } /** * Apply the function to image intensity */ TOutput operator()(const TInput &x) { // Cast the input to float float z = static_cast(x); // Compute the left component float yLower = m_FactorLower * tanh((z-m_LowerThreshold) * m_ScalingFactor); // Compute the right component float yUpper = m_FactorUpper * tanh((m_UpperThreshold-z) * m_ScalingFactor); // Return the result (TODO: hack) return static_cast(yLower + yUpper + m_Shift); } bool operator ==(const Self &z) { return m_LowerThreshold == z.m_LowerThreshold && m_UpperThreshold == z.m_UpperThreshold && m_ScalingFactor == z.m_ScalingFactor && m_FactorLower == z.m_FactorLower && m_FactorUpper == z.m_FactorUpper && m_Shift == z.m_Shift; } bool operator !=(const Self &x) { return !((*this) == x); } private: // The lower threshold in intensity units float m_LowerThreshold; // The upper threshold in intensity units float m_UpperThreshold; // The scaling factor that incorporates the smoothness parameter and the // intensity range of the input image float m_ScalingFactor; // The multiplier applied to the left/right threshold. This can be set to // 0 and 1 depending on the other threshold float m_FactorLower; float m_FactorUpper; // The shift amount added to the sum of the left and right // thresholded intensities to force them into [-1,1] float m_Shift; }; /** * \class SmoothBinaryThresholdFunctor * \brief A filter used to perform binary thresholding to produce SNAP speed images. * * This filter uses a sigmoid function as a smooth threshold */ template class SmoothBinaryThresholdImageFilter: public itk::ImageToImageFilter { public: /** Standard class typedefs. */ typedef SmoothBinaryThresholdImageFilter Self; typedef itk::ImageToImageFilter Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Pixel Type of the input image */ typedef TInputImage InputImageType; typedef typename InputImageType::PixelType InputPixelType; /** Pixel Type of the output image */ typedef TOutputImage OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; /** Functor type used for thresholding */ typedef SmoothBinaryThresholdFunctor< InputPixelType,OutputPixelType> FunctorType; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Image dimension. */ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); /** Assign threshold settings */ void SetThresholdSettings(const ThresholdSettings &settings); protected: SmoothBinaryThresholdImageFilter(); virtual ~SmoothBinaryThresholdImageFilter() {}; void PrintSelf(std::ostream& os, itk::Indent indent) const; /** Generate Data */ void GenerateData( void ); private: /** The unary functor filter type used for remapping */ typedef itk::UnaryFunctorImageFilter< TInputImage,TOutputImage,FunctorType> ThresholdFilterType; typedef typename ThresholdFilterType::Pointer ThresholdFilterPointer; /** The min / max calculator used to compute input range */ typedef itk::MinimumMaximumImageCalculator CalculatorType; typedef typename CalculatorType::Pointer CalculatorPointer; /** Progress accumulator object */ typedef itk::ProgressAccumulator::Pointer AccumulatorPointer; ThresholdFilterPointer m_ThresholdFilter; CalculatorPointer m_Calculator; ThresholdSettings m_ThresholdSettings; AccumulatorPointer m_ProgressAccumulator; }; #ifndef ITK_MANUAL_INSTANTIATION #include "SmoothBinaryThresholdImageFilter.txx" #endif #endif // __SmoothBinaryThresholdImageFilter_h_ itksnap/Logic/Preprocessing/SmoothBinaryThresholdImageFilter.txx0000644000076500000240000000721511412166664024611 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SmoothBinaryThresholdImageFilter.txx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ 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. =========================================================================*/ template SmoothBinaryThresholdImageFilter ::SmoothBinaryThresholdImageFilter() { // Construct the mini-pipeline m_Calculator = CalculatorType::New(); m_ThresholdFilter = ThresholdFilterType::New(); // Initialize the progress tracker m_ProgressAccumulator = itk::ProgressAccumulator::New(); m_ProgressAccumulator->SetMiniPipelineFilter(this); // Add the filters to the progress accumulator (just one filter) m_ProgressAccumulator->RegisterInternalFilter(m_ThresholdFilter,1.0f); } template void SmoothBinaryThresholdImageFilter ::GenerateData() { // Get the input and output pointers const typename InputImageType::ConstPointer inputImage = this->GetInput(); typename OutputImageType::Pointer outputImage = this->GetOutput(); // Allocate the output image outputImage->SetBufferedRegion(outputImage->GetRequestedRegion()); outputImage->Allocate(); // Reset the progress m_ProgressAccumulator->ResetProgress(); // Compute the min/max of the image m_Calculator->SetImage(inputImage); m_Calculator->Compute(); // Construct the functor FunctorType functor; functor.SetParameters(m_Calculator->GetMinimum(), m_Calculator->GetMaximum(), m_ThresholdSettings); // Assign the functor to the filter m_ThresholdFilter->SetInput(inputImage); m_ThresholdFilter->SetFunctor(functor); // Call the filter's GenerateData() m_ThresholdFilter->GraftOutput(outputImage); m_ThresholdFilter->Update(); // graft the mini-pipeline output back onto this filter's output. // this is needed to get the appropriate regions passed back. GraftOutput( m_ThresholdFilter->GetOutput() ); } template void SmoothBinaryThresholdImageFilter ::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os,indent); } template void SmoothBinaryThresholdImageFilter ::SetThresholdSettings(const ThresholdSettings &settings) { if(!(settings == m_ThresholdSettings)) { m_ThresholdSettings = settings; this->Modified(); } } itksnap/Logic/Preprocessing/ThresholdSettings.cxx0000644000076500000240000000563010735614373021642 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ThresholdSettings.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 "ThresholdSettings.h" #include "GreyImageWrapper.h" bool ThresholdSettings ::operator == (const ThresholdSettings &other) const { return memcmp(this,&other,sizeof(ThresholdSettings)) == 0; } ThresholdSettings ThresholdSettings ::MakeDefaultSettings(GreyImageWrapper *wrapper) { // Use the min and the max of the wrapper int iMin = wrapper->GetImageMin(); int iMax = wrapper->GetImageMax(); // If the image is constant, return default settings if(iMin == iMax) return MakeDefaultSettingsWithoutImage(); // Generate the default settings ThresholdSettings settings; settings.m_LowerThresholdEnabled = true; settings.m_UpperThresholdEnabled = true; settings.m_LowerThreshold = iMin + (iMax-iMin) / 3.0; settings.m_UpperThreshold = iMin + 2.0 * (iMax-iMin) / 3.0; settings.m_Smoothness = 3.0; return settings; } ThresholdSettings ThresholdSettings ::MakeDefaultSettingsWithoutImage() { ThresholdSettings settings; settings.m_LowerThresholdEnabled = true; settings.m_UpperThresholdEnabled = true; settings.m_LowerThreshold = 40.0; settings.m_UpperThreshold = 80.0; settings.m_Smoothness = 3.0; return settings; } ThresholdSettings ::ThresholdSettings() { m_LowerThresholdEnabled = true; m_UpperThresholdEnabled = true; m_LowerThreshold = 0.0; m_UpperThreshold = 1.0; m_Smoothness = 1.0; } bool ThresholdSettings ::IsValid() const { if(m_Smoothness < 0.0) return false; if(m_LowerThresholdEnabled && m_UpperThresholdEnabled && m_UpperThreshold <= m_LowerThreshold) return false; return true; } itksnap/Logic/Preprocessing/ThresholdSettings.h0000644000076500000240000000560110735614373021265 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ThresholdSettings.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 __ThresholdSettings_h_ #define __ThresholdSettings_h_ #include "SNAPCommon.h" // Forward references class GreyImageWrapper; /** * This class encapsulates the thresholding settings used by the program. * for In/Out snake initialization */ class ThresholdSettings { public: virtual ~ThresholdSettings() { /*To avoid compiler warning.*/ } irisGetMacro(LowerThreshold, float); irisSetMacro(LowerThreshold, float); irisGetMacro(UpperThreshold, float); irisSetMacro(UpperThreshold, float); irisGetMacro(Smoothness,float); irisSetMacro(Smoothness,float); irisIsMacro(LowerThresholdEnabled); irisSetMacro(LowerThresholdEnabled,bool); irisIsMacro(UpperThresholdEnabled); irisSetMacro(UpperThresholdEnabled,bool); /** Compare two sets of settings */ bool operator == (const ThresholdSettings &other) const; /** Check if the settings are valid */ bool IsValid() const; /** * Create a default instance of the settings based on an image wrapper */ static ThresholdSettings MakeDefaultSettings(GreyImageWrapper *wrapper); /** This will create a slightly less useful settings object with thresholds * at 40 and 100. Before using them, make sure that the image is in range * of 40 and 100 */ static ThresholdSettings MakeDefaultSettingsWithoutImage(); // Constructor: creates dummy settings ThresholdSettings(); private: float m_LowerThreshold; float m_UpperThreshold; float m_Smoothness; bool m_UpperThresholdEnabled; bool m_LowerThresholdEnabled; }; #endif // __ThresholdSettings_h_ itksnap/Logic/Processing/0000755000076500000240000000000011560342170014714 5ustar paulystaffitksnap/Logic/Processing/CVS/0000755000076500000240000000000011560342170015347 5ustar paulystaffitksnap/Logic/Processing/CVS/Entries0000644000076500000240000000000211560342170016673 0ustar paulystaffD itksnap/Logic/Processing/CVS/Repository0000644000076500000240000000003111560342170017443 0ustar paulystaffitksnap/Logic/Processing itksnap/Logic/Processing/CVS/Root0000644000076500000240000000010011560342170016204 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/Slicing/0000755000076500000240000000000011560342170014170 5ustar paulystaffitksnap/Logic/Slicing/CVS/0000755000076500000240000000000011560342170014623 5ustar paulystaffitksnap/Logic/Slicing/CVS/Entries0000644000076500000240000000055411560342170016163 0ustar paulystaff/IRISSlicer.h/1.3/Sun Dec 30 04:05:15 2007// /IRISSlicer.txx/1.6/Sun Dec 30 04:05:15 2007// /IntensityCurveInterface.h/1.3/Thu Sep 10 19:44:50 2009// /IntensityCurveVTK.cxx/1.3/Wed Sep 16 20:03:13 2009// /IntensityCurveVTK.h/1.4/Mon Sep 21 21:54:21 2009// /UnaryFunctorCache.h/1.3/Wed Sep 16 20:03:13 2009// /UnaryFunctorCache.txx/1.5/Sat Sep 19 07:47:03 2009// D itksnap/Logic/Slicing/CVS/Repository0000644000076500000240000000002611560342170016723 0ustar paulystaffitksnap/Logic/Slicing itksnap/Logic/Slicing/CVS/Root0000644000076500000240000000010011560342170015460 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Logic/Slicing/IntensityCurveInterface.h0000644000076500000240000000672611252253462021173 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IntensityCurveInterface.h,v $ Language: C++ Date: $Date: 2009/09/10 19:44:50 $ 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 __IntensityCurveInterface_h_ #define __IntensityCurveInterface_h_ #include #include /** * \class IntensityCurveInterface * \brief The base class for intensity mapping splines */ class ITK_EXPORT IntensityCurveInterface : public itk::FunctionBase { public: /** Standard class typedefs. */ typedef IntensityCurveInterface Self; typedef itk::FunctionBase Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(IntensityCurveInterface,itk::FunctionBase); /** * Initialize the spline with initial number of control points. * The spline will be a linear mapping from (0,0) to (1,1) */ virtual void Initialize(unsigned int nControlPoints = 3) = 0; /** * Get the value of a control point */ virtual void GetControlPoint(unsigned int iControlPoint, float &t, float &x) const = 0; /** * Get the value of a control point */ virtual unsigned int GetControlPointCount() const = 0; /** * Update the value of a control point */ virtual void UpdateControlPoint(unsigned int iControlPoint, float t, float x) = 0; /** * This method linearly maps the t-values of all the control points to * the range between tMin and tMax. It's used for intensity windowing * where we want to adjust the domain of the curve without changing its * shape */ virtual void ScaleControlPointsToWindow(float tMin, float tMax) = 0; /** * Check the monotonicity of the spline curve */ virtual bool IsMonotonic() const = 0; /** Load the curve from a registry object */ virtual void LoadFromRegistry(Registry ®istry) = 0; /** Save the curve to a registry object */ virtual void SaveToRegistry(Registry ®istry) const = 0; protected: IntensityCurveInterface(){}; virtual ~IntensityCurveInterface(){}; private: IntensityCurveInterface(const Self& ); //purposely not implemented void operator=(const Self& ); //purposely not implemented }; #endif // __IntensityCurveInterface_h_ itksnap/Logic/Slicing/IntensityCurveVTK.cxx0000644000076500000240000001277711254242201020304 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IntensityCurveVTK.cxx,v $ Language: C++ Date: $Date: 2009/09/16 20:03:13 $ 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. =========================================================================*/ #include "IntensityCurveVTK.h" #include "SNAPCommon.h" #include using namespace std; IntensityCurveVTK ::IntensityCurveVTK() { m_Spline = vtkKochanekSpline::New(); m_Spline->SetLeftConstraint(2); m_Spline->SetRightConstraint(2); m_Spline->SetDefaultContinuity(-1); m_Spline->SetDefaultTension(0); m_Spline->SetDefaultBias(0); } IntensityCurveVTK ::~IntensityCurveVTK() { m_Spline->Delete(); } void IntensityCurveVTK ::Initialize(unsigned int nControlPoints) { // Set up the intervals for the control points float interval = 1.0 / (nControlPoints - 1); float t = 0; // Initialize the control points m_ControlPoints.clear(); m_Spline->RemoveAllPoints(); for(unsigned int i=0;iAddPoint(t,t); } m_Spline->Compute(); } void IntensityCurveVTK ::GetControlPoint(unsigned int iControlPoint,float &t,float &x) const { assert(iControlPoint < m_ControlPoints.size()); t = m_ControlPoints[iControlPoint].t; x = m_ControlPoints[iControlPoint].x; } void IntensityCurveVTK ::UpdateControlPoint(unsigned int iControlPoint, float t, float x) { assert(iControlPoint < m_ControlPoints.size()); // Update the control point of interest m_ControlPoints[iControlPoint].t = t; m_ControlPoints[iControlPoint].x = x; // Oops, we have to do this the hard way m_Spline->RemoveAllPoints(); for(IteratorType it = m_ControlPoints.begin();it!=m_ControlPoints.end();it++) { m_Spline->AddPoint(it->t,it->x); } m_Spline->Compute(); } bool IntensityCurveVTK ::IsMonotonic() const { // The simple thing is to check each interval a bunch of // times. There ought to be an upper limit on the length of non-monotonic // regions. unsigned int nRegions = 8; for(unsigned int i=0;iEvaluate(t) >= m_Spline->Evaluate(t1)) return false; t = t1; t1 += tStep; } } return true; } void IntensityCurveVTK ::ScaleControlPointsToWindow(float tMin, float tMax) { assert(tMin < tMax); // Get the current range float t1 = m_ControlPoints.front().t; float tn = m_ControlPoints.back().t; // Compute coefficients of the mapping t' = b t + k float b = (tMax - tMin) / (tn - t1); float k = tMin - b * t1; m_Spline->RemoveAllPoints(); for(IteratorType it = m_ControlPoints.begin();it != m_ControlPoints.end();it++) { it->t = it->t * b + k; m_Spline->AddPoint(it->t,it->x); } m_Spline->Compute(); } void IntensityCurveVTK ::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Spline: " << m_Spline << std::endl; } void IntensityCurveVTK ::LoadFromRegistry(Registry ®istry) { // Read the number of control points size_t nPoints = registry["NumberOfControlPoints"][3]; this->Initialize(nPoints); // Load each of the control point for(size_t iPoint = 0; iPoint < nPoints; iPoint++) { // Get the default values, just in case float t0, x0; this->GetControlPoint(iPoint, t0, x0); // Read the registry Registry &folder = registry.Folder(registry.Key("ControlPoint[%d]",iPoint)); float t = (float) folder["tValue"][(double) t0]; float x = (float) folder["xValue"][(double) x0]; // Set the control point this->UpdateControlPoint(iPoint, t, x); } } void IntensityCurveVTK ::SaveToRegistry(Registry ®istry) const { // Store the number of control points registry["NumberOfControlPoints"] << this->GetControlPointCount(); // Save each control point for(size_t iPoint = 0; iPoint < this->GetControlPointCount(); iPoint++) { // Get the current values, just in case float t, x; this->GetControlPoint(iPoint, t, x); // Create a folder in the registry string key = registry.Key("ControlPoint[%d]",iPoint); Registry &folder = registry.Folder(key); folder["tValue"] << (double) t; folder["xValue"] << (double) x; } } itksnap/Logic/Slicing/IntensityCurveVTK.h0000644000076500000240000000666011255773015017740 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IntensityCurveVTK.h,v $ Language: C++ Date: $Date: 2009/09/21 21:54:21 $ 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 __IntensityCurveVTK_h_ #define __IntensityCurveVTK_h_ #include #include #include /** * \class IntensityCurveVTK * \brief The spline intensity mapping based on the VTK spline class. */ class ITK_EXPORT IntensityCurveVTK : public IntensityCurveInterface { public: /** Standard class typedefs. */ typedef IntensityCurveVTK Self; typedef IntensityCurveInterface Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Run-time type information (and related methods). */ itkTypeMacro(IntensityCurveVTK,IntensityCurveInterface); /** New method */ itkNewMacro(Self); // Defined in the parent class void Initialize(unsigned int nControlPoints = 3); void GetControlPoint(unsigned int iControlPoint, float &t, float &x) const; void UpdateControlPoint(unsigned int iControlPoint, float t, float x); bool IsMonotonic() const; void ScaleControlPointsToWindow(float tMin, float tMax); unsigned int GetControlPointCount() const { return m_ControlPoints.size(); } // Evaluate the curve float Evaluate(const float &t) const { if(t < m_ControlPoints.front().t) return -.000001; else if(t > m_ControlPoints.back().t) return 1.000001; else return m_Spline->Evaluate(t); } // Load the curve from a registry void LoadFromRegistry(Registry ®istry); // Save the curve to a registry void SaveToRegistry(Registry ®istry) const; protected: IntensityCurveVTK(); virtual ~IntensityCurveVTK(); void PrintSelf(std::ostream &s, itk::Indent indent) const; private: IntensityCurveVTK(const Self& ); //purposely not implemented void operator=(const Self& ); //purposely not implemented // The spline object vtkKochanekSpline *m_Spline; // Control point structure struct ControlPoint { float t; float x; }; // A storage for the control points std::vector m_ControlPoints; typedef std::vector::iterator IteratorType; }; #endif // __IntensityCurveVTK_h_ itksnap/Logic/Slicing/IRISSlicer.h0000644000076500000240000001427410735614373016273 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISSlicer.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 __IRISSlicer_h_ #define __IRISSlicer_h_ #include #include #include #include #include /** * \class IRISSlicer * \brief A slice extraction filter for 3D images. * * This filter takes a transform * from image space (x=pixel, y=line, z=slice) to display space. A slice * shows either x-y, y-z, or x-z in the display space. This filter is necessary * because the origin in the slice can correspond to any corner of the image, not * just to the origin of the image. This filter can traverse the image in different * directions, accomodating different positions of the display space origin in the * image space. */ template class ITK_EXPORT IRISSlicer : public itk::ImageToImageFilter, itk::Image > { public: /** Standard class typedefs. */ typedef IRISSlicer Self; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::ImageToImageFilter Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(IRISSlicer, ImageToImageFilter); /** Some typedefs. */ typedef typename InputImageType::ConstPointer InputImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename OutputImageType::RegionType OutputImageRegionType; typedef itk::ImageSliceConstIteratorWithIndex InputIteratorType; typedef itk::ImageRegionIteratorWithIndex SimpleOutputIteratorType; typedef itk::ImageLinearIteratorWithIndex OutputIteratorType; /** Set the current slice index */ itkSetMacro(SliceIndex,unsigned int); itkGetMacro(SliceIndex,unsigned int); /** Set the image axis along which the subsequent slices lie */ itkSetMacro(SliceDirectionImageAxis,unsigned int); itkGetMacro(SliceDirectionImageAxis,unsigned int); /** Set the image axis along which the subsequent lines in a slice lie */ itkSetMacro(LineDirectionImageAxis,unsigned int); itkGetMacro(LineDirectionImageAxis,unsigned int); /** Set the image axis along which the subsequent pixels in a line lie */ itkSetMacro(PixelDirectionImageAxis,unsigned int); itkGetMacro(PixelDirectionImageAxis,unsigned int); /** Set the direction of line traversal */ itkSetMacro(LineTraverseForward,bool); itkGetMacro(LineTraverseForward,bool); /** Set the direction of pixel traversal */ itkSetMacro(PixelTraverseForward,bool); itkGetMacro(PixelTraverseForward,bool); protected: IRISSlicer(); virtual ~IRISSlicer() {}; void PrintSelf(std::ostream &s, itk::Indent indent) const; /** * IRISSlicer can produce an image which is a different * resolution than its input image. As such, IRISSlicer * needs to provide an implementation for * GenerateOutputInformation() in order to inform the pipeline * execution model. The original documentation of this method is * below. * * \sa ProcessObject::GenerateOutputInformaton() */ virtual void GenerateOutputInformation(); /** * This method maps an input region to an output region */ virtual void CallCopyOutputRegionToInputRegion(InputImageRegionType &destRegion, const OutputImageRegionType &srcRegion); /** * IRISSlicer is not implemented as a multithreaded filter. * \sa ImageToImageFilter::GenerateData() */ virtual void GenerateData(); private: IRISSlicer(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented // Current slice in each of the dimensions unsigned int m_SliceIndex; // Image axis corresponding to the slice direction unsigned int m_SliceDirectionImageAxis; // Image axis corresponding to the line direction unsigned int m_LineDirectionImageAxis; // Image axis corresponding to the pixel direction unsigned int m_PixelDirectionImageAxis; // Whether the line direction is reversed bool m_LineTraverseForward; // Whether the pixel direction is reversed bool m_PixelTraverseForward; // The worker methods in this filter // void CopySliceLineForwardPixelForward(InputIteratorType, OutputImageType *); // void CopySliceLineForwardPixelBackward(InputIteratorType, OutputImageType *); // void CopySliceLineBackwardPixelForward(InputIteratorType, OutputImageType *); // void CopySliceLineBackwardPixelBackward(InputIteratorType, OutputImageType *); }; #ifndef ITK_MANUAL_INSTANTIATION #include "IRISSlicer.txx" #endif #endif //__IRISSlicer_h_ itksnap/Logic/Slicing/IRISSlicer.txx0000644000076500000240000003372210735614373016666 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISSlicer.txx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ Version: $Revision: 1.6 $ 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. =========================================================================*/ template IRISSlicer ::IRISSlicer() { // There is a single input to the filter this->SetNumberOfRequiredInputs(1); // There are three outputs from the filter this->SetNumberOfRequiredOutputs(1); // Initialize the slice to be along the z-direction in the image m_SliceDirectionImageAxis = 2; m_LineDirectionImageAxis = 1; m_PixelDirectionImageAxis = 0; m_PixelTraverseForward = true; m_LineTraverseForward = true; // Initialize to a zero slice index m_SliceIndex = 0; } template void IRISSlicer ::GenerateOutputInformation() { // Get pointers to the inputs and outputs typename Superclass::InputImageConstPointer inputPtr = this->GetInput(); typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); // The inputs and outputs should exist if (!outputPtr || !inputPtr) return; // Get the input's largest possible region InputImageRegionType inputRegion = inputPtr->GetLargestPossibleRegion(); // Arrays to specify the output spacing and origin double outputSpacing[2]; double outputOrigin[2] = {0.0,0.0}; // Initialize the output image region OutputImageRegionType outputRegion; outputRegion.SetIndex(0,inputRegion.GetIndex(m_PixelDirectionImageAxis)); outputRegion.SetSize(0,inputRegion.GetSize(m_PixelDirectionImageAxis)); outputRegion.SetIndex(1,inputRegion.GetIndex(m_LineDirectionImageAxis)); outputRegion.SetSize(1,inputRegion.GetSize(m_LineDirectionImageAxis)); // Set the origin and spacing outputSpacing[0] = inputPtr->GetSpacing()[m_PixelDirectionImageAxis]; outputSpacing[1] = inputPtr->GetSpacing()[m_LineDirectionImageAxis]; // Set the region of the output slice outputPtr->SetLargestPossibleRegion(outputRegion); // Set the spacing and origin outputPtr->SetSpacing(outputSpacing); outputPtr->SetOrigin(outputOrigin); } template void IRISSlicer ::CallCopyOutputRegionToInputRegion(InputImageRegionType &destRegion, const OutputImageRegionType &srcRegion) { // Set the size of the region to 1 in the slice direction destRegion.SetSize(m_SliceDirectionImageAxis,1); // Set the index of the region in that dimension to the number of the slice destRegion.SetIndex(m_SliceDirectionImageAxis,m_SliceIndex); // Compute the bounds of the input region for the other two dimensions (for // the case when the output region is not equal to the largest possible // region (i.e., we are requesting a partial slice) // The size of the region does not depend of the direction of axis // traversal destRegion.SetSize(m_PixelDirectionImageAxis,srcRegion.GetSize(0)); destRegion.SetSize(m_LineDirectionImageAxis,srcRegion.GetSize(1)); // However, the index of the region does depend on the direction! if(m_PixelTraverseForward) { destRegion.SetIndex(m_PixelDirectionImageAxis,srcRegion.GetIndex(0)); } else { // This case is a bit trickier. The axis direction is reversed, so // range [i,...,i+s-1] in the output image corresponds to the range // [S-(i+s),S-(i+1)] in the input image, where i is the in-slice index, // S is the largest size of the input and s is the requested size of the // output destRegion.SetIndex( m_PixelDirectionImageAxis, this->GetInput()->GetLargestPossibleRegion().GetSize(m_PixelDirectionImageAxis) - (srcRegion.GetIndex(0) + srcRegion.GetSize(0))); } // Same as above for line index if(m_LineTraverseForward) { destRegion.SetIndex(m_LineDirectionImageAxis,srcRegion.GetIndex(1)); } else { destRegion.SetIndex( m_LineDirectionImageAxis, this->GetInput()->GetLargestPossibleRegion().GetSize(m_LineDirectionImageAxis) - (srcRegion.GetIndex(1) + srcRegion.GetSize(1))); } } /* template void IRISSlicer ::GenerateData() { // Here's the input and output InputImagePointer inputPtr = this->GetInput(); OutputImagePointer outputPtr = this->GetOutput(); // Allocate (why is this necessary?) this->AllocateOutputs(); // Compute the region in the image for which the slice is being extracted InputImageRegionType inputRegion = inputPtr->GetRequestedRegion(); // Create an iterator that will parse the desired slice in the image InputIteratorType it(inputPtr,inputRegion); it.SetFirstDirection(m_PixelDirectionImageAxis); it.SetSecondDirection(m_LineDirectionImageAxis); // Copy the contents using a different method, depending on the axes directions // The direction with index one is the order in which the lines are traversed and // the direction with index two is the order in which the pixels are traversed. if(m_PixelTraverseForward) if(m_LineTraverseForward) CopySliceLineForwardPixelForward(it,outputPtr); else CopySliceLineBackwardPixelForward(it,outputPtr); else if(m_LineTraverseForward) CopySliceLineForwardPixelBackward(it,outputPtr); else CopySliceLineBackwardPixelBackward(it,outputPtr); } */ template void IRISSlicer ::GenerateData() { // Here's the input and output InputImagePointer inputPtr = this->GetInput(); OutputImagePointer outputPtr = this->GetOutput(); // Allocate (why is this necessary?) this->AllocateOutputs(); // Get the image dimensions typename InputImageType::SizeType szVol = inputPtr->GetBufferedRegion().GetSize(); // Set the strides in image coordinates Vector3i stride_image(1, szVol[0], szVol[1] * szVol[0]); // Determine the strides for the pixel step and line step int sPixel = (m_PixelTraverseForward ? 1 : -1) * stride_image[m_PixelDirectionImageAxis]; int sLine = (m_LineTraverseForward ? 1 : -1) * stride_image[m_LineDirectionImageAxis]; // We never take full line-strides, because as we iterate, we // take n pixel-strides before needing to worry about changing // the line. Therefore, we compute the step needed to go to the // start of next line after taking n pixel-strides int sRowOfPixels = sPixel * szVol[m_PixelDirectionImageAxis]; int sLineDelta = sLine - sRowOfPixels; // Determine the first voxel that we will traverse Vector3i xStartVoxel; xStartVoxel[m_PixelDirectionImageAxis] = m_PixelTraverseForward ? 0 : szVol[m_PixelDirectionImageAxis] - 1; xStartVoxel[m_LineDirectionImageAxis] = m_LineTraverseForward ? 0 : szVol[m_LineDirectionImageAxis] - 1; xStartVoxel[m_SliceDirectionImageAxis] = szVol[m_SliceDirectionImageAxis] == 1 ? 0 : m_SliceIndex; // Get the offset of the first voxel size_t iStart = dot_product(stride_image, xStartVoxel); // Get the size of the output region (whole slice) typename OutputImageType::RegionType rgn = outputPtr->GetBufferedRegion(); size_t nPixel = rgn.GetSize()[0], nLine = rgn.GetSize()[1]; // Get pointers to input and output data const TPixel *pSource = inputPtr->GetBufferPointer(); TPixel *pTarget = outputPtr->GetBufferPointer(); // Position the source pSource += iStart; // Main loop: copy data from source to target for(size_t il = 0; il < nLine; il++) { for(size_t ip = 0; ip < nPixel; ip++) { *pTarget = *pSource; pTarget++; pSource+=sPixel; } pSource += sLineDelta; } } template void IRISSlicer ::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Slice Image Axis: " << m_SliceDirectionImageAxis << std::endl; os << indent << "Slice Index: " << m_SliceIndex << std::endl; os << indent << "Line Image Axis: " << m_LineDirectionImageAxis << std::endl; os << indent << "Lines Traversed Forward: " << m_LineTraverseForward << std::endl; os << indent << "Pixel Image Axis: " << m_PixelDirectionImageAxis << std::endl; os << indent << "Pixels Traversed Forward: " << m_PixelTraverseForward << std::endl; } /* // Traverse pixels forward and lines forward template void IRISSlicer ::CopySliceLineForwardPixelForward(InputIteratorType itImage, OutputImageType *outputPtr) { // Create an simple iterator for the slice SimpleOutputIteratorType itSlice(outputPtr,outputPtr->GetRequestedRegion()); // Position both iterators at the beginning itSlice.GoToBegin(); itImage.GoToBegin(); // Parse over lines while(!itSlice.IsAtEnd()) { // Parse over pixels while(!itImage.IsAtEndOfLine()) { itSlice.Set(itImage.Value()); ++itSlice; ++itImage; } // Position the image iterator at the next line itImage.NextLine(); } } // Traverse pixels forward and lines backward template void IRISSlicer ::CopySliceLineBackwardPixelForward(InputIteratorType itImage, OutputImageType *outputPtr) { // Create an iterator into the slice itself OutputIteratorType itSlice(outputPtr,outputPtr->GetRequestedRegion()); itSlice.SetDirection(0); // Go to the last line itSlice.GoToReverseBegin(); itImage.GoToBegin(); // Parse over lines while(!itSlice.IsAtReverseEnd()) { // Position the slice iterator at the first pixel of the line itSlice.GoToBeginOfLine(); // Scan in forward until the end of line while(!itSlice.IsAtEndOfLine()) { itSlice.Set(itImage.Value()); ++itSlice; ++itImage; } // Step over to the previous line itSlice.PreviousLine(); itImage.NextLine(); } } // Traverse pixels backward and lines forward template void IRISSlicer ::CopySliceLineForwardPixelBackward(InputIteratorType itImage, OutputImageType *outputPtr) { // Create an iterator into the slice itself OutputIteratorType itSlice(outputPtr,outputPtr->GetRequestedRegion()); itSlice.SetDirection(0); // Go to start of the last line itSlice.GoToBegin(); itImage.GoToBegin(); // Parse over lines while(!itSlice.IsAtEnd()) { // Position the slice iterator at the last pixel of the line itSlice.GoToEndOfLine(); --itSlice; // Scan in backwards until we're past the beginning of the line while(!itSlice.IsAtReverseEndOfLine()) { itSlice.Set(itImage.Value()); --itSlice; ++itImage; } // Step over to the next line itSlice.NextLine(); itImage.NextLine(); } } // Traverse pixels backward and lines backward template void IRISSlicer ::CopySliceLineBackwardPixelBackward(InputIteratorType itImage, OutputImageType *outputPtr) { // Create an simple iterator for the slice SimpleOutputIteratorType itSlice(outputPtr,outputPtr->GetRequestedRegion()); // Position both iterators at the beginning itSlice.GoToReverseBegin(); itImage.GoToBegin(); // Parse over lines while(!itSlice.IsAtReverseEnd()) { // Parse over pixels while(!itImage.IsAtEndOfLine()) { itSlice.Set(itImage.Value()); --itSlice; ++itImage; } // Position the image iterator at the next line itImage.NextLine(); } } */ /* template void IRISSlicer ::CopySlice(InputIteratorType itImage, OutputIteratorType itSlice, int sliceDir, int lineDir) { if (sliceDir > 0 && lineDir > 0) { itImage.GoToBegin(); while (!itImage.IsAtEndOfSlice()) { while (!itImage.IsAtEndOfLine()) { itSlice.Set(itImage.Value()); ++itSlice; ++itImage; } itImage.NextLine(); } } else if (sliceDir > 0 && lineDir < 0) { itImage.GoToBegin(); while (!itImage.IsAtEndOfSlice()) { itImage.NextLine(); itImage.PreviousLine(); while (1) { itSlice.Set(itImage.Value()); ++itSlice; if (itImage.IsAtReverseEndOfLine()) break; else --itImage; } itImage.NextLine(); } } else if (sliceDir < 0 && lineDir > 0) { itImage.GoToReverseBegin(); while (!itImage.IsAtReverseEndOfSlice()) { itImage.PreviousLine(); itImage.NextLine(); while (!itImage.IsAtEndOfLine()) { TPixel px = itImage.Value(); itSlice.Set(px); ++itSlice; ++itImage; } itImage.PreviousLine(); } } else if (sliceDir < 0 && lineDir < 0) { itImage.GoToReverseBegin(); while (1) { while (1) { itSlice.Set(itImage.Value()); ++itSlice; if (itImage.IsAtReverseEndOfLine()) break; else --itImage; } if (itImage.IsAtReverseEndOfSlice()) break; else itImage.PreviousLine(); } } } */ itksnap/Logic/Slicing/UnaryFunctorCache.h0000644000076500000240000001255211254242201017723 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: UnaryFunctorCache.h,v $ Language: C++ Date: $Date: 2009/09/16 20:03:13 $ 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 __UnaryFunctorCache_h_ #define __UnaryFunctorCache_h_ #include #include #include "SNAPCommon.h" #include "itkMacro.h" #include "itkProcessObject.h" // Forward references // template class UnaryFunctorCache; template class CachingUnaryFunctor; /** * \class UnaryFunctorCache * \brief A cache for unary functors operating on types like short and char. * * This object wraps around a Functor and remembers the output values for * the input values that is receives. Do not use this class with non-integral * types and with types like int and long, or you will run out of memory! */ template class ITK_EXPORT UnaryFunctorCache : public itk::Object { public: /** Standard class typedefs. */ typedef UnaryFunctorCache Self; typedef itk::Object Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; // Caching functor typedef typedef CachingUnaryFunctor CachingFunctor; /** New macro */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(UnaryFunctorCache,itk::Object); /** Evaluate the function using cache lookup */ TOutput Evaluate(const TInput &in) { if(m_Modified) this->ComputeCache(); return m_Cache[in - m_CacheBegin]; } /** * Set the function instance to call evaluate on */ void SetInputFunctor(TFunctor *functor) { this->m_InputFunctor = functor; m_Modified = true; } /** * Get the function instance */ irisGetMacro(InputFunctor,TFunctor *); /** * Set the evaluation bounds, if you use these and these are small enough, * you can use int or long as template parameters. */ void SetEvaluationRange(TInput begin, TInput end) { m_CacheBegin = begin; // This code makes sure that if 'end' is the maximum value of TInput type, // the length is still valid m_CacheLength = end; m_CacheLength += 1; m_CacheLength -= begin; m_Modified = true; } /** Compute the cache */ void ComputeCache(); /** * This method returns the lightweight functor (it can be copied) */ irisGetMacro(CachingFunctor,const CachingFunctor &); protected: UnaryFunctorCache(); virtual ~UnaryFunctorCache(); void PrintSelf(std::ostream &s, itk::Indent indent) const; /** * The function being cached */ TFunctor *m_InputFunctor; /** * The storage for the cache */ TOutput *m_Cache; /** * The bounds of the cache */ TInput m_CacheBegin; /** The length of the cache */ unsigned int m_CacheLength, m_CacheAllocatedLength; bool m_Modified; /** * The functor */ CachingUnaryFunctor m_CachingFunctor; }; /** * \class CachingUnaryFunctor * \brief A functor that works with UnaryFunctorCache to return precomputed * values */ template class CachingUnaryFunctor { public: // Typedef to the cache that updates this object typedef CachingUnaryFunctor Self; typedef UnaryFunctorCache CacheType; typedef typename itk::SmartPointer CachePointer; /** Perform an evaluation using the cache */ TOutput operator()(const TInput &in) { return m_Parent->Evaluate(in); } /** Initialize with a cache object */ CachingUnaryFunctor(CacheType *parent) { m_Parent = parent; } /** Default constructor */ CachingUnaryFunctor() { m_Parent = NULL; } /** Comparison operator necessitated by ITK */ bool operator == (const Self &z) const { return m_Parent == z.m_Parent; } /** Comparison operator necessitated by ITK */ bool operator != (const Self &z) const { return !(*this == z); } private: /** Pointer to the cache */ CachePointer m_Parent; }; #ifndef ITK_MANUAL_INSTANTIATION #include "UnaryFunctorCache.txx" #endif #endif // __UnaryFunctorCache_h_ itksnap/Logic/Slicing/UnaryFunctorCache.txx0000644000076500000240000000577711255106167020345 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: UnaryFunctorCache.txx,v $ Language: C++ Date: $Date: 2009/09/19 07:47:03 $ 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 "UnaryFunctorCache.h" #include "itkSimpleFastMutexLock.h" template UnaryFunctorCache ::UnaryFunctorCache() : m_CachingFunctor(this) { m_InputFunctor = NULL; m_Cache = NULL; m_CacheBegin = 0; m_CacheLength = 0; m_CacheAllocatedLength = 0; m_Modified = false; } template UnaryFunctorCache ::~UnaryFunctorCache() { if(m_Cache) delete[] m_Cache; } template void UnaryFunctorCache ::ComputeCache() { // Can't have this running in a thread static itk::SimpleFastMutexLock mutex; mutex.Lock(); // Functor must be declared and cache length must be non-zero assert(m_InputFunctor && m_CacheLength > 0); // Allocate the cache, but only if the length changed if(m_CacheAllocatedLength != m_CacheLength) { if(m_Cache) delete m_Cache; m_Cache = new TOutput[m_CacheLength]; m_CacheAllocatedLength = m_CacheLength; } // Compute the cache elements int iFunc = m_CacheBegin; for(unsigned int iCache = 0; iCache < m_CacheLength; iCache++) { m_Cache[iCache] = (*m_InputFunctor)(iFunc++); } m_Modified = false; // Back to thread safety mutex.Unlock(); } template void UnaryFunctorCache ::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Cache Start: " << m_CacheBegin << std::endl; os << indent << "Cache Length: " << m_CacheLength << std::endl; } itksnap/Logic/UserFilters/0000755000076500000240000000000011560342170015047 5ustar paulystaffitksnap/Logic/UserFilters/CVS/0000755000076500000240000000000011560342170015502 5ustar paulystaffitksnap/Logic/UserFilters/CVS/Entries0000644000076500000240000000000211560342170017026 0ustar paulystaffD itksnap/Logic/UserFilters/CVS/Repository0000644000076500000240000000003211560342170017577 0ustar paulystaffitksnap/Logic/UserFilters itksnap/Logic/UserFilters/CVS/Root0000644000076500000240000000010011560342170016337 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/ProgramData/0000755000076500000240000000000011560342171013745 5ustar paulystaffitksnap/ProgramData/CMakeLists.txt0000644000076500000240000000012710727111331016501 0ustar paulystaffSUBDIRS(HTMLHelp Images2D Presets) INSTALL_FILES(${SNAP_DATA_INSTALL_DIR} "\\.(txt)$") itksnap/ProgramData/CVS/0000755000076500000240000000000011560342170014377 5ustar paulystaffitksnap/ProgramData/CVS/Entries0000644000076500000240000000015611560342170015735 0ustar paulystaff/CMakeLists.txt/1.2/Mon Dec 10 01:14:01 2007// /SNAPProgramDataDirectory.txt/1.1/Sat Dec 2 04:22:15 2006// D itksnap/ProgramData/CVS/Entries.Log0000644000076500000240000000006211560342171016452 0ustar paulystaffA D/HTMLHelp//// A D/Images2D//// A D/Presets//// itksnap/ProgramData/CVS/Repository0000644000076500000240000000002411560342170016475 0ustar paulystaffitksnap/ProgramData itksnap/ProgramData/CVS/Root0000644000076500000240000000010011560342170015234 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/ProgramData/HTMLHelp/0000755000076500000240000000000011560342170015321 5ustar paulystaffitksnap/ProgramData/HTMLHelp/Artwork/0000755000076500000240000000000011560342171016753 5ustar paulystaffitksnap/ProgramData/HTMLHelp/Artwork/CMakeLists.txt0000644000076500000240000000011110727111331021500 0ustar paulystaffINSTALL_FILES(${SNAP_DATA_INSTALL_DIR}/HTMLHelp/Artwork "\\.(gif|png)$") itksnap/ProgramData/HTMLHelp/Artwork/CVS/0000755000076500000240000000000011560342171017406 5ustar paulystaffitksnap/ProgramData/HTMLHelp/Artwork/CVS/Entries0000644000076500000240000001447011560342171020750 0ustar paulystaff/CMakeLists.txt/1.2/Mon Dec 10 01:14:01 2007// /ttAutoIntroEdgesPicture.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroEdgesPipeline01.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroEdgesPipeline02.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroEdgesPipeline03.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroEdgesPipeline04.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroEdgesScale.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroRegionScale.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroRegionsExample.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroRegionsHisto.gif/1.1/Sat Dec 2 04:22:15 2006/-kb/ /ttAutoIntroRegionsRemapFunction.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroRegionsSegment01.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroRegionsSegment02.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroRegionsSegment03.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroRegionsSegment04.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeConcept01.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeConcept02.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeConcept03.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeConcept04.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeMerge01.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeMerge02.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeMerge03.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeMerge04.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeVelocities.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttAutoIntroSnakeVelocitiesMotion01.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttEdgeBubbles.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttEdgeFeatureImage.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttEdgeFilterBox.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttEdgeLeaky.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttEdgeNoLeaks.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttEdgeParameters1.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttEdgePipeline1.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttIconAction.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttIconAttention.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttIconExpert.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttIconTip.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttIntroScreenLayout.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttLoadImageBrowser.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttLoadImageMenuBar.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttLoadImageResult.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttLoadImageWizardOrientation.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttLoadImageWizardPreview.gif/1.1/Sat Dec 2 04:22:16 2006/-kb/ /ttLoadImageWizardPreviewWithFile.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttLoadImageWizardSummary.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentLabelEditor.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentLabelEditorChanged.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentLabelEditorLoaded.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentLabelFileDialog.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentLabelSubPanel.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentLabelSubPanel02.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentPaintClosePath.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentPaintFill.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentPaintPaste.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentPaintSelectSome.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentPaintStart.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentResult.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentRightCaudate.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentSaveWizard.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentSaveWizardFilled.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttManualSegmentToolbarManual.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttRegion3DImaginaryLine.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttRegion3DScalpelLine.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttRegion3DScalpelResult.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttRegion3DTools.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttRegionAddBubble01.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttRegionAddBubble02.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttRegionFunctionForVentricles.gif/1.1/Sat Dec 2 04:22:17 2006/-kb/ /ttRegionNewInterface.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionParameterWindow.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionPreprocessor01.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionPreprocessor02.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionROISelection01.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionROISelection02.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionROISelectionFinal.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionResult.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionScalpelLabels.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionToolbar.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionVentricleResult.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionWizard02.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttRegionWizard03.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttSprayPaintCross.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttSprayPaintTool.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttTipsAdvanced.gif/1.1/Sat Dec 2 04:22:18 2006/-kb/ /ttTipsFocusPlus.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttTipsFocusResult.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttTipsImageInfo.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttTipsResample1.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttTipsResample2.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttTipsResample3.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageAppearance.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageControlPanel.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageControlPanelToolOptions01.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageControlPanelToolOptions02.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageControlPanelToolbox01.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageControlPanelToolbox02.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageDisplayOptions.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageIntensityDialog.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageIntensityDialogWindowing.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageIntensityDialogWindowingCurve.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageSliceWindowClick.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttViewImageSliceWindowZoom.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttVolumeStatsDialog.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ /ttVolumeStatsMesh.gif/1.1/Sat Dec 2 04:22:19 2006/-kb/ D itksnap/ProgramData/HTMLHelp/Artwork/CVS/Repository0000644000076500000240000000004511560342170021506 0ustar paulystaffitksnap/ProgramData/HTMLHelp/Artwork itksnap/ProgramData/HTMLHelp/Artwork/CVS/Root0000644000076500000240000000010011560342170020242 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroEdgesPicture.gif0000644000076500000240000004600610534177567024267 0ustar paulystaffGIF89a  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~ !, zBA(JS`Ali 9dR[ɯepp)? B ڣhH#(4h!H%F{XiUFphV[B|bB 0b%YX$1FixH&QdL5q@5i Bp UĬbuWq^6ІS<)y#o@=u0Iۨ$@~3Y?wGdF/> L5Sy @^4ƒFȄͅd0_"2$2c6(֣8,ju5s]D7]ueBU^&7z걇{'=c_w ҁ ȠJHwb~%-T9ɝCYTwyqBˡc$.6$z DƤTzΥd |)yiz4+"ړuI!k'b1+11U-Ʊp*4γDƴmQg,wN}gP 6ѣDmTMƳ)zjj*C*z[h$l}w;284#x,E99?989-G 褛lzk[!v]ֶmx#񔂷QoG8z^p|8_׾ot%(NԱcuߨ% 8pk'""FC{|ZYuc$XZ.` i=Y˔sވ#5渉:օnQ">Hڇ uA> C/萇G`"HD#Rq#D(GT8-bXFj\5F8ʑv4G>> C"R7! !)IC,"$ꑓxȨPcVbJUB _Y7H:nˣ}?",d'cBgP84:\!!"U/4 d{yyp+CE-xC&HeWX'Kߥ j -Fy&@TH'HqRN(AІ>t$D+zrdtz( >ʸi($5)J[R>/GL8ST7UcN5S#@P:2 Je SԨށ-B8(EEկ懣H"RHi[Vwu+8ckOSF5C-*ꐦ.,Mm,!k\lVRuKDu!.[YHq1͡geT7E^-\h%Ջ>ApԤ%2jwATˆkhzH<6tk@] /.w]ua sOʻf*Z/L-5 }c_7BmPC_B!3`s; 뺪n궋15D\3@16TB8/G1cw7/S#2#</bCd@FLhBjy, 6j@+ź̈́ኆK7I׸kԫ ID&hLE6V5CȾg"hBЈ[<8:C>@i!҇4*8(O$KS'իncYֹɮa_؆6=c';@ CL'!n.HOzU 3it=Fns63~ȚƵk: vmp^(ۇ3)o;:ڬ%P7$=h{#TS+iqGόbc©2#}Id>r& UFti8]PL*> udc\_rh=SkIۑϽvǻ<؀Ǖ ß.8Hn+nuEѺyIQd>[O2Oޛ 1v!kkU`8RW"/nU5Vl:< Q[?qPtpU @KR4 1wMyq8=jHEaxzȇ~ (Xt`ȈTHHQ؉zb<رOԊa88(C( uxw8y  "8Q:8 ،,X\p (C߸,H`o"9hIBuAXu]F-ѱ'pU!s.Sg ?dToѱ@.LLio# XDtg"E:BX.+f0ڑرBВD`0}REq߱ғ[ d0P q M9OkQ9UCW9Y)\y^!`ibId fVnyprI`c%{)?Aa#*##ȘygU?!9 l2r:XSɃ\=]Q2 66]G\ytKVQr*#dD51eRQY`I3aP7;# 9 Y~/Yu)iɟz䟑2 lCz!Q :#Qq~Uߙ\ڡ 04& w)"y*2D46>r"@hw,np~QQF~(*4 bPj5uaζtxiEaΆ ƇGisgz"O *r uwy*"{}y*j j"`::ZW(Jh tZgʔ*+Hªtv]ħ^x#@bbǚ*wҬ:2Pתٺݪ*vGA‰`O0sV*B2 - +o eOPos ЅoCXaFnSi5.76cFr?ѱ($kP&(;Q*[,23K`R䡳PP?CKTE怴 崞 Fc߁#![G# %9')/m۲79; ;[G=kqARa+ @P`+  ;:i)""@=!:.Rԕr͒ԠԃO-Q}:lS,{;"pot0s` ]kjӸH2L"Ei+e.c u6Qu`d Bˢr {p-Q&eBOth;yWT/sh]G ْMٖ`Eٗىٛ^%ڤmwڱ:}*ʺ}!MH6mE!6"=ٕ- jmC݇ M"m+]=޺ڡ5`#߹]']  = {lSHƉ/3;0G<= TY-tMEKbr6y1D0Ѫ; `m*=ebqeF _k =?A C)E5GJnPLOTQNS UWDYN"[]._ !Fbn;l(2;ㄶx}M0IN K8M襰0Pp陎›Nc+eꢮV/%C{a8-8V. TVGJQQBw=AXL1.oA9=,4$#Cq1& Ac~&`!bA~w` lAcd / ߇OC/,N~'nn~^ Nn."^uNT) ߇A/ug B^afC"V1Fe#Vʦ>6HT&/J:<4R僜=~!M˱/ XQ6!Lqd'P%!HAf* !L1 RrxM/8 ?t_D*/""/HQ?-]_ M#h` W^bkW1$Zb2i4:XDJ\$ TF0e7dv&tʑ',"zh%|)iDU!:*>z$ 3B,ƴ$bۦx m(Ys 6hAݼMjN!ApҨSxåa13>J(q 8bNdQ܌k`b kP8agQG |jJ4 LccP KGJ5CR*Z%ߑ8wjK$rTӭ6ߌS9Ew%au&E+A O~╱\,JP"Ta,iʴ3̋F 6: B 7N_xA:Q]e[+KZ HBC_{@ UP.vb9FcXi` LliǴ4A _Ɇr0\Ko_L[ **sU]56}E_;X-lMbXZ,K,h(gZ1+Sն7f +L*9nX^kW򸄝a83c;H]Tpɬ6Y| mxWQ^jSxU0`+ZVK$#~Z\)lBbza AC[XCDd&^ 1ܽi@˥ `cS sIJǸ=v'}i,6cZl4v q5?WS<#I~ÒdD<&RFl ,J!h f1̮0smМ53W月şf,X291OzL9D62 hGҒJKg:t>mPbԥ~éyjy0B ѥ\?58P"X!+/d!9XAHKJbc$nRqH{\I'[*OnM z݃w{wNwW0Gb?x^ˆᣉ8Eq8Y<M}|!KnK.q[y2Н5ur3w zާw pA8>9\lġN cܹqi"j}2r {P8(u#@@zFMA&]-2{ɊћN#9dC7%iE&JA%|iCKOST̯l&=F~[3;ss>ƀ>>aV>о0>?{c&3+CoR= Kؓ===dK[@z    l  Zrn GB!0\i /Sk: 8 'gi80ۀ4{u2"+%,-\.8No"@1B Z6,<ƀ;CCC< =T >C  [2DD E<FG HDhBPBBBG0 123 4DEpCVW,XY<,=\]|^_`D̥ElF4GHIyX+"y+TxDH%H>2g0T8ɂTA xIȲ ""I˗&IlYCDh/` Č|$|làJ(:J(,FHH(J`! / k63K$ R+ H4dl܉ _*9B"[nr0"3F(LZYNh\D>Ny%ΌN|,lK [*RE!"%#$}%%eRyrR( )5*2+ ,-5 . (ғS2>PQeR5U,@8}9:;u<=mH hّY+zaF*'}uط@ %VC (cO@ ԴTA , 2=8 ! (v x{Έpz{WsWW=WU eXMˊXX-X ]7-ٓ兔x yMzuٙ}~ݷu]Y ZZd*ڂC PZ`Zة%R0 r,"3 *Hy΅|u9sH]諣!gB.ݛ )8( C4]>\Ԅ#=sD)QԀ\'x\n\\\%܂\z\е ѥ ӵ,U]` u ]E;]˺]݌]-]]^#^\0#}嵊}E ɵ}H̅^͇]ա me؅%U ᥉⅓`P^e1PP05@M% Hmybrڴk1(kʀ!;,8@ A8D:z( xiy!"#f $Vb&'O(\)bnbmbAbc c0c@56F78.9:;cwcYA,'=b#&'()*+ֲ,bbbc(c:IcQV 6&I7I8n!9:;&XeymбDUC4 ni3}g=MgRJzeҀ2% P%J=asZYDRT㖧ag#rgߓ[Eg|gz~vÀ&h6 BhRhzmhxhhvȌVݎ6ƈ~Ғ^ƅvwx.yFz{Ͻ&&8hH\hFjPj`~jꩆ>Nfi À7fiZ vg4DDJVL%j2(_(SC( #Z5mFll8fΎ.ѮN.xmޝmmm_>%m -nFn\P΅p|^ooQ ٮھV ܶ ݆v n,0Z1 Uf)*PqEhM."MM)U @L )! PnYi _=w7W{)o*r8IZ;l'=(2),*W +g ,g -C.,/=0W 123#4_56W7'yse]s]$&?'(OE_F7G7H./012347YQ?Ӿ?` ȩv9Qi:_g*82( O]`}(Yᘠ? +jgk?lmWnvpq_7rsw-t'3ugvg p wŠ{|/Q~] _J'#Ѻ4KàJuQr*{4kxF( P"f ѓSwȝش++ ?9Dj3Ez++zS;-8p]"+^D>re˗1g֬-gz /輡Ȑ+|7kiR3>Omu<҇U4q BIr! T2WIra p0b %p" )+-v"6b7鵃W?cz{|Iه~O\(π2́i$ >Uxi}H@#rPb '" ++486∎桧{g|h>ߔUsela4 Phaaffmf$,h(h;c;5U;ɦf#̖] Taub ,P5@ T%gқZp/6tB~Q301G v4h֋6klSɾlz>mOVӵdӶݲmᎫP+»:ڋ,񋂿6p1sp wp7ԱS܎q<;cyLܞ-*KG캫;QlA8oB03@ MtNF ֋0s8xsש5ӑڹ397Oe v#8ޥf)n3.H^3@;Gg'+>𾯀'rC<}U M`78:N,INSe>9k:[>:.nwȻ7x+U.z+FdG=wLNg]ҕ҉|(0Ҩ7Y 8$v$ =rR9P;nr۳\4Kt0U nX o;vU/{KQʷ=/~{ !aK1acJ2ɱ LaBFM/zA%_N~_vV PyDE`Tyx[p30QfQn/),!g(?Ⱥ3Cc:}< 7EY$d&OɬMJe X^XQ ;H\2i^s\_9[vs\XFcYGCVŒ%ӢOr\e\9Dt87}NȬ3*Ps[ծ62џy^~XEsv C;̓wCР0$xbx:]wD$#}B. IW89Ec) ~mxHޔI'wo~`f?8 C(_!;+JyO)-y7bwW.C7ao|DwЇ~@8Pg @u':.b'YPdLRAܞF+,Ԕ`s0IIybP}ioq'ə@Đw +aPq 7!r!TP[I{ˤ:B?x~QŲznl}f_/=yo# E_ FQ]=_M_Dm_0t_x-CC^О_D_I -$@%ʄFI`mLMX da4Hb IDHFFϤeDrtrLD]쁍d0!%$,L4>n_??ddd d5B2$9$@Hd| E FjdNpdx$Z$7$cc##R##pd@@DA$ P%e AR@SFFEG.GGX 4&y)LfVB.X 45f>F t&4ZMD`Xb^YuGędD aT]hlHGlܤOuof0Ŕ`0c dVdR&EY&fnf6gzhfGijjkkVllm*m nnRoop Qg': ' <&DfL)TeGfnKtf |&Imu&IfrfGĦCԦ&oRTppEqܕLIb(O$$&Fxa[1jX4^,FʈAܛ,H`Fc]FFgŗt"i(i)ih^.h2&V2‹NiT)\iGdrU.ȗHi©Ui~h&*x^1†a($%C]IͼbFD!@ <뤥L`ĔwEb *``EHܫnBj9*#jI* *Ck+*+!)+0 ij@˴fFY+^hpl+t뷆L:ʪ«f_@ί kk+l4H.4,zAlekDP*+Jx'>sJ s(A?s447CBD D@EE@F3GÀG'HssNs/$4`EDD'Y߅mDn ]ӈ 4E!չuOKYvaK̔Ah0(jupWGXXYYZZo[_\˰\S ] ]^s^_[\i``aab_bccGddseWKXCXfY5Cu5CGuE5:u]]E^^S__o_T`_`vv}%vvpwfccĠ6tP&4,]$ɡWewvŧDըA'˩?E뀫ߖ8֭T UǯHGܰpDZߐs!w;W' @_ӄCW;baj ^`HƠ m"IH`kƱ JRDo^۱LT 2\D4lt$v$uH Z~=LQd_gS_"6Mloy8:wHQ6j {O ^_?zȾY>>7[<~O? O Q(c23ax&p((ʳHBjԪuDH#|0I gXsIf:hg##De=|CwԛhK5[TU%k]Vq[{&=hciE@ggAЙ{E2|$qD,ʛY@b)5Ζ5v*sjӎ;6.c1>R=e0kC3>>o5sZ@k6$Gx|ct:.d0C QJi. DYfiʜqL6%xsN[섫*p3*nSMj9RJQEKX'O;mqđ@ ӴtPRy᥋TJKC\o.03Rs6݄"9 Kjs@MPr EEQHiJTSN TQI5UUb!WU5YK0%Dn^VX5ޔ NNlY@jM [mU[p)RrC2B`/JTXUV_^`dfsGy҇giEVXh<frJʅjKkHބkLDy{[_miNOH o#Tnzbp쎚G,eflga-Ih0iaˁz2:-Z!zlF%,n?S/{gE evg:.V'1Ls=I7uYw=lWnkmN@n xC"͎Op؈^PTpաV( ZdԜJ'2P p)TXX֠eh(^ R!8qpe$r;ن `G5S_pP%ph# V66>! MBOpa`(C b%tp)@%#FQ"įD4 nvSeq4 .TϘ4J8a0ecgq? "N YC&R/RQC+Q/8P lTiP >Qa30pbXJ_@Lb/AX)fzќpIlbf斾Ň 9չv"'=1`O|ꓟh3IkIEШ%<&eDLJBę#(5;:!#=FI)!Nr-<*xδ&i~(AžP@"Ɋ}P ;K*u~s TLs2Ьlt&GܨZ!g`j $3,]3'5%Mas!V`AN2MEemhg=ZSUi=Zvv}3b+Kvk"]aiXW]H[6Pݠ].x ^>@z_۱L-|IB_&p-IT>j\)D,' Y31$<D@ȰXFouCOI$aUPR$"0g ߘk9VӎcWx ARdIN̒9d@jl +sZrb<0yLd6%>d.\9d~ZP5}94 ¬;2!ZN x O͠%F!TgюvQ]۾ b큒y9wQiZ,5NTZ4vګYW֡5r]gb+.`hodJ.mm[6=n[FH1uj[ִ^qt6{lWHxl8Ж6jm1a~qZt|GV&3q##: 4Ovr h/1oP==g],wһ)٥X<Z<f{˗ mR8pp:K.zt*ٖ+m;>wϻf}'< 5+B`p?I~K7ys^W "utg_?}RυՋQQ;j؞t;ì4 =~ okO9 /"o)&/0*2MOځ4;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroEdgesPipeline01.gif0000644000076500000240000002514510534177567024563 0ustar paulystaffGIF89a!!!)))111999BBBJJJRRRZZZccckkksss{{{19RZ,((AB8 @!BE#0cƏC`𠃓,@ bv t|  2:4юI\ʴiH .U48clؐhD-f ;ΨPtA/Zx@k! 0@7hЇ^9B`:+*ۦi^UUBD7CwTKڱʥނ{ڷJVc":V"g7=HU8ԅ!ɛzFl؈g_~X}PC @-bSNt%9Asi皋=EvXM6B g]~Cb)@8RkҹyAiiw^RyFUIe9(I%wd$) j89d9MD@tiYh6Z5G8 80j[r!!lnAGν'\R4+ͱXTB@ (59o&u&+qy^oX6VI]Lz$@CV>avvj ؖc"7|5\ ͍ȺHŇaFd(݋D%,1Iìogb$Y%|_DI9c@ 5! 08#HNޱf׃.JVRXYq wCN21]YҔ4jV⡑aD/Jђ8if&3i&V#W `l̟4SL;ŒNhD09x}c(I@1UM$i; O oFl ٩0'COv|ˋpP ,;Q2,RV4pI89M~FL$[A%,`Z~7\)iQLqᰖ\0dM3ͷdٞO31=Ż5?ʤʫkZϒRFqgrCg^6\ujh!jB;"DatP3]*P(YςuJ쒓jɴ ІZUo/ԉr-WpҾNqD\ \ י@lF7Kiv)W~*C 0<.$S-/Kp<r~'3e*^| Dp}k ]$h%# Oʉ==2 3 8t{Z:"bF>hU.絸0 Zl0;m5MU;38-_lyϺY".ȉ09c֬f]e<M$jĢ oJ^~5 cj ͅ}+8#7)ѠNGYы62VUy4GY/-9oӍB  :~6xg6˜;c[ h:+ (NU7w&@C9YY!5V Ǫ, xIg,o 1Zt' G< D33o"LdgbQ"8r #-^۳C]ݴTux|VO K_sbՀs'J~M X/φ-tcu07z"E`/O7kuidWssydZPűO-ګo}&"^ u|.~)]? >33;۪MײZGbRW6B3^/"p&t!M^=xh -. Oyo;ONo!>{.2AGxyϽH|#Cvٲ|b~o[Fd+ zrXEQm{o7h}o9BNc/5n`Uc€wQ1qW)xWܥ*'wxoF%z]%1րbЂw~yz7!5(~A2Bޕ0<؃AyBnEA+bmfun9P,DV%2|z[[cv_C!wD4MVf GXqAjqH]Le"8T%yDhY%(; siYwȅ@F{d> s|~+1Fcp5H/ȆDžo(Shq,NӊJ`zzHsBbqԗ @}i}7[HzOVxM4:؆؅w9j'uHcG츈Fs爎}:y莈o؉q7h5XaQjA3))ɨÎH_x1B1&8cXϨhMS?VcNzYawSiGFȅ+|2@u^݅=nʼnR35cDQhaŨ!7Ɋׁc;y|Z]A)uC EyncYjsٌ-na5Q;-#la(!ك'!B*_s%i{1؎'AIycEk{b9؆?Y.YCٕ1wߨzg6`bgG.I^ٕ0ygii*,\b4)Cl]řu),醄EWpϙ@Q%ȸuԝyB%t^Ws~4w6&깞vَ-,@qq8ys}湜6t1Ġ Gs!qo 3. zqwgUY-&z<{rk:b(+ͲYC(fE燁sDiFIA*r)tUE׌ycW:K< TG8x׈rR%MOJ(4IsVHǥGy{cQ.32O-6I^Ha"G 0vρdXexaXd5ړ/jE(vz9+ce6:T;d"4?1rtᇺeFp'll/t҈&$}0N:#"YmI⊬B1ʩ:*4|q WiE4Tnz<1q5.㮭#?2G$F}:1ɭ\R3e+ aG]%wãyP*ʲdN6RD*}ʱ@˒)@骢23uDH2긦7  Z` YGGx*DOK뮜J$"To:s5)`B[WKW3yw+YD+7r[*w'{k~;(a{W%A+QẮS+OxGi M Y@ʞwdkZxt*+tv\˭l G^ejGkg33˴j999 ;HzʨN'ˤ;!ll˰V B$AD_0QzwFvGnFԿ:+.Tj1F0/j'%DæNSòKs˰8BLD;dN#0DG|w6KSlk[4{0T283)Gn)E"g[Gʲrs|bբ /! pZȂ ʇ,6 di'˸noWیaX%h߂"- TB6B˔*ˌʎhUl/=yb]tihٟ,)ۋ+Ҳl].Uc}yUthy[p&z_wν*\ > M\ރ1PyUFLm&gʁ gݮX@9-&ZE- ݃=`՝gudTq!M@݌S}.oUCp7Q amCsR.~|۝KI]<*N2РY"C.~0n2)"},qRm~'Q@+;#K歶. keNO2&]֦m/xRGb%.m#Fa8D)!e]=Ϯ;Vz8 d7V<YBFj2# /WL];w-x.!]n H* 0(Y됮tؼ%%ص"ΞC̒d.NoԐFYd}5y q-Ώ3tMǦ&}eN욂x3W'A>D}AöT) g] o.8>9QNaNϧZͼBվ3M!=ԄMW{VoR& Zы1䲔)K Tc&N'H_$N[~W1or7W91p-eͦ;~^YTnnMLqt$dm }-Z!Qde`af+f`)oJ R ~<a>V$n)aOܚ++ oHh]c\ӅO=nR08P 1,L! $ X@BB ! \y  4H̕1i _" Zѣ1qN`# (˅SJT0qb` a%MB@RPKuԹfM=Y *Ѿ֍qӨR:hi+MVTP@ز Φ 8K< xh_ح[ו bR?z0y%OD=eiۜ5߿{jw tn/v24nLqbXO1:?yzU> n1C?s2H&**:/4諰(@?R9N@t-.fTq< !f: >"FJJK$ϖ[0sI)踔K5m<2s@bH'8%HrҲ(I$@ sδ˦W+1rLusM $J>JJ,4BtK0GLzrH3rmd1SO5 Q@'p$MGXdUg%k j2 6& C0PDV^Mnڂb%CeBer7] %wgy魷W-kKW RǂP-OW2@?Y2v}O?S!0cX[idYl!uj+@% -6ZH4M[QN5eͩdNցAC N vY^`h5zP[.l9Jc!2Wmׂ͢mRDnuY2,t$Ռ!uPP=t䓏Ws s!"O,s[ꁶ̤03gijF^XyV6ԝ!zX @0Nj{.WQL 悻`;.@l ?''>9Ey' ajHFq9`@<`a0 OgC t9!* Vٚ[ (ݱd*s;d$(2ȊV~n&UB89a+/S@˅^#CB9$z#HJgJ?hZĦPKH DR! P%x;6`9dr@r! ȤF,Ir4X YO%Wr#Sl*@z(EٖEp'dg17!kN3ɀhJ҅$m K"$mmkH›:iNtm\"7#D MkhMptI9HE$&2 Uӡi Hf":"%I#q?ENWҶP53DM%:iT~ T"$&66,hMUޠJL/[MRH쓛ąS LfXZG*3+K0M{+Nm5~b3Q,O c2>HsMAlvsCJ!%Zdڮ,6"J\6-H2 ] S2;4aqh$ag$kS܈i$MfI!Bel>]yAΎPKh6+LQ-MaHƵ O MC\UI luQa K6t _ȗ _7V8p\)v"Iebȳ]B*2uD7Vɠ( `joiZZuycc=#H$"jn?;4"5Ӗ5F`mmg[#ۮvqŻ]q+wZ.Lc_V]ξTM~K>oNܢ\,,D`+@@zˋ:HIb9M$%zpXrs $@ՠ=xҼF7/tƂ="y,&x;7]:sG߹SvYCH܏^hNE"xcnD'1_p616/~2#gG6~4mmnsh _~mS̽3ʓ̳vc}؇Pܮ{^>}+VtklCkMskUkDia>"=j 2? +s?3? 0=6%DwYc l{3:?^{!t A"X@d&*@LPI >sC/Y<=f%&x/ 8<Xg{6O@uX?+n"6\%qGrp *<dĈDSBzt |F/m;Io԰])Ȃ-҂RT E R> %%|}M|T.uXAO ! iQD 6 7Sb]MDIXŎ ZڹςU6m#Gu-%8iGq֦#͔UyQό6iD9UU zUTBPW+:܍vx* N?=@}]aTQTXfu;`%5A@ : O4WWG=̠jWDB[+ٹ;sl:5n$ MO9c-`d/nӫTMP4YcWWEO8ޛd=I>/=ch1%" E5aeE[ r.zcd0ʫh2@[8SY h;5A`26Rdfen M\^<^NdQ.^c R0jє8x' ɗ09(5g)y{A\S% Xlk X$Sge+8g>Fi~ƠxޗVah·Xb^C5)>FͰOy^)ZЋakbfi"ԝfj!0l<~;G {'3Nit zkb6qʁo) L 0mhX> kl9l"$F#!itgH̶m3~FQeV$e@g>C6'blqjUCJl T;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroEdgesPipeline02.gif0000644000076500000240000005527210534177567024570 0ustar paulystaffGIF89a  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~,װq7n֤13F1eҸD"Qt ӥK&ErB J$K6q T(QF%*O6iXI"5ZG)2$RDHP.-eԩ͜y{TS&~tj)oK$={ A D)PھE\x?}Ma5Ӝ)Cf2hıDz}[p$Qer}1!Pw&3ل_:ɶ^'l"&چ n'#(rH!NJ&HJ H#.= (ٹݎo}xI^'Hagw7fцw!In@"uaZ`uzu'f"BBR"R!""8$廡$a#[m}.am \U·]BI$GlEVXb!(%,[vEs5]}Ku3,lHa&<ҶEyc6Y@p1 r L~"!G`(PA bPPFL]Wz=CEM^P=UP`)FO 9 %lnZ [օqbiXð0AAX9J<"ʰ)4! HH@6hZB=}n3,dlNH:fo&0H<xD$^ܐx P+?0 i9i~0 KNw &6 ,@! D)p oCLzKN :PKv ☬H1y;PgD$bFIoHiBu~dCIxd NaC; VhBL =T ,Әl&}"ȨFKpD&*F )iZ'/oDHE2mNwr@: TXzFp°:D$:ԢdfQFbThU {FvNTD!=!!2Ix< WGDbQj k.WGtHC 7(h nC ZRo۩CUְڱ=+vیgX!(8I8`JޡHDVH1( 1% NR~d]虯fؠ50.!z"8Ƭ1$#Dk[۱(oy^p%$C!rh|*8 ?B#-TqVE DJG+HuҼP1Y) 5 "4 `P` шrB"aydcEf[*-E.bhz8Dp" z4#=OǠ,02u#@! b`*NHp9,#بQagjP2̮ T0/1r.oW@;oA8;#W#֡'R G 1 x %Ha T-8VUm[e f[ E=a cBЄ%4 V} JlDvB=dl%3tzjH$Bl0@0Ao", ů~KbRXqF\j:޾rτKIp yC)0 E p1X$2 X.iIA@=YPAQ~"q2+Bxp"3 d^HN<諢5 o+bK'61o8%h:B"4~m֑i>`RӖ\NzIOpb 0X j@#< 4Vs9ζC7>t7ٍ5!^3x O@zBRh@ eR7yl|qGS]s#6)Az:| hIr7x/3GnI[Z _yB~wt\aVlDs;\@M`@YegPf ~ {gGĵ? ~gbZr3A4A2{d\uaO@}x1}9DP PsĄ~F`kQ!U\u`#Z0LP?pF4WOS 0w.'{43xP5nrx'=5nP\0K@>1 i6@iQuIF}`m0_nEOYotQ Rd\ȵF]PN<*ɒ,.6g0( d' V&R$VgBcz`! "'-78+QcU<7^6;xe_p;@hjLՈlW_HJ).+,2p>pQ@tp{cWn#wgq}4g0(S[\|PobVK`B;7p9ApN`_;XyVV5bGx?z@m`_QxI009I0_vppy9&]vh B'S!GsX0NEJUPadnc) yمi*ْ2`CW m 8bށ+wE:T4G$Ţ\bG0?T:OEJ`ZEaU  ELs=֘v$eRwkPjuG9`3}-0,uIgiZPq0ʃG.gAh d/8||$%1f\pRpEG<`YKpn^'~YpsC7%RDqpasgZ@IO},.45W{莄Gzv?Xt|ga#-NE_xtlf_=O4wyс8$S!lM9W ٞw x+27d E4`[oaՉ{q EhE-5DF11%Rnx,V`VMD? G8gon'z$H-BCwBB u c ʄ3m1sH5p:BO`Rk8wkgqs&xXm|7Ã2%b y`skpb DjB0{Upp6lCHڢ-=t2ep6mn9Opga{z=݁0zcX!,pd-0$Mr"q&)hШChU 4pH~rfGڴAfDHQ`]PmEȵma*D -RA_ /XvrwCQ @(+[Ww,iz'<@0{. xA+0 (oL~y)+są[Zd@Yes5xJa2 0=5Hس#ޔnp"};jD=@~_po`ehMRM'Qvy@H˞Q:}+S4\ AN|[43".l(L,ܙ= *^`m03"~*]Wʨ^ @W'oٷxᖺOU\`6V2x;Zu I)XrC8]((1"*Ukj+}pXvPs,jZ;O[6MP]*21 %\"H$#"HE2(BM%,UF+gjw`t0rpnw }M+ ;TSbID@FV]J+ HNFcڅb(=}#;zTLl[^0AIr qpmg@amNKk,8NFPP[`6cI4'Aٝ8G:(?]4o|2.tZil ?:PuV*Ò枫 nQkbpkW~=%Vڕ$FfcBr.768,/H$&va:ʹ]%h`CZ[ڒXR#Pc5Zv\IBj|Q2C7w d/ya' 4|%=W,-D98*|Rka@%LRc0镎JO+yi0xSd s0mM(nv]yX\M%K>)Q# .7N<_t=m:AJ[_9 ^iUM0Z _n4*B^Y($kc2C0a3u}BM{nㆰ~za@MxW^a^*.v)ead@ЊrMރ$v&n 4M'w/(FO*jk'i|-[]]*Iig9nau| R(`va5tn=-/"|/UbO+SpZZTz^07+8P,/0bM4.0PTZJU*TJII,]ʤS'O@%jG ; SM.U #G1ZHD9:d!A#/W$G 1d̰%O\Ŋ&GE+T]7xAJ2j B!BHQMFA4&M>"e @2EJ'N ^Sƍ= N4aDIKdҴ3ច'7il¤PcH!7&PT2ɒ"@tؐ kUuFEP2&~yI(M:20+ %ORQ#Z O< &v&x2*2#b!"" 'b(Hb2=z+.H" ,8 : 3 1l@dX)hD1)%L*H WÒNJi%ps$řj^ =H.,PBnd(x *j2*( 90;l2s̒L\26 rS4:MP=;5g+G`"P~ −c 2 "| +Xk P‰'pB #t\@/U(# 8 ,6d be7#3OJO?Rd֦]LM[Td1Q87̰ #~ȡ,ܵa` 'H",ߴVh=+VYۘ#?x5): b7x L) G9YQH>G%lMQf= 0Q<0ʹ@b&X "~ؑ_" ( F؎[ϰyTIдbOX!LQ 35I_CIJ(ieu+tCj8#R$Q3}χ!P &8b?YpM{ b * D.1c$+2kSؖSMx!P7RB&(a}>pV@@03AaS!A€XANm)Dup1ݕ 5!ۤ[Q P)l(SS .q d#!) nBa*E*'lЀ&cSdXjC{biD˘`=H _@%"bȇ'QWҴ nd'gNal0\XtI%sxLOHҙWG-! y%0a o΃FHU1;>qU)|!~ynح@|3ttƃHaTD3on«.Gq)nԻTbuKm|D~qoCSBOFkKW(D6CgSK jz2ZkGغC?" #3KSy&+6[Šۿ޻ss<163@!,Qs<'S'sj'K >7#= ;K?Ө|CAA6% T!Üڐ:㈦GKV`6i–FPC=5-:6{uSAv1Cہ4:4D`G+)cġS67Q΀BNN[F L35q{Ē+3 h5[AHUܭ :saBd<X 4"B<ą:M6d\`ͫ,+ -S$Xv8mS4A[E#rĄs5Q|4@x[F JlEKXYNYXMDiI|%G.MQF$,sQh$s&'P{7*[C0XTtAJНU NNu!l&5&+Ȃ,*x%iKq/,QC;BZ܄Cddiuڇ l4ZZ-} *(W@70N9& -؂,(PaꘋnjLl_aTEdM4ɓVY`\uɣ[Zn ^2U ѠHhO3AՑUx,.+ի5z;$MFݹ!0v\NXhDE1_,}2MΠЀH`JDX]Y+Ђ.-)H_3>3V%زi=ؓLIG,eJGW`:Ix\\Jh"X)./,h`*7[*b'aF+waX&[PL YVRШIp5-٩j1ЕK|)Ȃ./,З] @/> DhŖj֍XwTIL>l.Yc턅VXTHOL]U>6()е`$p*Ђ/0-'@un pJNC"eXw_čRDåA6fZZd"VπfJxCľ-Bob`.10p]d o<+B=DǐeE ee6`X(l1Gֆ6fhW0bSK&6=H(kT-/201,]miFkW ؁-X}_~c] R!#`~j ꩶ[hdVfU0QxKG&Yk($)&*104@3uYfHԶiu;u jShNju Ob~ZlhYlFX,D[&فЁhY,2P6`4 /I ЁkS)leNJ"uUuną-ݛ0TAnnnH`A朱Ёp8x6@嘂&z6LDkG]%< p EU @&T˦vfFK;ҙ1a)-i9:賂`#oDERe\ Ul 2'q2YU85ׄJEh9F_[߄+Y<:Ђ"ܾ%7{3@HHBM b%/fQjW8VuVw6@ ,  "p 2-<?FIKgg_ziiI8tTnb)\mˀe,=-g%bsGw'uSGRwoR3-1" "89d*+G:< pfGiX~*?mc@Q ._0?y&2/fy_u#7r.(Z*.ac} kfR!r#m火P,9'؊ <`l'*qlp1OyNRNā5 Qɜ&p(+0g2@.e=YI|sȟvJʧ r. &/qfm80cz #Y2X?#E=?:j\:rcG FL"FM< )rҥL:}%jTSTjX2a|e󕫜:_ڤUTJ թGSdU,YfѪe֬ZҒS>iIQ?zIC,VPr% /cΰsOE,atI"Axڌ"eI >v,̡Ǐ!HP2F5hId'O&IdfLji&l5zwѦ.cJjXkњTBq: >w9#K+XlL6s|id c &GAYD@VtAA"@2i#}(RK$Ld[NJPB*KWr)ǕW`EYh[`tZpazn"\Ʉx$AG_\= KDqe 0`%h‰j^k"lM#&QE *\QQ32+b(ONH"qݍE\ hZ(%)UH w!FR0_?@ qRdl̑i2$m9!JLQym7)O2EhRcrnV>tIˆ!#ax/{e H%BJ &}]XDBgZqzr"Vrɳ2'+/Y;,m=+ޖ8gX;4Ѷ *%gvk!^|q^F!H'’*$1ad1JAB AJy ]H'*4,_'0g) e.ohF4"VHgihD!P7 cP%(P ݫ W%, V =P&$R|;sgC}H'ܚyȧ Q.yL2!ynkW /³)PNL@BwF&qL$!B?n)R.+k"vOv"lL,eirb&eA~*N-q% MHBF*}G5hd'Wki#AApj+c9KQٝg"9 `qfsC3OLP6ym4!7t:E+pԨ,`" QB# n`*jȘU(p VH(*la C ;H>WKH*&K4_7d(7\7&>"'3$yHyB#Ef_ܼ^?+g´HhZ5 sK̹P7IhAG-R) ID$4#0GXS Y*B- NNB(('p&#?(\ƍ-3&B6$I̅T^E]ؠmd! $d,`=$@G`,1KfsjO^iAPA>xLFtԊD)ϸL Egڌbkɶ,ݤft'";%DB#PdAVd B@Hk4ndS0N.S*r'Vr! ڊnNXת]:ȁb&@ wAn. ުﹶJn'Urޒ8&"Rg]1 #\TYD"(:$<"(ES6.n'om ,oʠ8vS2fmK*J%?.:SD)tB,B"04'APpnÎy2@lm.$G*; g(9,*R'$1!!#y ي'"!p]..!ZjtAlA\‘Q.kkZ&xBɊΜS)`:o W!3B"!B%+xm\f!(#LrWyd\-߼!Aej~"\IKS< ,Ÿ.qR%*b )K"d! x[)`Yv!u 6[2l:A (epq&;3=߲K2ZMH<Q@#,! QCCIM4Gj(Ed5BHXkۙ4lz ;I4o$[Z F~M/,ˆ *! B tڧ͋Ds7PϓQXIAHL`vD)AE Յq[5\s]hsV*5RaQAAk;KBS;1#SMTk|7Wә htlF_A` F+JQl3Ek|q(Фv>_Pk t#LB3E'aW#s%/B& X0VjxTe@|x&:}Ƕlt~~O_9-pd&I4-8܁"|QNmY,3`\x@A}) y+ 8A,Aٜ77-}˵KŢ4=#916 pӁA&4J =0. @tA`Qy @ @D%BJwJ}5pt :nqE@u[tqCAxqRP$ Wa"~|  $՘ Ѐ(h7Z)H [;'{/][-B=v[#L2!灓Ӂpɻ{7B%@Z%Lo"LA8@ ̀ A{Akb+m43<۲CJi˷ '$\\"7xi;<AA* "Pg5*<^|`¡|$tp}G~ yO@ǀ Ѐ   |!4uCvzp`=o W˂=Z´c$ȷAߛ ($}K~Bv2M:!Ā6%HǺB=ǀ@hm:g=]J5i*1"£AD5j,挙2d4$sfM<M4 +WZBUjP>q$I ;k`"3g7j̐ 6t(d 2nBҦOLR +VU[Suݺ5*VcY2{lX`rVV8 TL$9RdH=vYF!C2d4^8FL0_!MӊSzն*LɒBzBʼn#E"$ȏ:pؠ32fCH(X¨Ç#I:*u*լ_Jv,Zjٶ+nSByķQ"Bv 0_ 0#ɼ肋.7&ZNJYXҊk.> eIR,h" -xͷ.n# 3t *F)!f,z =LˤI=c3 - +B-@."M/@<iĒOLaYhɵ@SP2>8 +X چHQ{'JP" -XN@*H#zdY&u+JTLM,dCу7  .b(x ( 4M8"-C 90LDIYj][jX^YŔO.qC 2Т )`B$tVb '+BmY$K庍L_9kO4C1%H Bn C0BP7-*TeBҖִDHQE-p ^!C\B$ч9 W0)77|2m?r30D#& U*;ax~_8A [M:@, R2kp&p[x# Q_E,VAMD"Qj`-8!}=( G¥@؁&wX ;`G]A vTCĈJxbO }B#!wq YbD!34_l^&uHMF@. _$RTՒ`)K,$!=! ϓd> 얃5Gx㴬;R"PZ-r^xN1包*D | ]W%0OM?A+@,3JlD*ч;T`Ul5(ĠO` !?BJ(T! ҄"$QMS> FPP\(P7!zҡ(z C5E:[X$XU'. aN m}k\yB׮APz% y y, `),'@m8w K-Bl0V4Îe >aUͨE'Rc>xʫu~0#, R3' Yu:yNMsX/2n<N]O0g )u˿l_3¯u9LEI̲ ]BЮaӾ^`8mar/G"$NOσ&| V</`O'ZEf b$6B.FI.Q <0a`{̠N:DZa:$oΧhJ^ 1mKl S8( O|MgBXDC dfp k{ ni  k 'v*)p ΄k J*8p8h`F ؠ![Qc-:b1k "%+ܮ(`sdv\v)n/LLk^AȰ-E$*\ A { ֤ @RaQ<-4AZH΀ ` r'8lm1/)PO1檆"M1z!pa5^<(̭QO0@  v.r#Db7C~@) H!_oj5`@ rG6-EPʠ2@;H@{[3zs=\=u0't dkkX CT Pw7۔7 ;Dq6oG$.yg V>vm YӀ"!j,vRzz3@%\wVxJh!KE]] /pS` ?qrG#Ta|7" >kڐa!Z+փI+8cI?w{W85'wVL v`J6Š d%!o̐g E i@ p,n  l&Or1WS%I6B{g58X9AM_(E(s~ IP @ 6P2(2%Ur.N]8z#RJ;9knAkf;8B惹 Et8%&T `XA#4 ^ 2^JF(` VAW3Ykk]ޥ]j4=]b)⦏Ka g@ 9c<9Ocn1Bi Fi["m jzg-w[0)nPId]0:^#5<@YZ' b' $!b{^4 wpJja zZ jw*/)Sz;8a2.n.2A6:aC5F,8dضnؖ8&!aA,b GcpѿdF!] ` `g0A]˵zq\CNaGa$>8A2*a"!!&*A{<uڤ %K*Q4iH-JdhP ?}$!EN&>U"W6r<ITRiT4k̩*j-wԧM"1B4c>}"J<:,Z̢[+E)&%RaF Ϟ}d"4eUY&pg]Ji}H klMno"pLI# #"="I%t )v}ZmB))v%$N>#,Hb{Xd2T`$ahJ*BFH%[kz'n96";aQ %B* )vyZ *(%E"_:j#XHr~`%#a"tYff~2vi"T~zAv\y 'Lə=FcB Qzz V#S֗rXjd (+$bf0YEJؑIuꙓԧO’l-(1RND-RznQȧn#3+}`cHiC: ! I p Bq@;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroEdgesPipeline03.gif0000644000076500000240000003213610534177567024563 0ustar paulystaffGIF89a!!!)))111999BBBJJJRRRZZZccckkksss{{{s{, 80p " P@P4P@!IdƌR49cė /\( 8C2؉!ǟ[|ٰ`˓^\RSBUdI HĞ_@ 6عa%{Ԡ!A n tx[z.0 HTFj\S`2a pđs'(Hڶ pDzqnR#&pQ%iq :&9dʡ-\n%;c i`®x[vd״ ~˜eç]0)sS)Re%-EunHADTX--N 8F8^ xz N.VD?I(vfA8(E6W]rZV)G`7q@0%>aTxj`PiWz݈rDD&U)IՒ5]Fc66L x }NK]ajVg'($S)vKzW}Y*I!b*`'SCԙBԟwWeER:%jsND-KZ!i`u ӥ2@~JPKxcR~mKGI8U>'Pۅ$iRF7XC^vC ,6cH-hPB9OEҙnV8kհllj iY!h(kr,kD\-u2F5:sܐz*WUFA:ƥm#p7lݵ{_f3P?Ռ$ϹN? QuygB>MH홥^uk ,lB sRE|:6ߥl\ ӪCkUD0nA %m2DVMГ(u)ܕO''MQÞBd}kɗ{#Fx+vW&͏:|jތǻp-l8 /xYιr5pH Rz3=-.M&R9Qʈ@٤(9E-.LBtSr19؀vQM/B ĴZ"t"C=~a$ʎ#R@z[DB!y,Ɓ ; (Hȑ9"_T8_|j]EI@DT8# 0mE!jH0JIipBF)d"R&e/{bG-7x&@,-nT H UYc EvkC&"gb,)LtSSPw$g޷u h@4nipL)s"%N"iB,f6#iXn>ǀqSzl:X!-D.7HyETchk珞2Y_i+2] ɨvG]#YɨHv$i] b9T#PԬf)Gc/pK MÈ`DesmAʨDMl$!nl ͧ#q1KC#/%$R*x"2#t8ȈJٱV~Ê<ҳP*_me; 㴂eB-^zM>O+ʎd#`X^7jˉfKlt$rQwwNxٹ^/ݿ]d $DL׊vo6kK^h3p”lտ뷰&^F@ c:uyZ(w{5UQ5fG8OM0~)Df!MkUz"~V D߭`v=H~tM8| 0$[VZ2o/R!lՃ#]; @KӴA[!G\ =F)VbƳ̇k9dz$'s}  XK%=tQ/"8s;$3%XID%>zӗ|"%rYI+3w&y10}$Y=($C9{zg9o %rE[ 6%$Alj #0CPhw=t 3s&!EEG๓ǡ2&zq!<LbCǗ6CWJ(4fs'gDVYṠ UCq`Xkͣ-sWnwYjt&W4t.Ir2Cqtգ!6fzXj MnYRV's& iRj9`"qħkqB!)?] &A(NsZ r* Oa9c/!c#W2Kaf[YW($ }HWGVqZnM%K"O>āk99H[ %Ś| 3qt)y :^)IHd蘭Ǭs9u$%i{ejzQ9ĩCQʐCgڑ0ADN}$=AYn}# V 2>$!vЁZP{H Q|gKsY":Og]#st6&-*(Բ-D'Jc: Z"cz AұEk`G"c ++CVzфih =v!6V3aL~bo V([V鶛@ʼR,ѹ ϴ玲)f4f͡x89b2ZEv1Yqe2{fJՋ%z -ErVk9[qq3 |V{uaQx6]jVz;Rak6\`R6=dYg,(`LSi =UiZG e!,1tz'l.X :EB3v:8;{o2-B|{v?h Wۋĭ' W;?XQ% ƛf'};!ː+M!"3W-<)s^n^{ O 5W;3I\YM gf)cSlɘC gO i*Y/A R+(Y061K  Pt׶*=a6ӵ/x¥KǦ{F٭N+2#"op u77˽cOP5ERS` T=3 Ίe{KЧ 1=Lڅ梻D,=M8\ެ[l' "~IvW}K  !}y7yXMZ`,]S)4Qn񉟵6FwDycuL˃M&jӆQ9J-tK¢f[v4LEi[sFtΓykķ2?S:x&2XѰPO1fCbuBc>KАMBDK9+ݔ|1줆׫hdm++{|pܦJG7jȯĒ$ pݕ,bbR|þ1`e8\Q ՘k`"5OR J{ ;y"b[I$NDIH-a%/n^Yg`? #"+Z޳;^RF(1Q^'F7p/K)x 1˞=? oW;GHr?џ%a,rR.iBnء.it>i179B{|"RXiO m;\n^} :Ʈ}@%KU%iAL_]A~SR9u4YۣY}` 4j?Po/ǨdX+& "nH%y}B!~eߙmDMDO o3W*xyx#dkAznZR.Cjˍ q7FƩ#l^= 2moa$8"` LtPq 88``"A:D@A $Ɲ2X(U*K2h(R}<8kAw6d@Ή)-bԨcK .LH X s $hLrtԨO&56iV[ZYBfR\ ,Kפݔ.*ثL"VKcn0$&Jxs&v>iuq32@.Wnn9ڷFNH<(NznUq\-Dސ]s3 |MSG+[zI'2Jr+J WF/>qd>8M\a(y_zǀEi>cؑ:?*Hj45u{!§/XGK[:w6m-}B/mU5=Ɣe6J Pmfe'-ꫠMT&1\ 6٤O{p%ԳM2|OLhDNJъ~΢-܂ (J@&y5ҕF;roU\'˘VĢItL"CL鹤ΆF8ētw53vȊZZ=xcaHXUp1 ;NAQ[rTH ?g6zSZXǚ&YÞ>=5 f:4.tmȜh&"SoKnJe Y=3l@̓DMCJ(iv4,ꨂ2&5:ZRN:I tI{`.q8kcN2/=7C f%E My6R*HTpJU!b[mc *yTkKH)qDMoXW|󄤪H!ҼnfyuNj5xID|2Jh["ESDj4[1ٹm@.106$C]Mm˦z.v>MG*8i&=Ɉ׃ "Zyn65$ʙxORzw_e 2wkr2 aˇ$i=%%4fUc~CW{ )&*jrtص[ZGDUĆ%۝늲ڣy7 MM7y_敝H6K|Q 1aK,(o* LeYʣz?ōd|hb5'Dփ wgD[N'x,gel0r9M$Kv uy(Ii^Z./o4}Nz/%>H'6ZʳV_#FJG@ t7LRKYzQDN:wZIc=M"v1I6qNm,:Ӎ=$D6MosyYGctsV}Y9 y;q 21)kb3c- 1w1 ä] #1jِӥ[ŒR)l/+3?uk&hH!VaC3QòBGI%Cy‹S:=1%1`?*Y9")B0;1F2QBk ˲+ʳQ>kQ0UT̙0 {XlE%Y[ůEcE+Old,4џ^:Ulkj E=o?p,'< _tDǬ pn A Kx E t[lӶ_)"o{@R;r(B ] KÌ4B5n04DqC^))Ȍjz;VYx :PZSIĜU$e"OqEZh57S"䈂yǛYw⾈ABb Uu3d26 8MP;. P- 5e`TD8 C.f9LU5])@Sɣ-)$ 寽y3aFks6$H"fvgb=+?a ;#Jyu惆r8N$YA) {6*|\}T "T( dP/fh~^M.fOh2,g\*SZ0)NHG#yӪX@ڑ [Z3L *+7S,J)fTpݺ:9됬?QٲVQH빐Dcx{kN~X~NL@vꇹjlx܉AŦ::klQgklVr3A0av: ː%!l@ ! -g=Y >.,8naN CrG4hc n nnkֽO1Hon].Š(jf h@uևpnn(/vl4 { I$Enhͩ=zf$hPF cAЕC*~a:hE=\jhiGDroNVQ˹nX*Wh`_:MĈ"?0(4"=`I3.o8GKe:^l_3 B6ҿPZ{nXB}qٟhg]7nwp^Hùhrt·tn1 ] T 2hH'hB@a(d L@ҁfz|`Ӡ X4hG(D !4`… ("\7&rdIR\K2g~z0'˞}+%t)䦑bbвVm@B ֒LpY }b\ ,Xa `SPF3XB ړ\ <`ٷ['K)TC^AǏ9MpP#Ы>qE^kC֗O qyr9tuu]vQKC%F)gzy\B@C [uPD^-n PO!(H݃5VSUhv^$x&E4~މאF͖cWh\ qMU}\׍g\uEoUpitbe]Y9v`AQf*IZyEAhlۛʹYǟl}\DM8hvt(+ aaT#Q! E`LƴM*mJFDl妎ac1[fp)Уzw{;eBu-Rzg&6&pi nW1GVG[Q`u,~Ci|دNQ5$zjh yEUapnj@ԕq5JS%ӋӘ*w TE{m3|(ݰuD1mzxS*\<ִ2IҌgV S@0PgyWcnG'4 \'vՇ@Ck1Y&:Uԡ dM4vo_Q \ޞzY+Ӯ%8WH?[K4tvsUp_RVPZGQhvr ASQ0X+X9hhCySflFfdt\[剣_8MPZ 2&90SR@N[ׂW؂]OA&\TT 'IU{XY)eUP$pt@NΦ#<.qSSV@T a0yAT [a~[ҫkc/[]/ :Uw0N 8 As5S LWalleL?ȕ ,/C Ljq: S}ᅩ"CVYS !A!<'{U9ltיVt/Ik۪Zc2ZTo[g\ִ%y}1Iaf$AxAY5t7v:MtWʽ7]@9Ɩc^s͜{I>. iS[ŰT)IYG^'VZBuֿapv_)צ%aB(hSQF 'Rzy(H#oSe-e=g}cJn+$ՓMQ*lX  p&U]Tcd_jQhChXg5C 18M-,[6˜!) lHjrC}$WHUPه}vʫiY[(I25{5g2YFnIJte뚨oq.ʆ( (7QvwpWSBӬGHeu)=_[^{v(w=_M;ƕ f A< HrU#&< "^q ^ KF:4ԯSIv!]k+ ؘQ=^OVJ9tU#}I$)[ Jy{\]:ZSץin*MS*$# wX4<a*,f<*/)%; ]}2B"]H SK\ ovاQ7{rZ@2y:.bn3r^`K>"bztr}^z"}Z}~cG"7t {V~w/1O -|rq9!6%Bn! 7G Nt,Cu"B,r||(Kwz|&6C>$n.!Wsu 53BOr2:8G:&_P)x>"QO()gGR[, D\0C& PG8(Txx{o怇҆A({FHv9(Kcȇ R}%a$6t[I/Vz26vAe Vk,b(NQa{Q 3QSts`Vw0^ [s0xz8TYAchk%s!U@ G"G(C;vцABH8H0X(5Au'sbkp{cxH*ԨzF'2d!;CAi8f@Q ,LH~uSYbjjfh,lQ0 JvJx:)>1z!aJZu %.d *D.`p(+_qO;')tKO) 3q)uK31+#uBrZ}<{/1! : 8'*z;C4\fOr P^.!A'5!`xQ"?.qΉ:Rtf P0A.TPC$`B 4Q"D B)=d|M$@Ϟ 0(Q (D i0p`   rK U<E&p N= 9:PH$aE '^PB ~-KS) v j\P+Wb 8eʡ ut2`X(L@Xf CxPx1cO>,=0O^]u[dH +,z 8zrK(9C hjɁ ѦJ$L) lA,k"4SLSO 6i+M~ ؊J SB*8.'$N(]ZN (;6ʋ1 nb.}M]+ 5޺+$2"VPLԄbϊNsP A` (r`R·Xs%)Eqʸ`$rr SM3KKنb@K F}\N$Ve6}{dl[=S( Tc!8(o*, u3I*ROX:W3^L/,̋15$&>b[ /&mJtg J8 ]+(JkHL7:3Ze2ivfD57@聈X/Hi@釢nܡ]@x9ŮN;TGк +вb: ><ezҳ KtvmΠe!KVH(WgD{6JCQ=9"- } ׀E~X~7&)TDHJwV Pm >`NK>oY)$D}.< Є_m7Cφ_]6CjA P ;5ԯ̅9KOV5- @2Sܦ}JWKoddRO Ȅ>Ol2Ƣ'H4 Q2y̎g"!Y@!Po? IG$;$ \'jb%;E!,sTjdYhGqsz#o\Jfk:/(Gcd2!L+6waO2yj ,L6+ ,ZIqĀ,;r0u[kLdw'q(鏣㳧&4ad"Boi$1T2@^|U>̓G91(6*!:IM҂|)34D>=MFVGPeT S!@u$΃+zbeSZWQBMS'5=kN2)2V@W_ Fu>BMd]͎{XaIGX䓞T7kOfVx6B]Xg>h5P+^%cJQ'FE !+M +'y,t@fbr2ɞ@HB^!ٕJʲ!'o$ d-ap{~}K\SW)U67ti @ [{.}WO|HrTR\~ }cb$hnJ8`6Hrj`v Wڧe%LtJlmk{FD2 H ϓrpW<"rxYI,\涜YHF6FaS 0:٢3ēa%rОbB$C"aLT"PQTjZ4 4'HIgD" vZ)2hHtkV&()  ʹFS@vg7`a.We"ۮ@m(w$jrk?t݀YtfU c aVx_҉eh&Sq;M0 @zohžV%mE@iX]M"CQJJ؆g!wnwK2A@'.WvFkMU/p0BYNg2.)!06 * 𓽞B$;c31;q?cq?ؿˈ?C#@> (ۺ#K< ,c$ ܑ ,@k35Q2?3` Aj;b{(?2 `›a/7i4` '0* ?3!3ЧؕOCSC#67Tr#(D<=)DuA?{irO  $CpND;$9&67,,1 RS2\I h?3D;,( 鏁+b("+&*(/Kpj၀{FBoLZÔ.1G3'K 鰛HfBˬ$4'X [nD 3@o?Cqȡ0D:5ԙizG~7K:zǂ}̥С [Y`{>BGo*W)fM"fkDհڽx # CxQ.tb 8:0TCb̍ɟ 4 M:$xDT9JKW˭)oa #xJK̸dlD$56c KtMp@/̃%ތL+oɐyp̱H=4ZP[ 6+4*@ƝL jK:TϼHDNn#@t>vi ѕ` @FI C􎇃Hab1"0ELK/LɠUqW> (MtYQHxS<>L[%} p4}kH8RH1QEx 2S2M5FT/C%xt+% ;;QeD`B9A SqӕX`UE-YLdn9Z[ &|g3˩s+Ж5E+ JH aθ){be V>02hցJ0Ojm6gV5D}P[PqĹ8`%K,ԈbS4d צW~k3lՉȀwKXXnlTB?XM3k!h4EVcmypI-?iG J1A*EG!(}Ο,Sȗ9UYcR**08ͼZ ։5HW& pNE=|)Ƣأ9`'mZ &ӫkE=Uv͟Eیz:d0 q3sHZ]bBx8uW#4]u-NCIM Oq@0(&(m]+8 }SjQ!Ɍ_-_R,D k7-*/(U}4S ^][XKж#}=]}QoSS蒀 #09:Zc &ʋM7qbTו('>ˎ q:FQ `gKP@TbC:0D4y%6Rx +ᒩaaAx 3N]ymycۤXU᳍X|/Zs/Z/1;SK+%0 Jd (%<0^18/fp"B3ZZΝ;M e_8tXHܳO8Eef>@T&0fzsǔTOg Ƀe'6P[][ O栉eυ≡fY^Tʿ_c?ȒD-hD@h iJFa CQ <\i1Giٙr~XAP+]=+ftTǟVN7@.$!Jګ.ɂLK.΀-LC/չRN `;x`W2&ũS=QGH"I0I\hP Ô#9R;q6aٙ쑭/_4lh 0m"}ហ4*cu x ^Fͦ^46Vck%BԞn 1<_b & 93>=ъ 9-$HoԳ[c5VS+> nyz-1pRGFд|;9mc ,*7 p5gbi 8㒟DGW?!8*stưudL8"Җ.#w?M'DPi3;MQ|5͂:h347GX}qj}A;1>OymełY.A3 BVS2- i H: FUFPb 8JjJACkbH`ـn"QT4bh*cdIXYc,U)# 2.l5%]j3V%6HX(8^wI3įePʙ;)c XC Z3Y&p݊BW.x^F ?4"LQfnP;?1 v6ؓ64SJo~2X:qE%2wLb/n@(8л9vh ؜_N#96&Wi0ps%җRH, (jxQt+Iz9Ky"1,2j d1 Xd4&l8]Uw7iyQ6PV\b82i!P9ʄhB=Ue#-.EB̌l@ə@s KGw6q ~ ז؈8؛Y O[SOI84\x$GRa aK%]0EX7e[E\- Y1yod`T8nȠ`p`6 > UݐEWnf Rq eNJ1HJ_i-QŔUK0 Qsdɇ,5 "ƀKS2aKY[a䰐bYKV*bl|a l9"v3-RXPYPjeI"<_o)R\* b,zRF-%H{P#R/:[̔$NLA1ful*m]<#(J#olV9IP! E  !ETaՈYA5D8G:DMMUn@,t D ^ALw d  }CKDqPaE>Vy$D,,IޠIB`J"WxmELBEBZK ͟`P LQKTS\D%u$)diQ$5e%a/UI0J[^6&4",[Iǯ`:z|ڤ ΀n!D)can@3YUPPQAdrP0W[25H#VV]h f @Soqڳиh(j(LeT QhiV*cn|U2؈2BG8ɅJ@RW2DYMkd٨!5Y$XU5YT jHhJW\tP*r#D*"n4n: 9eDVƬd"+έd=TPGMIFY+"vxcH[ꪪGtjX؇o ݴ':c(][|@,, lHIԭzĹv*PxGN8^opʅF"~81MvĖ蓬TJ]LjVd{S8H:f[2AT)ImxF`X-,l@@ LLIjTEq""ŌV}0mX!>nlFVfLv-SENn1O-@ۢ.fBܒn-~̢TJLK!391m1=Iz8ex2ciMPd^؈aHM8Ū@@;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroEdgesScale.gif0000644000076500000240000001162210534177567023677 0ustar paulystaffGIF89aO  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~,O H*\ȰÇ#JHŋ3jȱǏ A]8fQW $ 3ޱ( 2tΒ -@i6"4DZ0 Nb]׊,hU,Ak so)fnYݩ &P ^[F˘3cY[Av@N/"'"װc˞M;v 3t=aߋrz2 )_μGavzuA#^j[,2wӫ_Ͼzd^pjaXX(hL|<@N ` Άv vX 4L3P`Ĭc88Cj15At >3 7:PF)TV)0?&N:mYQyl)di&dVyp)pfWcΞ|矀 ̝KK9&袌6/T/Tj饘f饾Dq#ꨤjꩤ|xbK8*무*.+쯸Z(|6撫7fv-.z-ݔk覫qp#kԲͿ,\˾Ң 77L ʒgg, Œb 6$l(lƆ50,4,s, z,@-ϰ\вίPL7Lb4^4Xg\w+SSHchhtmx^ 4..*|y*4G.$4w眧by騧꬧^`f~)\2{)4v)%|sm#Woԏb-c(Ƅ/olO/2('. |BL*|< Z̠/GH$B_0 gCr#D/v@!'VX( hbHL&*QD2*ZX̢(~jH2q]aGw[pKxy>|gJ␈L"DVb8%fAJZ̤%)5Z`(GIRr$G'X򕰌,g KIr#ރ,v^%$n\L2dS#\AjZ̦5Ǎx8Irs&FQFπߠfX+Z A Xy@ AJ[ҘδP聘?XK["` ˀF;bb6հgMXwї   #?`!f;n6"0xZN<X@ lkv^`ۤ1 jTӍ 瘆d!O+\ uc84l@v@9$`,z-58sǃxJ$3XϺַ(d5hRq$?0Nx~F2:QA@2񐏼'Oȗ9R$" `u4IdȃWֻ'%,Ccw$0Oax@[ϾBIO$ KJ])pp \0[Xx~_Z"8$X&x$>l0284YbW<؃>@>x8(.:HJL؄JHh{`iPVxXZxVPTpb8dXfxdH^&0fpr8tSxRP|؇~~(xX(c؈QPP x)a0XxXOQ8N8Xx>^8MbL،(.[ڸ؍޸JPIx蘎긎&8Xx莭(W9HxG` ِyY(`UّFPE0&y(*(Y")@R04Y6y8YDCB9DYFyD9<>OPR9TAbӔN\ٕ^`ٕWٓ@hjlٖj X).KPvyxzy>=9Yy~&I9@@ٞٞ4pb3ٟ9).Zz j <:Z:21 ": &9,ڢ.0ڢ0px/8:<ڣ:4Z(7PFzHJz.,PR:TZVzTN) 4`b:d+ $]:lڦnpnzi 2z|ڧ~)($z( b':Zzz*. `.:&%ڪZ& @,ګ$Px"zȚʺ*Z(Ě:ZzuJ (:!`ڮ %Pگ>J{ +g "0[{[0b ";$["+.  .02:<۳><6& pHJL0xT[V{XV{P[( Pb+еJjl۶n`v{xzxk 0[{[; ;[;@; +6 ۺۺpۻ. P{ț{ k;[{ k ;ڻ 껾۾[@+a+d<;Uf8Wy\||,CQGm&%"u*,|4UqY&ÉF P<>@2Eg 0l\R>>===<<<;;;:::999888777666555444222111000///---,,,+++***)))((('''&&&%%%$$$"""!!!  !,OH*\ȰÇ#JHŋ3jȱǏ Cdd%4n1"FE̢Ӡn 54yM4oH Oq"pUPj5 -X˶۷pߪx; YXehb L`c^Z ޞrC1#"2k̹F@@4r!c#ȞM۴Gn D͕ _У;/wޱXcМT&OMTWxUn6vߏ wƠP& &FVhfha  ($h*tk+$0(0H<@"H&L6d Dd TiXf/Xdih `p)t)_NeY 矀*1 12@袌6裌`f馜f:äCjꩨj* zxL *무j516믴ڠ+@&&ðE[VkVl`+䆛R"p!2;`Po C' 70LTlg=D'2>l $l$q0,4<@ +"H'L7tDhTWmXgm5=D\`Q !lD.m7gqwc2F߀.E&sD 7G &dwHTf2I`騧'!(n׮& //{ ,4G/L$-dw#.e2M`觯7aO o?~P LW@ɐ &H Z  GHnЁj0 & 7ȡwpTG XHL*#BH*ZXbA` H2 \܈w6pш!&# <ȣ>qYHF L"H-2# P~@JZ̤%:BQ\(GIRB' .ᕰ,gIXva`R^ /K=) CH2f:s_(f"X̦6M0HCIr,g"eMr.7  Ā ܌@η|zj @O5co'N[|hA8x GrBh|E(V0oy!N8ϹwϼA3AHOҏ~ԧN[TGӥԌD`N'bt"p [XD"N0g0BO+3[ϼ/߈i|GOқ~{E>g{Gx>B΀v=$n!gDO+?_3$[Ͼ/O䟄~W)gPϿ0Q`x X H0` 0XxX`  "8$X"x 'ϐ Ђ.02 :<؃>< 6!8FxHJH(( 0TXVxXX0+ `b8dXb \# ІnprE  z|؇~| v(% p O 0 x H0 0XxX`81` ؋8 '@ ʸ،θU1 xؘڸX !` 0XxX 8Xx "Ѐ Џ8 ِ Y$Р p0R $Y&y(&  04Y6y8YВ 0 @B9DYB < & ДNPRzSZ U ` b9dYfyd V! pr9t |ٗ~~) x"0 ٘P8 yI Y$P 0YyYR" 9Yi ᚯЛ! ʹٜ̉ I0Ґ pؙڹܙ0`" Yy虞 'Ұ 9Y9О" p ! p 0+" p Zz # ":$Z"ʡƙ Т.02СE2 :<ڣ>< Ӑ6 HJLڤJ* CJ0F TZVzXV D 0 b:dZfzdJ Q &` npr:p]:cP |ڧ~ڧŰz2 ڨj !p Pzz2 @ :Zz "Ӡ :ʰ82 ` ګ zp ZzȚ wj @ ͐ :Zz ЬΠ :Z J0 Юϐ`B گ ' p 0 !k0ha\1` б " a"4m3F  2;4[6{3 uLalAlzI+ F{HJG ,!4z 0 \۵^`^[ &kH!R;?1 a/k r;t[v;P1ahjp w{KנFe{ P{{k/Sh 3V ـ ۺ I!V ڐ ;[« &6kpհ [{؛[ fvfahoS6_ d N껤P gfklCPۿ 6_mr <\;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroRegionsExample.gif0000644000076500000240000001446110534177567024626 0ustar paulystaffGIF89aIII333fff ###[癙n$!, "diFzPK,`x*|/_b([8F$} z}`EӥD[ " ̺x?LNd<@RTVCZ[\ ^azcefh % Wmtr '{@ C%R%v@8hZh ‘ R8P{1$*+2&\?6g""Ĕʈ<)ԫ 61![@ !M=46JȈSEC%qrT SܦB FYRGEd$1K4?Eg2tLU ЩiS N=#N1(68Ti==5lQ^ ɀ";-ha!$)V,RKH_zt$0`@j xR1vl)lċc'h屼ŽR~{͜*l tC8a2h&):`!c[M -Β9b zu>$Vߎݭ˒}=)u p7 g }ѵ_u0N6`f# IXЇl$z8u$`e'ҁ<*` IBƤd5X^YO#=H^)4dIIdVrZex i+@)TXYmIbb J(B&5]l_Xjwވ5Mppaj%ة{'/=X*C5>i z (^rjkV|)y`m'Q'“ &u~ +[or[I$j+/= 佡[ܾpk :.<&1Mlފ8M u$X 2 <(51K3 (CP4$GY|*?*("ûu]<\HǠ44c, 2]X7-N1 '}u5Z4Ug^4㑣T/Hѓ\ %xC֭HW86K0{K֬=yFK)o2i609++_;nRs9߫weAP0֬pp=ͯZ6ۑcmcHTԠ)=Q9`ȓ:^"/ gA00A5l HR Ȭ(Y D+ze.P%Bsk&D QDޠeONSh Cb!(18#ҨF;7ܛ"?:FŎ'(1m B862N'I!3X8/@e~`YlQ+[Wn";-["ۥzY_֧s{&1ugL seΜYh~7(^BlfUp$$ːT)#gxH^,= :q'Pvrn%E<{W&}2[)QC塂)0GL3@fԼDcЅ*KqJ.6F8!z2=OYZPƓ2M1Ф$D`lY2sU >wro%:.2{jn+dˇdC37!krD't)M|%Dfc_{=p@o| ~z 'XZןr; #=l? 7Xy Q3gg`~\"}hwWA_y}~ @}4e}}xsg~uc' |>P:RCwJ qxvp}}eWfЁFQwe8!xj]['.硂/ -x/{18X#-Gbd)y7)/rqgυ9ҔRssG]ctօ5p'RGAg ,DrlX0nXhpX{Et{v,CwgׇjX\Aoxh78Xvx{eu\2lWAh8IEa_{ 0&{w3k&Bt3hSWVmIaw!4,Ȅ+CHv'NE8zj s11g PpXP3wHrX0X0n |Ȍk {R2R'x'!2C~Y&V2Pp|H# %ى pu!_$/Y fWߑUeAǒ1Gv׉ b1:IQ=rTTd.x]8)pyP2?fYTu1Ww#Bs$O^(~SEWМha?kjPj Cfu4g1s<៚&['Z -* Z#"**V p0xY'ĐY$.:sPbNi"8w `/I"SĤ4jݩUWY3azKzM*OHT& `w)\zN4djl:`j*xzXq5ĨڌZaaˤ 5=P&6S|VO: i`=9Oz8@9Jwz3ZC?ʊdc|z8ۺ'Z/Ьӊyy&ઇzqI:htU6@su?궪vJ%3eWeR K2 {5{,;G# :%KhH3;4 o *& 9 .J'CE kٯҠʴ1&:(x:<i)'U *@xڲԶbnpj0$[:pS }:ly P:ԕxϵc :`hefӴpuv˹k戴Ix+[akKmi+/(uXw[ĺWS A; hF%* jN+W:j{ ¨$h+ :A[ϻiJڿ % @%A_p <>{wFW#5bDli9'ܳ+kDKK,,EمZZ0+P ?߱/kDH|JL/Gǚ v"C nJKgܚ 78nrp0xuwqHa<,W7ơ8yXhs5#]q{tfk ,,+l^^C;]Z.>~65T1@] ᜋ b Zˀ iJ PIϓ䂾+@.|,])A M`Jn6Dׂ-D\iBB[Mo `%#!1n csJ#BM;ԆK-zn|^~n8q6p|m}leR( ԉMmCέ64_^pt-Mgi놁v{A̿wk&Wsƅ>.K. k9^Ӹk^7p&MHDr[=@ҿ ȺǏs5m O|]c6pY$` x[\=K(R򁕣7%I`A};?/S's#^CxNlc6W/sZ/\o?=z9d_-fXDn-O~Q) ap~_#6 ڲ Z$/svޢLI%ysOOH뮋-q+]3Ҳ_hmhk__įj]R<8/فOJϷ/?"GHI՟АͪF_/ Y'*3L?q \ |!' AK  SB3ڤKOds=\?,PU^á^W!#O !f@ӣ@$d`'fæ̩#e)AWo%a,V/rO2 p 1vZ3)o6t0u!t ONq:89>=8:{;)Ž| OE@DDB8ЄCEd(ٱԌ%#f1):~<2H% ܑrJ#-iPLݔO$ 9耣I ,QMW--0|`TU,0q6SbԅW} bࠅ#6PŜkdwάYߠMfҦv,5_CoY`(;m/ds|o.%qg,[ߞYܿDXy Ҍ:.W~{`5h[G?WFFS.6 HܰF%VQwnu"Ɠ $ _~bf,(#ZmW0S}@]m|ߵHUz55:E vs%IiuwIY:Y]~ad OUAp%t4wQJ;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroRegionsHisto.gif0000644000076500000240000000375010534177567024320 0ustar paulystaffGIF89a@!,@ڋ޼H扦&-l^7Ђ>L2 <S%J+ MۯLNО4Q^T{!|[!6H$Wuh'%X $5if&z );KR ,n~nˎ[> p?oKm/@_5G^AqKPYÀ#HqD&n#1C FRIyM6sRWJu0c֚uҧ4 } hD\Z鸧PI-f%SU]DT*,$6\"C2;tdu 6[Vۭv+ \nXcM6V729灡tZNUd=5PA[\ 6C7MCog-\gȣg /M؍~ZyS_>=AP}4Z,ɧxRҀw`)f R 5҄V agFxQr)sˈyء&j _NX"("1c_6r#DhVH_j5y/X䉭|'A  9$I9'hy&Ym$9g *0BYɹ : \2z(@@zjiz)XbZ'lj`dn&~׭) {,|x>[*L+Y{:{{-j!閛+]B.f / G +b٫QQye[qqLou-2١rrYZ6˫ a>c̳ ~2#-=*N]u$~2z7Qc0?fYG 7h` eu-L-NǏ>_}뜲>~T'Jz_=eU˟W@bS V1᪃6.PKBΪ"Plgq Eh@r$Tn50^BB!K_fMQQ>EqJV$j%|E1NщgAaS;]cWD9JQXyeG7-3ld e-oT$8މsaI"FLΑң$IIqe "*AL-]ټ *1'MH9Z4[qdI 9$5s.Y\~_&Ky,IgW9yHDd٪?x<&iOs:#`h><Pm2@>^j =[Ѝ>N,Ϗ3%u'D/J~sK w`)(HmSm;Gӕu$N_7M2ue-Rb'e eS"PO[Sֳ>3`m z u3+ZxFumgWW-bU0fUUdUʛ|6pSv/c/7!]-e(ԋ$r-bQۊִʩdZ5WOTnɶŹ+sq{; ݗ>שE<Qn.}mh;Gm{ w}q' l[. X+xD"w`AroH`jȡ0n,H1밒8LơشLpcKb&IGbJ# f:.(J,aHRE`;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroRegionsRemapFunction.gif0000644000076500000240000000267410534177570026002 0ustar paulystaffGIF89a?!,?ڋ޼H扦bnۻb1h\sd:{ҨRimڵ䲙Lʛh8mO?u7w8hhHYfF6Ey f7'J[kIu ,N~.~m O=_? {ߟ|G@sF‚ ;ˡ0bÉ2 FY\ | 2ODo˖/k\1sfM7UԹa:-8FKjhS**O3FjsG׬_Z%12K=U^;+i];/= *B `1c{H1S܁3d}W|tc:~Wׯ0;u̬ޭ1I&v[Cz+6sУ۞Nz1\g[IX_]%zA>{dB|GG0a_,h^:H H XH:~Ȍp>7!(b)y(fc2Hcߌ:8hLH$1F>7d .ɀ9:cRR) Q"V"b;hda]8%G|ɦVrb" y' t2!}ngmfM"ʟzxh*8JJ^n ä=⟝ JCFi#8zj*+Zkb-Bk8l LKm^{@R~ nKn枋.) (D(k?K; D 9SxF@l#.‡(WDŽ$P$+D 43\q%?*u,y#4@B1 /AO_Y.|crͤ׉Zؗ7LW[}MJ1(k0=%~}X8}:޸tByπ]c9_ ߦ.޹盃xs^z dA^{;8.÷;wo{*9?fV'~ϓ=b{^|W~+?g ?3a S)ؿ]P6r @BN)T!NZUB CSC-yCQAt 7CIXDE1TEw]pY DNaTF}A i E)FEP]:"S|10F$lB*rld HJr%/Ljs&#Or,)OHBFG`-Ȣ,m \fIe ;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroRegionsSegment01.gif0000644000076500000240000001623110534177570024765 0ustar paulystaffGIF89a:::fffbbNNNLLttpp԰ ***33鿿?yyXXb??jj'''L !,@AȤ`9T:B:Ax `6$ho#Md2,ExB>Ke{g`h`xFIHOR^TVW^| nD xeYaGJKPQ§KOBjfŒĆ|Ȕʗ͛^WdU7Dqz:%b ĘQސ"F*Ӭ\)D R G.e, @E2ڈTxjM$TKAS8g()=UBu 4)ieB]|HcBUڷo J 強 $V. (fL;Ⱦ&Dm3z~6*jRNWVxkbCoKpM7߿NQTKP?s3 ( Pt]-x\Y41@g,Hr^M?W'VL5cـ a`U` &`0Ux?!vaKWbס؛"QLQiQ( hT8GV"u]f&Eـ:F𣠗BiWV{Wpe} AB&)W] r6A@2 )H}VgJ(d1iw(BݤT:DUEG)0= SB2h)D2h"J,"(ʺ,!wŵ"j@z([u&.f1ʝ{^*N]f2.&)[4ѱRiȒvɗu*myF/!.?s` J eG( gnue:V dGu'!"ߖrv=]F&/ވB@ğ^:ҳ=aO _f^Cs*8L~ZY'= K$H&!!X;C7w S,ܯ~k ALH$ҕ.8`?@ q:mHA+-|4=Qbt8 2tмcD! 1J4 X(RwBAaE,^{%rt//1(` jj#]3ʀ!ֵv= Ϧ>h8Rd=$  $WN/m\C(GJu52|T%XDWƒ EE jBɖČ A &c) eFܓhF󀈴D4^U! Y7pzk+yfєYyZ'}Zt 8@@`!xP!0.5?@-T=iBUJiLw2(Tʔ.Qԃ?BI2+5@ӎ.Ӡl3LU`aSUgt)[& #+J~p+SYWv5k_+Wnuk aX(vdcs I$-`uP k^v R)310L岤ӖSݶZ@E[ޖq4O| \c 7uŕn 򩹶] gj\f =u!'v =wyk(/(ޅ5 t~Jضn.}[ku_ q6/``H ) `¦>UawUvDő&@ "h#c()5JHBo@y$U㦠Mr]yc8ȪrPB/I1ƚMUЀyp>]BruNLy`hc4~n`ʁ.ylNܼf8o@sB@] W ġk XG|;7c^F;,2߀{W}'c'_gh/W~KHH''SGjw <v[D@D$$c肀0 ؀6(O'`;(wXF#FQt}@hqDh}ksg1ba\VrP0;C=y)@ByOWhH{0u`~"T0/8Ѕ_a(Ffexv\Ox|7 S| 3~yXs5 @8:ZHXpOg<}h(gx8HX7W/M@[XHX (H?.w1u%Ѝ!vEfpI;U6؎]>NL}r hG}XxW$W(m u}U$[.X ~ <KiX |d, 9CD0PIHĕ X"yOP&@D 0H9g*fO,t:`|MЎs@iEA9hH9JyL5SƎUZ\y<^9P_V cYpI} jɖPwdVɁ7H>pUiJwhV`G2 A'tYyxI9 ٙVy؏TV y*ɆQXt%By=DQr /X I( ƙP]8`~HIwF ;s3 HSCЗq󗞺ٞPi<M<FY;z3y Ҡh 9!Br'} ukFe9 PZ j:91颡9Wwf:˯@۲ {rEBG[IeMIe-g I LZR^k yKԳdugk1bwԶ,*7($J[(qzIh۷ං 8 6\*y[ P@oB0)y@[kQ*Zviۺɹd +*˓'tɺ:$J ;Q pK $e۴~ʯ2kj K 㻮koɾ+ כmgd۝3KdẎ"ƐvdH#9Ѻ, 'CBœaQ (<*|Kp )Q/|ÕQM%'|@L+Q"iɿ"Z&`bٖ/+TcI0|`Lce\Li?%^u,L\y={<}̮k\<#z&@Ӌ*3 yМ 9Y<8$+:|=}Q>)^@d`ZI=7KM=?:ԖP|{^ݦH(aͺN{jHEP`hhh,WbFL{vфӆ}O؀ B\7m}m=7}ɋѠh͸\k < FteEߌ*ӴζוH Q$H YD;E}ܲtYiD1iϰ|){ЃM} 0@a)͸ .1L m]y7>d[0I:iH)}7b=s`Nn\ ]mS9qMڟ[<&X5^.y)<7<$>@ME2Kn,M9yCz&tNvx*]LiF NsnQuw>Gnt&]ܼf̍'.pgL ޡOHZz,S@>])3*վP6 |Ed`FI)W}B] D6=^Z,: 'NO0^IpSMZf~-=R \Y]> R ^eaKq>L!VZ k06ͪLKG\/^o.b_dfݔprkKX7acOegsi3k-_au-@~=Y%\;tSVR<\qxl̜? T9QP;+EF/~Oa/BXѴEN(,n2 |~V5ZJ:Y.3_ OK?RN_ާ2q_Y@ELBB`P2Z-@\Ǚ m[/J%t Oibn K1 A brN3PHP jq ! k AtN/ OדÔ0bյ.vؓxt[/ B5UJ;~H ]92$\Y'PdO4` BHs(bD$Trh8RJVy8D(!X|31l2(QU_ Bja)ͦ48RjH*\ UV/]W} [4e1D `ZV` ׮_RaLak= V([bt*X1;Q&h9 fpcvH牪2#UDJN #fl\r;A:L%TZ~=ݽ#NUX o#œ>O C?jUALocy0dک 8dۄEΈHTyP$ pqF$jsr$Gp(R1q%{ir'o&Đ <bD1S̄Ƽd^Δ5׈7,MN(I5 LbNIvP%,BQ#=Qp"MbR=+5@L7t:bԃ.R| '4kUCsuEXaG0U]RpZE"T%=ڙUZeGWjK:2Chc#eI~iPL&pzkkcs/ r1"ƑkqnHg$:f;C]6 g!XƷݲ%<+/C/pӂQ7@uYeܚ8=q c(I_Х,ho0iEU3q0ut4'Ƒ͋ 6824-v1 ;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroRegionsSegment02.gif0000644000076500000240000001622510534177570024771 0ustar paulystaffGIF89a:::fffbbNNNLLttpp԰ ***33鿿?yyXXb??jj'''L!,AȤ`9T:B:9x `6 ho#Ed2Ѩ,EB>Ke{g`h`xFIHOR^TVW^| nD xeYaGJKPQ§KOBjfŒĆ|Ȕʗ͛^WdU7Bqz:%b ĘQސ"F*ӌ\)D R G.e, @E2ڈTxjM$TK AS8g()=UBu 4)ieB]|HcBUڷo J 強 $V. (fL;Ⱦ&Dm3z~6*jRNWVxkbCoKp]7߿NQTKP?s3 ( Pt]-x\Y41@g,HrM?W'VL5cـ a`U` "`0Ux?!vaKWbס؛"QLQiQ( hT8GV"u]f&Eـ:F𣠗BiWV{Wpe} AB&)W] r6A@2 )H}VgJ(d1iw(BݤT:DUEG)0= SB2h)D2h"J,"(ʺ,!wŵ"j{@z([u&.f1ʝ{^*N]f2.&)[4ѱRiȒvɗu*myF/!.?s` JeW( gnue:V dGu'!"ߖrv=]F&/ވB@ğ^:ҳ=a_ _f^Cs*8L~ZY'= K$H&!!X;C7w S,ܯ~k ALH$ҕ.8`?@ q:mHA+-|4=Qbt8 2tмcD! 1J4 H(RwBAaE,^{%rt//1(` jj#]3ʀ!ֵv= Ϧ>h8Rd=$  $WN/m\C(GJu52|T%XDWƒ EE jBɖČ 9 &c) eFܓhF󀈴D4^U! Y7pzk+yfєYyZ'}Zt 8@@`!xP!0.5?@-T=iBUJiLw2(Tʔ.Qԃ?BI2+5@ӎ.Ӡl3LU`aSUgt)[& #+J~p+SYWv5k_+Wnuk aX(vdcs I$%`uP k^v R)310L岤ӖSݶZ@E[ޖq4O| \c 7uŕn 򩹶] gj\f =u!'v =wyk(/(ޅ5 t~Jضn.}Kku_ q6/``H ) `¦>UawUvDő"@ "1?48@ S[%$!7< qSЀ& >2# d Y850LTi\(sxxvv ?90m5cLʎTLg:۹xހٝt<4C)oMjHyn{6$+ԪNRTy[.y,^ .m!JRjh5j[z<~t{-_alvymlX#ʖ!l C;t mi۸ıöH&UhQ-?>@pׇ;}.OL‚Dw ,;AP9ZF؜WBθu-Wg[ȩt-hһR׼ P]qymQ~&+:X~@(L7uL6!r}hz /#vgO̽q ;:Nfs=AgS156y7<>A^ /jρA+|6.?_,+CIMT" ߼{ė>W~tܒ(|}ziKt)Ky!^IZen5 A8KB2c~J~tt0T< sDPe%Ia~~~z'W(`;d(uwd/rB@i (s27g.(w`x~rP"~"FуR@BxGf7{)faVf3# CVb׀y]j {NLq$i } kGBUu0 VwPJA"D@g cnsi`؇88h0 yoY\(!)O@ yϲB, >h{xfIXepGF}Ms哒46xpW;3 RlUZXyW'q)sU iIQgw[ƙM&鄈閦iyHiQ OXZq阵 h酃Y0 37P!<ؙ3) {F岞džIЁr Hxyii 9!Bx([֙GNYJ: "$Z)p(y*v֢)O0x< Z8 J ٣>zK~E d. iLڞbQz9!+.>Z QhI(tHn)@9:8rDB ȟhفaSnIh(¦,CvVar1r{ꤓ:0*9WȥIp;>!MBjƫ'/=:ĚgcQڬΚ 6B2H/y=&Dg1fu芚7EE:Myj < O3HĪmeP- jyf-${E[%9#S'Ijfs!{^l8,1ZK,(* в#7 5ʐ6藳Y e-g I LZI iN{PkuSk1^ (drs/WfIT"p; s{JjPRe+pkj '붒zǝ@fš}D-u+_)z򹹜ʸS۶; 6G(+Al78H ?6:9JSז W++!QK+׋2nޫ`0>YN+KPYO%`G{:Q_  >:p}| <$ l I,@Vj "BŚ \V%'ܜ3zZlòI 7 ŵ`&PTao:ēIY wS| Vq]Kdܼ D@MY\^`IU`[x'IAku8\Nk`QϜ PrL?B`+91嬣5kO0 ja~1%0wnj;\4^[dZfxnZpšvi.{I}^H9=kB)Ǻ4䕑FjF] XkNQ(Ɂ-}AOP>Sޤ֠2>Bpp} 藾Os }J[p-Ӟ`Ϭ nܮoZ)Nv}PSNVL -=R \d= R ^Ӂ>,=@~` d` .<#u60DR4gI qGr=2G64G q01I1mfD*)) ,/PL,'̔NFM~jSbPdt:׹ *k=#MPsCFIѩmIR3R 0xm(ޘsOq 5Q=TbUU7`NV[0[(P V ?|ZTfCk,ٴ[.&QtA=ou]pWrC;7;+`%W<^ZrX]3weUJ *ǕGѷv- P`L*`yOi>F #2d@YNXﳞ--$$a (NJUcIo8 4%Ft3c"xUЂ# 8@}!tWBt)t !k d\Z 8!xx;p:aŨ <ipA5}#CbHzhqmF1|$χF6ƒ@!Gqtx8H 5a|҉!#X!7o\"MJ%L"xXKe{g`h`xFIHOR^TVW^| nD xeYaGJKPQ§KOBjfŒĆ|Ȕʗ͛^Wd7@qz:%b ĘQސ"F*ӌ\)D  G.e, `@E2ڈTxjM$TKA8g()=UBu 4)ieB]|HcBUڷo J 強ӄ $V. (fL;Ⱦ&Dm3z~6*jRNWVxkbCoKp]7߿>MTKP?s3 ( Pt]-x\Y41@g,HrI?W'VL5cـ a`U` "`0Ux?!vaKWbס؛"QLQiQ( hT(GV"u]f&Eـ:F𣠗BiWV{Wpe}AB&)W ] r6A@2 )H}VgJ(d1iw(BݤT:DUEG)0= SB2h)D2h"J,"(ʺ,!wŵ"j{@z([u&.f1ʝ{^*N]f2.&)[4ѱRiȒvɗu*myF/!.?s` JeW( gnue:V dGu'!"ߖrv=]F&/ވB@ğ^:ҳ=a_ _f^Cs*8L~ZY'= K$H&!!X;C7w S,ܯ~k ALH$.8`?@ q:mHA+-|4=Qbt8 &tмcD! 1J4 H(NwBAaE,^{%rt//1(` jj#]3ʀ!ֵv= Ϧ>h8Rd=$  dWN/m\C(GJu52|T%XDWƒ EE jBɖČ 9 &c) eFܓhF󀈴D4^U! Y7pzk+yfєYy2'}Zt 8@@`!x!0.5g?@-T=iBUJiLw2(Tʔ.Qԃ?BI2+5@ӎ.Ӡl3LU`aSUgT)[$ #+J~p+SYWv5k_)Wnuk aX(vdcs I$%`uP k^v R)33 H岤ӖSݶ3Z@E[ޖq4O| \c 7uŕn 򩹶] gj\f =u!'v =wyk8j`ƀKSͥ`ۺ. LZx X1@0Qآ >5`=mJ&Z!6/`H )G)(/~ TɎ+0)\WB}#A!s%{O@1e7k2 . )3 o]⸼B*LYb#c^vĦ%ٿVhC"яu΢Q2=:U(?_:ɛ.W 'ZԌ.5vN͟:F Mv\ c-$BQkRl ՙoOulH*h݋hS4@pKg&Te3;"0s"EACla[(Ρ`7QƼ*g8 ~p$] NChoН#@~S)vgYxi( [L+BW\EEX.D&ɂ_)Fwҭxz(@\AVp:#z]hW;V{ٴut DB$D& @|gopw>L_ݼۄUzp/7 k追?7vy$@".d|"}_`V~dyw*\~(w UV} / bxֵpրVuR(x,ViF~}Gp6w:I{ecpP'|"F}0X7s:Xcp\%h}rE| }VyBPRx UWX"x*:s_8Fx > 5WDD OfK H@`>ez)Xhl,t:p\XXQ%uw<1@3V8:|אRwdBDYE4EeQsX; A{JEkI4k1JlԴ SKbs*Z fp.);@3gh˶Qok q:s vkhK|~˩S0u[wyI*Ad -S8H Aij9yx"ˬk)OD%r0q2 EHiț¼˻{!A|:$[$ viЮB/{+k I{:{v)sA`pA @B\L@?i1Ilz\9|$VM[PCQCEqeשI[rǕ^|:r̕t츂ôKb{IW||mVDv̙4|tHCkȁpdPǟ|ǡ@H pZ\^ǯ{%DžX;MP/UL ܩ.{#LQL}`j;|, u۬Jݜ ᬸʌ̬Χ,j\Ϝ|[ *͜ sZ`ڇ'M ͫNݲʜ09Z(ϕЖ\;ѓl,/Mѐ; ',| EPoϫ-]WVΠT*8]uMSU WOv\̼ ԣ\@jW@|A}*Y[mr} ̺Y <tHDؼf A!؋6DͤwOS[}x H 4]s˩}Ocȣ۩*g} ʀM:FL7m .vZ ΍l\ٽWA:|:ݟD=#8Qz?M2˵bgqmN^\ ?97=v}s. +@czsa*g⎜Uݫ$; SSԨŸ;M͸~&mQKM~;Βa}כ[XIZ-jHOnB(׹}qq>uF] X1`NY2>^q}؀Z|5<OPj{tqxo(uWLI嵾O 2HI)uѫ^),ݔ5Ѩnت.PwK n1=PԮ0>R#\͓ ޠK%#JN|2,xlNc^#;I:qN|ϕq;NOJ< KAMLa\ k06kM>eR-߂/OU1ބ.468sh*#wVO.oDoA]73O5O7?9>OљA,͙qUgp&LN[1*au-lOFftOcKuN,}og Văph:qV|O?K.C&c ۿ#gEZu5 p_Yn[InI]_ ;%Tƿo1tNOpK ՛=ggU!-$@W/?*w$_嗀{fĹ@ a1T.3)N n.EAS֎mw7K$r( Fb.ijlQK r@/2nPȏ3Ns0qr1u53c4v(tɀ׊2µKlHXpLMLN(WWY8Yl;yZJzZ:){<-\x.9N"l!Ib-Dm=Б=(A`ߴ8r|(Qras9LtѸJ)Mwgɵo7̽;`g;t~yuQNG -th/2՜g^4V[/7;?Zc!dc)Р%]iP񋐽n>  Ӌ0T(@i3 \+;T+eīIGV 2B,r#yDdo !ҋ) IF$&EJfJC)XlC9c:BwLL#F9+t$#8T.PJ ө8\l njad|Nbvby$ bXE|9|dhopAgkr'v*+y01|~m+u/"J ɠFlf\tw{>7E|Oaa$Z@>ʥ#%&)pgXp xӀIpj{;jtHh0F[7P$t S>Ό . M4:а0S"F# ["(aG;+PHUQT qph0#_H5 |# QY= X>V0B pnOG#" 8Q '0IJ1`6@D;B؀^2})+Ixf<,l-%J aԀ/y9ځdjPc,h3}LE<̰IJmnfIa |0iux4;%g"=im H{S2 C @=_ jN4"g.J:`G7~}皆>T-KaZQ_@v(Zvpt Qjz/]<yun# z;0lZC-!Bܯs[6n*gpm ,"| n[0 ?ڋ ؗvmL{i Dx4@d䡁^.| =+XvpR\"/@lL 7,E!}.=vzLG.b )]lnPvf8_yjsf$9{NŠ?8#t G & SD9t*a*W$uMT DdNWfXW7 /}si}([1׌5JOm=>q3luX*&q]K&S<$(뒘7u3,.2yoyZ&ɽo.[=n,V)h"I0n-j};7RJ< ˂r')ckpu 4?L@9S@{:}F]H(zxKo6بWcO.d]Lz^U!kn3=qn>/ه$@سnHNw }]wIR|EP|4w=<^LWS} fHdU+@Bm,l =bx WP߀SKwk|^P6 Ügw/{/ib/KЧ~J~+DwU=}li!pEExg~~hzWD[gBZup m"x_|P. x %d@` w>)h*g*&V/#7!Ӈ 37$a0$]6d "t( O%:tPsh|7-@5QQX8^(`H_<$A>$2}A<(S>pLS^'m|PFGr/Ն%e6}}fs34|Ev~0$:&Jaf.6%7p17(&i@@{$0mGِ[{,ԠLTZE (Na)0~Yŧæo\ r^5g{b{Y$1ii)L]V:1 GU*i`;@&A"V#E@X Tʨa"Z?ZX8*:0jCZrת>ɭ魇 |1㪘$pHe>'ٱGJ:rK(Z p>V*,px*z s+K(o &iDb( {K;t,')o EJƖӳ GB8X3;57K9A%PHu9FPC=Cdk&O$6(QZ? :ty wʶzp@௝`>{o~밀"˨mko !sW{zK-bpwAz(Z `TQYiӟg)a@kx#ۻ;ZTH+;AZЛ١sPSۋ  ~ۜ[{gj H7K1p:(jrzy;gl@P ) { [, `S֚|-³G v[>6yC +wUJu@LS P Ri[:WkĀA^l٦u͞0·E=75o'윏x ŒvZ6=rʜ{<7P ] fs=~m՟5}R PB_5#ڧ )}ҡ)٭-mןM7,Z[$5Ϙ¯۬a,7It̋Lٰsӽ1:>ڍ]7-Y̻mѭ ]I}8ح۝5 V w;N00\Q%yEKJ}rN; ^TnāEr  nNtC .NP1>qͨb ;+{+.䶙ʹ060d`(YN  K̩囱JJNiTH'AtE>vIqob؋X-Vul)@oL3re]`~փ^JʋdKN靬y-R +A=2 >^fGl[^0\/u&;]{)Wl K"*O?$`KHG^`*%-Z^:Dc[;-sP0|~Y-m?^f'$g~(༔re-!I/ @2pZ.Abe5_!+O /+0FL,1_}ƙI-*zPi_?dᮼ꜑OsJ 0 ;/O]MOV0(⫨,>5c) T$`d^fÑuGɵxkrXYNb]Z^HKJ &ʕ#䔚ZVKf TPҩj+B§.1@/o6+2s ϧ7T =~m;*m^=I ꮼ;2 ^ hqB0RIAdZُPc ˘2o|aY9');mh 9jȳdk ڸ*%NbdjlR[m̅1Y5`kD|EE>V!ޤb;S=E2ÕyRUDZKNi=+Um 0ɛު(۾"|~ T$%MZdߵQՀ '(M.{8{z_6|kG}*0!_0Ȁ}fH.bϐ! A "@3"!_}` (&w A.c2*W(ύ DxT?gن4?D؈lBB>)|%~-հ%BDė:-7)pfy!y$y&t q1 e'd߉HR$R} hABtyTrU"z4ιĦ(t ӧW:jvMq6G^J+&ʦx#[2ڔ$j{r+@j ;tfxsqnQڮ>R$w,IaMX7D̟DÏkCq8ˠs<7@g*ĴB}I4M;mQ ,1XwХac+#=0mv h0!uWXҕ{ JL78a2p?RD>Y~{k_6Fw @d( oF^?@fb]A3! 9Ѕ{ T vƆ5!^#N^40K`ݲH&E"bӑ  Hzj +;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroSnakeConcept01.gif0000644000076500000240000002756110534177570024421 0ustar paulystaffGIF89a  "$(+.2489;=??"""#""%!!'!!$$$''')##. ***---0""; 000333666888;;;>>>BEDGFJKLVYY[Z\]]bffiohpwyyzAAADDDGGGIIILLLOOORRRUUUXXXZZZ]]]```cccfffiiilllnnnqqqtttwwwzzz}}}   ??88eessJJVV  --36:RSU,<Ȥ%Y+"xDS2~cŏBLEITZ&MT2eBRXONZ(QW*]TN",4eI>)䠒%i!ϋ/f$SGP+b=I7UJ_j S͛9S'Cr4)UUA:eիRzlb/ƘъiHh{2rզ);ub_)rӢūw@Z[JVȓCJ2)v WO=]j赦V k7mwG4*rO5tNJ\OĂP4HP2 Ogjd@tއ'RX`Xr+)*%WeI6Hi%ӂN4Ń4E8!Yr8 F Rȥ".B%r2X#E7bHգ :CId-iSlaHRF%m+]mɥfHciQ\TdV6ʧJZꧡTw7"S]+֪}TEGI8EPYZdiCeJŦx)zꧪz+_Z++|7줕t:\j-:ŪJVT8.z$DF)<a@)kE죨%J k #i륈)œ'8o+nGX 4iqF\H"J&a* խ;+fsD8{<곎@ REǥ(ttQ2qUcm.Zۚ߄u)a=J-)D!թsXw`F~&JnzWn(]V n@!:eXOHfUިҙ93CzXٺ»C";͒lm(ꭳ$Vjʯ.^ϪU$d[ד +W 셪k,]f2ɇ>򖱪F >AkH6Vvj}#-sgx %JQPۚ#UMl;[Sֲ,U\'0ָC O 1*EtaVTmA[%v+}7;ނ P~Eq)MyL) IxҖFYbKtt/rtB kx[& ׃aЖ0* [C8=7>NE$/aHX^{L!Y ̷Fm䧭LTvKH6E\,=r)t2V0}'~PJI8V3*^|F!s ,Z-􈱈hR(sg{s$tHiMgӦ Pj4z-NIgZ;K W+2#:#jK[¶eՔ: ^![8 a,2t0BPSn P!r`߫tJa^xȽ}ߦ𷘣pY/xe !~{GxEҍqs'bɿ[N' 'p&Ow/gA$@!Iʁ׮0zLu 5myKA᫮Gp/Iި7Mh?q[ ̈́/Hwge; & %6g|LxJ'򓯼ݮywx' ;=Rz 5K|H@q"Qu쵙 ?~i UlKC_Gpk8 G'}/qgQ}{bqߧGG~@~~Wmavm1'} sҡ}2 ܧn9c~%"~ B X; G-w[ȁG!H'"3*cL (3@?ylfm*gy[0yoᄡ !NZ;Z}&T MPzRT4q;хGgbH;dhj8lm{KuwezJU^(;Zx\8kf!wNㆎR=sSұL+P6n>?o6&;ZȈ ( A5Z8 ;꣮u%D8re:O.:Syw4[="c:"7gE٤+ڈNZ@[WQX[Б%e*R,q^٨ %B>a눩ȩ੠2B DYz RZJ*ʣʧٹm WW-`sh`@buVcȫH٩zڳ=*]1k -ں@љ3{{< EFl#< W Kfb';bA= k:һ96tۥ{< \ G,;%I OkKs*` 0“]0H[HR = KY{훼򾟳Q90p ilĖ93ۧ.L!0L, Sv*m5rL+a#s,Īb B\-0p Pk"] pPaj Qǐ*4WYBa]Ź%x&qδ$팞Λc;asCҲop om:uниС:Lek-lu` @# ǐ.v[q5ݏNQA m0(L+?|q%"r P <{x,0`3"Y2a0Bh (lp d rxp At9R2$^}ص"L?|ְ֟!ِ* 9$sD ؈-خiJ߼ܵ2ΩG+ZtP p\sRK 1=@-ݹ,߯@֍ݟݛNJt0q]j{C я`\]+߆3o .L]` @0  Í(L"t9:˽˪zu,W)䣠et%5{@Z~!_~/-d^f]B%G.IKNNxo|0 @(tac>7eG&E=kȱd 6ΰ#-LN~ x`jP1|8ͽ^-:?-6A'~ɎJǎn0 @h ]qtd nHm~0lF; >nʮҢ7/=G~ B>YA (L}M.-4V$L> ٞ`$ ïp˸"'W2O\69;J='3T}Pڸ>!NWS}irO5o@ʂO>TA.8SAp/[!K0CEO|~)?/- %!<~oV[TC%`0[(yoN?aYB?3pXA;p/-';mSM.WsPQR! .T!9E$4(G !H!)TbٲTZtUM3uJg+VAW JM.e4ӥ<_TY$A* DȐ)1FAI S&Myml%,E.ZSҧ'QLRd jR3?U- :nDZe_6ߣ6$;No*vQTr93!:+|"t9~C[{zRyګlōW{x%SL9렺Kk <\KɼbKok&XlO*>YBd'UTKp&bbƱ &@ӾK2,%,*Q/ ʰ ;  Q_)U#\,FipԑG?H"Q92'obr='5M),K&fn+69ۡJc/ # @€Q>tGA٤xYX* X|R$QL\x DdlCE"*eT2Df]kRA,(#~&&X"B' ֪h+$bu# H.3l̂ʮř$AL&@+Ub!}!0d HB'%CVx'$(0JR&͔B% U7zB}ٓ)3FB̑J.{\~ܤ;Ic")KJVR1,$5eM)ELLv6oF -$"Y[ ,BfBU!oCX606bJhj"Ta( ITA5EwZd4+jf&M1%-A-,)5i"ꢉ hUҳ գz@)TLlqת {a#)hyr)JQTT!K4KOnu\[WD-ZBl `xj1:wx,f~BߋkS3vEhʓ$$XmZ *vFmIm[=u1+TL}LƄ(}*JtV-Z'QTUA6qi^ (`qLYƛ3JoKE-|#B_z~ԟaw$1ЛP{Iߎ((_}1Zږ" vG^6s(ce2:bצ KdXel/GR5O2A,*D\5/as,[3sQFܡ#)Θ%! e'H TeY].ޗ-|9elͣ4Hi&VXqAB[[!zL 9}ba4nY(٭g]Lj3^-`1F6"ft.jW' 촵mغ>wM^SAwW-jlg[.+񔑦O}(;9j-+`mJ-\Μke ȇ+{VLoqP]FOW7})9O.ȁv!b9J\;+ymOFyYjDY=39 Q.%-yebu {Ǝ'Lh'׎kCNz3EB];s;d28aw6^/3L/!V*u*:|G^%O bz>ت]?ؓD,=lW _{ΓjCqTh!blϰҒS@ BӶk?8.#*뫂ԈDḎeaȈ l!L@4C?,?2+#@ 0@@P@Qx<#2k,) [!"죈$#iX b;S'Ys|<0CW@Qa|9:ą;T<C|DBB\C D E))HCYhC,DMT :,;d <QER4eAPPE{yAW 0Y$ !L$`8@ U @9ҩy#4r8U0| >ܸ U $qrst4uvTwԈxSs6GG;#~<3l mHnDo2pLH)XHhHZxHu vwȋ,A/Ȑ}F"IƙTTP0p sA,Ѫ8%0y 3bX P?\ a6 tJJ(Jܪ)JKK ˲TlK{˸7˘KK ˪K$̮, KQ{)KB{L(EʢURh,%-퀱 S-C=ӈ|)R6O7%#&ND7(Q9xqT NHRQ2YSX*NTXSQ RuSU TU=TVmUyUYMZ[̬ By4ՑZVsM2bi9PuM5ŀt,tTkmunUoՈpȏW(WsUUt]YYv]wmFy͂z{|/ܢ~-%P@rݫEXeMWUwUxXy)H ˻FsS3&3b]Z 6]^Z_ XI JGL 0۪͚Ѣ%umZUڧՅ텪J-ڭ%5[=[QH[LYη۸ۺۼ[ۿՂmuWP #rB3΂[؝۽]aS4WPTWm]m Ӎ u-Mu9zٕ]ڭ`ݚeZERHGj {AHQX^R ڥ݅ͅޥ }u`*-R]qn?@6A>BnCNEv689fZ:d ]a=f>6dd=c #U'hSP Uca[6Vd3 J M]Ci+'eu!.f8f3š,ny]YZbfjLŭYf p.qF&r0W@DUbfvvgWgyzb{|K:ВLka}a$Hؔ5UF7,`f !\cZȮ V6UV .iU=Vbe iLȠ霰i2㝎ViXiֳ䔶NT i`QpPD 1ƀhIY jtasR1RjV gpkk&kkơ*^ek1 lFkދlexY`tl2I2j~Φb zDfg#mPMmZXmD=뇮ٞ)"cgmmaӾ6Y(VfznEnL7&ޭ#W`(6m rp^id>nʎn<$O%guE3i b\t~ey.pp=YpQjpypp]R&4 Ǒ>qg㙖&VuppX RrM^9*dQ>T2 qY0Ҭ#q(PvÂN(/)W*'*!,Oo-E./ s54_<()9?;w% t<%+*9VolW XNXZg [ \'Uu/t}`a7(bOO>vKxuguZjݡvBG`a/bVҮc%)鸈+9\b\ LCВ+ gb)H)hx݁ x!xҼU{ 93fWa]Atbxpxyy W7w[yy}3<?$7j\%rzO!*J(ȥ1^_'wFz3 =(owRW {6{W4-/O1BO|şo߂Gforz Us(ΒQ1!AKDŽ2}TD &}-|;WB~x_ @~{OV~O"*` ʒăHQ8*FKq1HFj)e *?HĘc҄X&V:us'Р<{jНryS„ .*R/=bucǁ*KjbOȕť JZ$BzBRF"yՒ4I"6+xem%_&)6KhcCR$s*yF'xxQ));itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroSnakeConcept02.gif0000644000076500000240000002766510534177570024427 0ustar paulystaffGIF89a # %$(-122579::9?=?"""#""%""'!!$$$''')!!+!!,!!- -##. ***+**---3""2((000333666766>00888;;;>>>@CEEHJHJMQYZ]^K L**A==J11V--a`kjmmpstuwvwyzAAADDDGGGIIILLLOOORRRUUU\QQ^QQXXXZZZ]]]```cccfffiiilllnnnummqqqtttwww~qqzzz}}}        XXhh``JJHHAA   0036:RSU,ϐ`.jYha6"2جDf2hcŏB|FIРE[ 0`i~:CsRBLO(2dW*]TN",4eI΂)`.jih!ϋ/fTXGc+b=IYJ_W͛9S)@2CUs4)UA:eիRzlb/Ƙi[ę{2rզ) R3rk&@"[[JiȓCJ2n3v WO=l赦V kMm-G4-rO5tNL\d6Ƃe4HP% Ogjd@tއ'RnXrQ+)3%WeI6Hi%ӂc4Xƃ4E8!Yr8 F ESȥ".NC%r2X#E7:cHգ :CId-iSlaHRF%m+]mɥfHc2i\idV6u̧JZTw7"S]b5֪}TEGIEPYZdiCeƦx\3zꧪz+_Z+"+|7줕t:\j-:ŪJVT8.z$DFn3̐a@)+kEμ%J k #iIՔ륈)18o+nGX 4iqF\H"J&a* ӈ;+fsD8<곎@ =g-G(ttQG3qUcm.Zۚ߄N2aL-`3ʘ!թsX wpY~&JnzWn(]O ~ *:eXOHUިҙ9z3KCzXٺ7ûC";͒lm(ꭳ$lتʯ.^ϪU$d[ד +W k,]f2ɇ>򖱪&>AkH6Vvj}#-sgx %JQPۚ#UMl;[Sֲ,U'0ָC O 1*5Fta+VTmA[%v+}7;ނ P~uq)MyL) IxҖFYbKtt/rtB kx[& ׃aЖp0* [C8=7>NE$/aHX^{L!Y ̷XF6m䧭LTvKH6E\,=rT3t2V0}'~PJI8l3*^|F!s h8Z-􈱈he(sg{s$tHiMÁ Pj4z-NIgZF;K m+2#:#jK[¶eՔ^i[8 a,2t0TSn !r`߫tJa^xȽ}𷘣pY/8~e !~{GxEҍqsobɿ[N' 'p7&Ow/A-@!I ׮0zLu 5myK᫮Gp/ި7Mh?Ƌq;̈́/Hwge; &7 %6g|xJ'򓯼ݮywxW z;Rz 5K|HԠq7#Qu쵙 ?~i UlKC_Gpk8 G'}/qgQΰ}{bqߧGG~@ǐ~~Wmavm1'}p sҡ}2t ܧn9c~%"~ B X; G-wrȁG!H'"3*cL x3@?ylfm*gyr0yo !NZ;Z}&״ MPzR(T4q;хGwbH;dhj8lm{KuwezJU^(;Zx\8kf*wNㆎR=sSұL+P6n>?o6&;ZȈ ҈ A5Z8Z ;꣮u%D8re:O.:Syw4[="c:"7gE٤+ڈNZ@[WQX[Б%e*R,q^٨ %B>a눩ȩ੠2B DYz gZ*J*ʣʧٹm WW-`sh`@buVcȫ٩:!ڳ=*]A{ -c 09~` D oc;[˳PdĖZ9“pUp῵ko!~#d9 p@~ @ p/6zۥ|jK  "`*$ ˬWYa*ЂLȹ5 С @Jr 96 }lнU TM+,Ha, m,]f  l9};?]V-ԩ<E] GݽD/C+äh wj\Q" <\a*ς; 3m0m,׵2]hװ׾!}p  m}뷋#*e+LRj Հo0 ڔ @ ̫M'M ۏ1`m]+lݵMǦɀ*%Ldzu d  6\ѧ}971#iJ"0G+9M~@J --w``P='4Ԯݶm n)I-=T ]0 azC~L""ϸC(p*.!oW3S(D%2 BP PHxd]\""G^Ҩ'n++Q^VTV!Xo `(dPH N&E=pւF ~NL P ' ; @0~qf p5.ڀrk*OҒIcUA(t 0 @ I ؀ ZL W"'W?o,,NFHJJ(C@ p Lp*bo2g<ȔїFb\AO&,30yC3w ;#p![ N0Etjs!O8**%Dp[.@G23P??Fo ߽?ӦQ#X5 "0jϞ-Xbf4G Dr ) TD% &I $ )IqY3A(p AISiQM*(R ZkxA>ubL  Aڬ),Ulۙlq̧PD]UZ)iNJ0[vqbŋ7vTrdI):k2gzsZPEة5UuZ02׮Cx2ja]oQ:(ad[#ߚqWNmcj Ay2` 砃I::F;PK@<1o:\/ {o6䫦>KHk'Q@Ptº` oz:C> q)!?Xʮ 4,-taL0 4 P/.898؜*̩o)!Zk&K쒹/5 S1](4QZS6< DNd&=|R)@( DlHK.2I+T 4PS96T N LND-UӠ%fPifCiU+EmH=2S PwRd,ِWl(H$В7|z(m wQU+vJl6?om5ܮ\3]7v݅:y٠47~E O#`jB*w-ct5R vareyl _65g8anOVK^񓠠oŨ 'bptB z7r[3g%[sS-:āZ~ myǦz3܌,b]b^%3U)N{]Q/L(L8pS6HD-c?}Zc;|gR2‡@Ew:_Ǔ}ZG,UAD?P!`̢ћ$Z)TIp|m#h=Q U?O]<yR'[Q$D\:L[>t$oaA7! ^pJA*@arZ'+ D00 C n#[#JquNF>]L+⍶ {T|o5t?_^џr 퓐e 0,Ui8-ZL#73!`p 3 +RȽi@@@@8Ĉ?5PA`ApAOAtAA7-! DD#D$4ĉtB0 ̛ʀ$I9 #7?hBnH x B-LJ"c8ĝ.Bj@nBI J4KCMTNDyEoE RSTU V|E EBZ;E6D0DD7aDOܒPQDedf|FcFҙmD Wƣ{P K9!8ЖZ 9*k ؓkY|7 H&K?fZHuiHxHHHnHHHB 1 \+~ T1lI8xISH`HD4I0ltdI (J" :h!Xt,)xxz R/,=1 XLi21)20 DDH;5˼K3KK,J)LlDŽLL4̤KKؐM M<$$xMldG $A$82xOxٹQ I41AGDB 4 40O4OlO O99EkM\je}  }O*D<}O(Q8/mAhI4 JFAT 2mRgID $6) C* +1x /R/12EmYӔiSPy89U:Gӹ=>eK@0A%T2C812E3EG 6J7%1SSiT1SHC 0@TA}G`1 I4"Zԟy7[0Q; !cdHm]V.mVxVVVVXMSVV֚WK3[ K*Nf WHyT4UԐʩ qM 5̶XRX`XpX#TѢX{3ݒXXیTY8h 3a ΄ XX!=֎UZ5s#Ui6$S{Y" [[t% ҇Xs#KsĤ?jPa1[[ܹn[pq[r\ Z0\B\E[%EɥԷ }\\\] M%\IU8`i8вEG4O/}MP;K?^o^U7BNZ=]TB7)!E蝮^$^M%E_Ddjsmc^M_^E3[IU8``[ ްuѩR`l i*;m"^s>'ȸ^,% a53>ama l(b۰A(0ibYHaXb+.,U-& bc :bLA&c`iɵ]i2BBdbgzgx6ylAggmކ~nhTFf^hXmgV≮>SFh^Ez 5-i N{鏨i?cUG꠺hjbFlVnbі9ҶW&#NȞ2Xb4^ AEg{i.cn ,ОO{Co{3ֆinI"^d{ oBnu|$n誕oަovonoep'Q$0(cp֙Z޼[ }khf&})}pDW]f4U񟁚^Mh6d j qݓ Ԥw%s:'+z{8⪮)8ׁ-s`-|,|8|7Bǧ|L|˷?WP{?y17|D+__Ǜf|8}9|_#Ж );jŽrJc!CȚZ}c"F0)o}?wiF+H_.0DjQfEl.VHњ+92䲔#Ks2821f&˜%C*Ϡ%-:PJ2e4$aÇ'VĈQ#Gy4 J,])3͛8u)PE:U)iO~zMF>QNڒ6]6[8i)Vl3esĎGky Eo` Z@]ܑ3˘IjgG4Zuf]Q۷;yZs "E/G}nÖM>>CBCEEGFMLVY[Z_@##A&&F++N @33J11H;;L99Q U'']%%P11cfdfkoqwyzzzg++==AAADDDGGGIIILLLOOORRRUQQUUU^QQXXXZZZ]]]bOOkJJbZZmTTrFFyCC```cccfffiiilllnnnqqqtttwwwzzz}}} ;;2211  ++88!!IIttxxiiggooVVOOIIKKVVjj     &&22..  36:RU,Մإ(^Y7"xDs2vcŏBLGIԭ[ 6ll]SCRXOڦ(8pW*]TN",4eI肆)堖(^i!ϋ/f$[Go+b=I7]J_byZ͛9S0Cms4)UA:eիRzlb/ƘэiXș{2rզ)ic2_5rӣǯw@[[JvȓCJ29v WO=]n赦V kֶN5wGe,nV^ʩGϺ.;ٻC;̒ |<ӗ[=krt."Xܖ@Di L: tPa.yPp ^?eCFUMM:A৸7ɜE b 68}K  upC*, ½@pu(q)V;8B`ZSA)ٲ"bX,(pZ;%')H$I6;KjTc#iJDp&W8Q `Yef$n# 2* "y2:r#'IIB ';Pfa;*9NTq yCF$)XdGQ|c?sv$&(E2RĔTY.².aIj Y7 0Ib9~S, fy3x|=H}4@9'4z}#BEС0t'!2Ng+5Z&jţaDmG,(*B*t /=@L=9ǦE,fLB氅sTiVI"TteZ` [MCWO܅ScY7jX\ʼnT5!u%]WP~-@5[ƪ9#X+A7nu O+c_17(E9I>ڎV5ellM5[65pRnӷn{ID84u`;RU׃ێޭlx &{Cb0)<$ O_$+Z@ bnnu*NȂ kxd(Y ˃-|Fk3UZnt(pլb鍸!=&I$q`AF*, *æⰐV"iHdAϳzFf b_JDEdbI$'|ˈΡFsuaϡU"pPSy>3hCg?:qtj3_1Xt7G4I0-\Mvw`tZljv z-N#mkMZ^å 0m bEfV|Umi ZT+'w p!E&_[@ {"`#6[N$a]tn7-{ _D}@krU07.pk/lÏ$q:9.?[5 ]oUMrjQ1(ʺDIn_Xx@Z5"ca-o7UsF$mAvԢ ~t9=p;/w'JÇ$oryUaqOX8}%Tx7셈'O6"b(( %pT9- ?O}[/Ɩ~'jRߐ}3ܗ3W"~*)'~i~n1oQ)qGjsַ(}a{f}4 X  ~r8xGI t,Q2#[1j<Dy'oQ;feĀ{DK qmz'hbM堄c؄sHTV;XZ{~"'KXqYjj'HprcuxJR[8;WxY(i](_Hyac㈎R>UҡL.ȄM}HJCUxAybHh9 /_|hV NG1,hy;Ę3~:ʨx! ""8at4aq8Vs+g*̔,VFC~" d+ FY0!!iIi o yJib :I7"q少+E;Y$ 91 Ip Y* )H@4Nn>xK@@ w4AtH"|%~bfM$I֙U U[h*yEL = {`:; ` 2PxtڡBƙ% 2xAw yz>a O:|uV*$zzEy(I _@!|P z Zmuc:Mk!!z*h^$zmrp 30VjI:1N({UcBJa){+[-i/ 1;!P р:;ky<S-c#=UM{O'6`PaP Pе y)@B0u^jF9B7-vK:U?[!wԃ Hv{qK {;*[ {R ˳@C"W|xۻѲ{0+ "0 x˱k˪y*3+<*y!۞`LՀW[TS 1;z=ӿ)+WZr*lbrG ;%:Y@} b0bA DP 3kQLjT,b"g$̙LLhP dU¶ [|A]!4-? ӴBӗ$]m_}a@n`ʌ,{tP K]rptv})ENQR5(wSw;(؎[0 ,c1ۤRםMڦդ!Š Hʮ]"ۏ=0 ps9>۾ 3ĽT,R+ m- MݜP H#޶D@x3|TpZ<= LMݡ ՠ (ΝMɄ#J<8 .$ᾫ@b^P >\Ҫ>t^V P Pꖍd+-^>ɭ.b}̤P 0i^*!s^Rm^؎n~W.D\nG:O>ףC(;9i bq~հ s/>.r2{%YNA&L=^4V ՠ h 2 *\soACJEo$*I1KOP P_[ғ+\f&,+C43N 9ϰ a %-C~4O+`Fe)O_ 8 PE%aW-O.f0lPCσOݑ dPÑp3X[~YVm,dQo#pXQ:q" +,djXVrmtڍtWI#UcNe;v1͌NIt3ϝ,߅Xb76I,]SO6L(G4P!Å _dыvd5MqMl'ԥʚ3Մ.gO?eI4Ѥli"jԩa^ͪڥ]CD{ßjFQ#[ E+]xBkΦ]Нuq _S|"CXe~ sGV,P~ Cjv`|ڢկ:kKfY~w:g6[8b*瞂Nڙj%B,cf+rœbl$xLQ(8siGyL(uAO B09 rX Th**JQStˮas8q:幓<,ϥ"P-Pmѧ4! IhQXZ9 B`NKSkB6QB=UKzU9Οԓ->qՕW_0 DS60a t OU1ۍv0Sm)ՖYFqxb,ViTb15J59KA7ZurȡZS>G/8[$v8Ή+sXh>3dBI6OS^x9f6fysvcz6#Khũդgզ7~;աzJVR^ckzÛ/0qfLwLnɵ$Xoy`6}h1 |\头]׋e2 𽩣^r8K͑o;tS_}֋{g}# +⍗^^h7X@vlh*w+Ηn}9^F0A'G q´$!PS\H a1F)ftC0PF?3L&Vm0%X:N% ¤)-|! Ѱ ɹrXEh"s'I'~P-bq%1ڰV9 Ҩ5obh4$2 8:>&qaœAT%xə<(Uc$ +/eGECxNJ$(AJ*\ [J:LA-ri,Y/ˏ%üd13;Ȓ#=e | :ՌbUdA@@ -`+y`$ttu\ı*KΆ7 O?rBi^ ;^lh%cőlAZbZI{(&7S_)* ջ BCrVCa26@ÂPæ`æpNs8 äxBÞ;*#@|ATVBCDdX3|456D7DɓCORj* X#]0YE8ACډ`XJ^Y3C:Bra6&JFS 3whzFBFBFF{ƖF|ƧoTplqrsL`TuhGQ|G'yBzo/8h%~Gt$H 0oLHq I,ƺYQÃH2+3(l22*.!n譫ju1c4Ћt쳢,Ի,HXJhʧ䎩,,.O순Kdǰ| ɲ?'JtJZ#,0ʒAʂPbʷ˼ K`H,-r(%*R3zr E<%lCAP`ԙ8NmTP݂Q-U:F+!UEVWsH;;UN _]`-MN-OmdUV# 0Uh}TiWXֆַUn W+US@2!SPU+"gQb@Zz+M2#?tQY؆כy%Ղ{؍͝uXYY!Y1hˠ͢PdcuYY*:!xXs՛at! Ϳ+4-Qv.Y]Zօ8(u8[K[ Ф5rx1ۺEؼyZuڰ*4[̿p4A+ YMEY2]#+]۽H؄P$Uݍbps]]]{]}~^$֧-ކC޷R{ Awh]yݨ[ȹ^^_%_4_e P_9`v@-KnE+3YM`|aF9*2l\Yl Txl(ƃ  =`b.;H]alN`hqaaMa!b.|0BaZS}5 u᷂ዌ0ُ ('-Յ="_1ac@2t[b]-p'xc*;V<Υ= c.ABCD;.~IRG$HI JF3K <^=FddQRB~U~}+dI>]v y \d-;ß]d>ecEYC5]Z ;`*f coesI.XYwV(x1) k-l.m'n}ނ~N >gHuuS_}祈g=T' D=.U Tuy#RKiW~eFczX\i*fמ%dj!ꐮ>vꦞ鋐逡 x~ׯڰk`UUIkgn}kĊɶ^Fqض \g;@ݳ^Xi*JlZlkllX~:.f-.FԞ~fOmx8iϾ.mq(njP^; ^ æ%fQh]R~ @!oNi 8oHYPn{oOonv -Ii]0)!'kg&Ge{m0 H ׉? pzp{f^#q| lBqŦ0ppOqkq!/_#'$g 2_4AUؾ[ƾ%8"덽+8BI]vw0MFPsXs{msgٓr4Xw|! )yQ0 )fhK-<]{e#!|1@|P`M4湗ắm̯}-5 }n0}s@}w(8M\ 96Q"kxvyj|k%-:ZN~[~k}<@ 8W%ٛV~j9v+ kˆQh]6r#H0jܨd!Vbh̘pfƫiӦ9o9%GRJiiL8)4P!Ç%rգȍ ǂH㺴bi*r͝5#:TJ`F 8j[,r z0JnkŐaɒǴrK 7\v)&߆~B|o.q%V3E"9 :hҦO˭Bw5=_ oEo}xf޿l2DǑ+v_9#vxSDHx%`To)QՂt&8CVX\f%%g#wsQ< xS&eTn 6X8.&! Yh^dtiȡVxֈu~y%].OCs%x VHaxQ tR]zi9׹C!P$9gH'Z4֓EהUnt%:Yj ]fT3MEl"ɉ8r'AɚQ XHԠr`$ouݤo·$7 U$e{"G(+h`Aݸ1-Ւvʞ霤FyrZe5IJڱ5%l:KMFl\Mrk*;;;iQf번 ϱz{[ Eoml0 waR@;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroSnakeConcept04.gif0000644000076500000240000003230410534177570024413 0ustar paulystaffGIF89a&"*)12::###,!!+++1##4));!!8++222955;;;BCJJTS[C%%C**B11C<>bkkpssy}l,,c55`>>h00j==w&&w55DDDKEELLLRCCSJJYFF[MMSSS^TTZZZbCCfMMkIIcVVuEErKKxIIvWWuZZ{SS}ZZccckddkkkviittt{ss|||  ##**%%))55==2288""//##5522==   ..""99$$++""**==11DDNNGGLLYY]]FF__[[jjkkzzddiiaalltt{{qq}}CCKKCCJJ\\]]DDNNGGNNRRRR\\ddjjccjjsseess    ##****88##,, 33   ((((KKDD[[ZZCCffvvccjjqqFF6TɋĒ,:&(́CGQnq-Z Ǒn޸yHr]u&QSr];hd&O:q O8IH/]էUz֯DA uID+f1G"If庲ߩZg !=UN<ШLxeիWZpOFq.Fu#Ficʔ\vs۶xmӆ{)m8uSrfzS8'B.mRp~I`yϪ|>~$p851Y]\eK'ՃؕtiQV=Qkb8 },Zՠт/\<7hc0V^]tҊ]}e8aXކ&uE7bC88Aa,ȠTSDً9Xy UYqtfŘ@ćڈ%򄢊rri/J|46c72݁QvD'FCuJyMJRkꕑS0j Z#>=ԗHٖy7idNqӧz*N6`Tà<:)VAGO%nz)8E^XS*κfW nj㹉 IdacBSRL!m]jRHޠjvFui>#:]yD~;xvSijv"qsmͮv-s ;Uш.9Vl1p(՟)'bGZ%m^=8֮굶ׂu+-)K'}}Z[ΞDA:q_5W:OGU5DR|D$#|2Ԋ<Eh*SOT˸%ZK7k{0]n (LkL2PP9Q7%a S)WR&r ](P ҆beH(F 3H#K` !O`ɠF4q,*W+4@ݬ5GY?5t*\ec!/FH$̙Xr0џlrnB-@GT~̨GU;=Q_1LtO0 a$Chϭ«Mes L5(FCEQԑL뚠S{s1rlOm (@"c}m"K. 6qǵ<{.h,j3ŐeLgH"[a f׺:| > _pAV* ?{C~`h;vQa%duT"6BTGkT&y P** `ʹ!#@ח" (y¥y/j)Y:\Gچ {838D/5\`f̮P]*0$H\=(HoLyhz '6nUӕvja g`Po؀:8V}D+:pt6G´v$$qæm x6+ KC,w7$Q5p1dXbrpg䅧 ,||$ڀψ\7%Ѓ_3hk%ڜ|R b { YM;ѲjQc( b14!4έdO-DƙNkJIcw>ڭd՜F)p~ \iٝq˙-~Ԩ0~$KOsEF"'96a~ %m dbp9 عgK@NTR?CXS YB?~IkCQAY^ӕc+FlD8{;e0` >apG%=zG; MlP%wuB͒V[fe$`H:pG;(r_xYbg^-L,HP;R B~ 8;Ԛ% :X>ߍd\ BS@~_XWw52g5D2BEOg}y} 5~e$7{c@pP~GF p)PʰAܠ" oK/|CwշK!dzMQ}ŁpTBa?04pc؁ pdP|  qe'xX!,#z^IjP|w-I&da@@<_L(F2p'  "aZ,M7 (&5zhdgHJqfQАA7~xpXm`5p0'x Lj4EGzSdXf(ww(Rע**4LWY~(k7Ą$ C );x-46.8s,8z&Ajh}{GlMNX2#Cݧ5x Jd{0]p*43 9K z`h(\Az:&hx j@x w .7 YY1gQ7^SȆbTp ]#.@?ac 9[Nʵ0>%tc(AB9Yv&umM" I7;*B`TÍq9R/Roa%$%DIzFN { Wijctj Tg8VhCBLU#'9Gs‹#ҘxW#Cw /p^[vt\ˇ#ٛ28ҝvz|/`Zʩ=y)cщkGfz"+@?4c} ٘;s,f[uI78`iO '׆  pzyI&J8?a"pɐa)3:kAN=  z0 w- j*Ң_^'287:?NL*$U(pd׆B O*YBR%Xf9(O6TNWPMyǀ 0ǘ R;l{fS+[ꙣȑ")sڴ wX. zЍdɢ}RW+&E!Z 8GQv K$p 19j,w:1"Ǜsd%6'uڢ!A`oHwN _؍i,S*(Om3Y31 b{ڞuBAGF PC*2 P920"K]y17rfQXbMpT*c jY,A_/Z#v MJ{IS 3*q@S,_krx0j%c&eYɪYbdk8`;3 ndbP\lSJLUSJI!i3e g{*/  !}CIB v/MQJWQ0 ɔ*m kYXAdxmD˵hT*x8F }ȫUV&[[RSa5{&s_-p `[Sv.|;#&Gs'<y r*nc; )AqZF#o5=']EI putѹ,+Z:Z.Ī!2\4|B`)Ʉi*f'h_c1%ÈRa>Ad#̔b\}(G@,_tgr`30aL^zyoR*b|a|\ƖrƫT WL)Ʊl10,PwYÜ`ʀ#"@$,&<\C3nɽ Ĩ\4:pفp5#p r ڻFn STlYAz%=~~F΋P+(UvQ%U8=\=0]b5 Yj? J8{{ {=Au_%50"B`\ݱNRP >B\nû?dețX])=Kl\b #]9 UV#0W ҈08b)Ai!I( )P*pfF@".""r|56~ӿɭqiONq&p䝜גp N!"0%ꇭ);[~lS)ah-OYk(pw~%ϰT4\YmCS >ˮ#s4Ci{^0uV .C=Ecm]P3$N>~-DyP[˿iRˍnBTcŨQЗݮ*~EcT^)`SF,PRM2BɉxYx‹2PG Rѱ- E2*a>SX&91UT62O"90 0t逇>c`.@EB.T uQ!ػL0є;򎕫OM^&ATM!sˆ/|Q%>ch$4ze~j GZ.-v Fl1*le;لDne.!^BhA[Bϊ hrM&JL5d:VjS,*;G,#O#Su͛p=i6L8F 3jDTKi4oO*?G9pxg׬~Md+6QGG̚y͌ےd̵'{ ]&߰&;k|&SMvz)xȂ_1=QF yp賥bZ#@/&6e "k7afTy-G$@hR:4A&'.` teg4[ɩ⨨mU_^D@6MG쇁srterqJ{:㦢Ngl=Ԏ!A@0ԠK5jH~c(~c{o6lS#Af+G3!?B cq ! !@h )u(*7֠IH$ϝ V"Xe`h  &Hd9q`bSieJ2ĩT+I[XfHu28úQ,"q0U+qm{9i"@؝Жrm۸[еd80ۨŀ}tR 1 肪 ? 0FVB%p8-+׀ǿe-C:*e]i<}y` ݰ\ Ҭ@_5x"@Zυ PdJ)x UE ! -_ uAJ;Qlxø!X* '(=UDI $bl=h>+ʄ,Fx jߊ l3Bych~K<v'D:04I678cz8M'^Ј}jWtaaD-H5= +8 F% 2ҩDޣI ճLԣ[bm^ zpd "'aOP9>h!W-4kFpyk%ڬ_{ȆtaQD%P)"X>U3h`ۉ@+Per&y#xdHe`wP,sR"^) <aZDjis obHgW(d`|0ñ@uNf1XP'E#&)L酦x IZ?!kx!eNt Ѭ`=? ͠lnCID|֖k\eЃip 7Top Oro #dYrDRN-%7& :FK'-mnn9p~]ԠP)?Xi dVfb@颈h4 1Ёź.r$'N6禚(kvdN;hL XT‰Ų|HI@JhCM7bs GvT>`r8gn;x  Xl] y.ݣ؝Ed3 Fܠ+ʳj7D 0z7q*$VL'/wg4o(Jsvz8јϬyY4Dž*8ླྀ/dN\1Ջė?.)B]Tɞ^zM~=!nąpkO3SW>p͛=C T-a,|k NzSDɼqZD1ǐ]=ȚR%9C?_ ~SbXDhɓ;w(# $ :&ӎ72'_O4DRv" e_ sta(FpB4ӐC'Vo{@+PeYp| _\>ʓ:pZT?L!MAFӸ?INPeePGD(gi1pZ+A  ۊ rRL)=4+%'W@P&WAZ"p\F*3. " sS;o:MAۑF1W+V i3bq37jTw}aB 9Hh Ζ+f;Bmк1Ўʵze} msͅ0¡ڞ]w1|L7b #ST2\Ș{,L-[[W5:m*7t{iNӦ{Ck*ރ!#(l&d>݄|3觾.'ӂhqHe n&`:F1Ƒ`&.aaV6kGK"6̻#@);Mr[&8Qpr]tqfҗDLz%'8;rāeK鵼WPa1U9VĜA/gh qІ~4)Mg@ԥVei*|`Ht/ JlEh6B;-7@Wa-Wvv3%MTOV7YAEt]~M4.atC&P=d:!yl-~׾Fn* ڄc-tĸ 0l ҇*le[;e[ ;"n{aU]FMx ns>[?6Tn]T`0 Pn{KtXu9;7}&}I:+V{Ȣd:I>3mV''1wΚ_dT)ABH.֑%3N~3%=: F9, ;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroSnakeMerge01.gif0000644000076500000240000001450010534177570024052 0ustar paulystaffGIF89a11AAAuu(((ZZZQQQ-vvv333###WW:::IIIfffRRŶ||ppNN6zzeeD$$I!,@PA,jddhPzDYa hCA+>\_^B$p!kIzWQSQOc~EV(( KEvxcIǜ~(DvxDp W(.KӭKڵ޾ Lш| Lhp BD dB@nyGd,zb! \yN⪊uh!TVRʣTz⠩XPLjrh :*Q,BiXSh2xeرO e.6} Wչ\ŊW/"`Ŷ!;lᴇm$ZQ):kv'zݹh Q,ua>'-^jУ ̽Vp_wOաR>ӫ~u߮-;?&B޼5_'Qaq1^AI~\@ـ~S $o `N"Ʀ!_v( u ~ RU lȌHy$O4р>B(LŐI&$sMcMPYeA~a!y f3[}=SfP%W ^8-z"tJW*.t~Hl"L^"(t.Avi~蠈Xinj[|:B[^ i'X#NdM' nЂa0.j G Ge{mꘜW@՟ӊcmj{Hy.|0%f^в\q@PLYj/raG<$\ '@+ZVḵ.r#CrSZԉ@Ž3oY]<Zq?}]&g\8]aD':-jƝ]sdsWN~m6vwug|7F9=VY6wa}x-d?zvAy?KxTx%(K00ұ(˻Ezc;DDNc;| 5Sp| (Ma~]8~O0YsRRF9t",((/ osPYcsp?q! T! =q!(]'}!<z)@a$h/Z eHX9 { yO%NQ 3B1 [ > Z` / c42ghGYw K0QL Xr6 "H q6!$#9I.hDN&\b !O!pQ%,U $-8ŷRX%0 ̻h 2fH]B HZs~.f~ɇ`pG(fIr @Tʞ>d&1  Tk$VV)Ȍ4+0t$Z4}v3>󣴳Hڄr"J'RlVS0Dr:2E)Pi_>aA~''G6S݄z~@z8 ӷaS~w45Gz] HRLc1G.Ձ!5#xC%tE3E~/ց!@5xA7hs9)HzW3&s4 dIhN2sPxvRS(WUWcYyNX6cb=wdE XpΧnf򆠥X0~cvJ׆YV[8sT7FvhjHdvĈ#lrocvvu{xHqX7thx{d'MA48fɸ英(~'qnpmÆ@:CT @jFhor]؍&YHHa)Ўu7V獃wHGhgH(B؇(|0V$% Պ9%}'AqMԸyG13Ij5L(%BM kpI6ٔnSUYOYy\\4yrdtugU FOhIVoy\q9Ly(!k/ZCH @4d"礐/# A9|Paq;qK ɚ'@ل7!% "%P@` h Gh  /|faQZz| ԙstљIbi@Չ%0G)YQ'3='i 0XtM -J:1RprrDmfѠ%'*5 zA/2p4: y a A .$x `|6y3ФN Q SUz}WY 4[ʥea_ PQzЦpz6* ^1Gx %9QJtUᨨ0¨਩ )&J` =*Ih:ztv pA, -1PRtؐ :*Z:K֪:z0YM@k6:C: сMOtےk#/q闰 ۰r V@#  !-gN (!#% 9oCh8;:{C,۳>n>7#(OPo<TôM {3-TpY[ SڵO+Ck`iHa10d^`@C j ?1G0E ocv0G+>y¸긱 [ ˷ҡ e:p;@c;T e9(G6 X{䄾#>0~PX~H1p舮f@镎]>M`fαNXєVi쉎N¶&'08Y3Xr4~Ji#ԾptpE0~.Qށ>^u\@@> A@#`sR~!?o&uv-/s47dsz LM_>$FAu"LN0HRZXZZ]_Obd?#hh\uCNLOq?uoxz|Awһ2W?^/O^O/^/\~27ЄG*L.}O0Dv\*{OIXfb\%Џӟm`׏ٿݯ?O_Ǐt`a^ɟ@ET!M':N7:]WV%NK_~J/}.LLPMM NPooRIr$qURSo sScr TTMD.klXJ0rVjPWw.o9oy9w4 Rk{ 3Y $(\ 8ƮD0o]zgb~] bpÈ'k0R6v33h, ϐx:zyhP.~1T lO>=JaI6gT$.Z O+=pѲYj']x# ۠ZHts.#ꪽ%ڽE7+pk _yXr׃0^5_άLْ A$a4Wb fA)\8 n@un]&MVA08H!"24hE 7Dܸ#2)\y=)xBz8\qŊZd̀G D&#Q])A=)v&51vU"vu1Q@9Jk(Xj*,N4UMrBV[ x'e7"w l}g_^QׯaیL0ZO2aҬNŨ\$]u%z͹mg I,t`6-\Î aУ̽Vo_s/աRށysӧNu߭- R;?&ByހF!#|t\x}KsA'q_1_vN @9xHHgdۆy( u zRTIȌHy#O4>FLŐydI.\$@MPYA~q! `(fldHl"rBHJ ~PeW^ 8y()9iJf"raEnʩm:nIA(\#J_' BU0.j G Gd{mꘜW@դӆc-jKGy.|0%]̢\QMal'yYǿ\X!hp.ӥz{Ġ0¸1!k1"Cr1BSVԉ@3TY]kL= qL?yWg.=;,v =nܕ .FД?}o{[N?_<'/ʛ_׺Ap5QNqI÷z:H_iuй'C?=ۉ_>ҹQ|ץ-p`!@G=3}t_JsOJ=hCHH{mFiM׊L4bAVj nVwp@:d j)vX'BmmЌ`t rט&Cue!@hax(|Hsp@RH&j*piV6F 9 XTtgXM Ւ-k)jk'IU'k$8iB;iO? * 0I~gu6|_4O95@i5r K]2@(bQ  @󒴅k*Ilinq9u9I|`"Ԗy3tyCvɘ)XiG-I{u`Zz"%P ` PXQ9Ǖɀ |@ɎY,ȩ9i?90P4aڹ!Q(3='h P hIQ0 jyhyt;Yy:QJ@n j z z.KͩRßp)G@im x:5  w zѷ6Z)8:j<2AC{EG 4IZLj9;BPp̗[j]&gjCx %{?:$*yt%uz]@xi 樐:*:cwnሧ qƠPxP#Rrtժo :JLٓš*jm9J2i &o31" a$ @Ѯt¬j#.a.4W@#Ŋ wNZ'[Z맂ıoCh'{6IJ겁p;˳s>7T繌4S rHKZ;x`Q >^`o3 >ӰʴֶGD 4tkt'z+|}~;+RKxPݰk0* 9:Yd!s+뭪gH_T .ɺڸ W0 +΁TT Wp@` K {ЋvZM`嚽`н{;{EGQ+>$%3 %oC)84)L 4  ^ <\0aPxp?`ՀP I r@q5ÖGaI FA`K9\;= ?lVCX,G!wq^,ƂP PpƊDǙo|qsb|w\vIC^9,DŪ&z, U,1OpɇɁ1P̵ùv v:"BP1DR2P [.g*  0i͜k~{f1΄ьΚ|Ώ@dzl<'Lq9 =M0 P-pBѕ` -6XL6*!їҒ!|@0uNx@Wp kzdqaܡHJ]pm-TͦWYe[]m_ <*@g immM?omԗ7d<a x]BT?ũLKrM==0r[Wp؞MȌ-֧-]R7Z< X\pvvv@T 5=YKȭ-@!=ͭ$-mлDݫe6Yb孭}˗ޜ,9~  XT ni N^n,jq$L::„'۾hWɡ!4.O6N8N:nC=Ψ?q4ٻxDX(ՠKqiN|q8 #ƃ-#5v~xz~J|ɀnC3r艮Tp獮3{^.w^+aa]q(v^(8.r-ue}!(Þ.ɮY3u5h禴<~0%.G ^tYq^~+&`5@o5z|OSTq8OpqOb#%_'l>yeFS=3?PPxIg03_UHOXk>/XNR_TWY!$ MCF_9=: iL3UeuwV]/|_~?LNL\_S%U4ue 履}Pi߳$į?/_t `O[% tOoc@ETM1v v(] N$V-m/N`ok,lmЬm.O b QP E4 OjC0sDԗQ puU Jv6qN 9ylB/i)uaO]?aqezú{V\`LRA* C?RbG+CtٲYgn$:'\˗1 !MH)%@$@VΈQMYe%Ye ]m nZӫsV(,Y׶ W\Xޞ5{%.FxqAHo%peI gl־^r쵡y]Zi{Kkӟ`# PpC%z&d7761-WA>#ߠM@RA܃0ӯ F@$@Dדm`K>N̫,+eTZLtXѶ{Qg:'T@6X G *S[H$+(h<˲ ;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroSnakeMerge03.gif0000644000076500000240000001431110534177570024054 0ustar paulystaffGIF89aDDAAA𮮮(((ZZZzzhhvvv-bb333###QQQfff:::IIIWW̥බRRss||6DuuNNee[[kkI!,@A,cX¡PbzIh hlC#x>įT _^B$p k#IzWQSQOc~EV((KFGvxcIƜ~(DGvxDpW(0KҭKٴSA>! B@un]&MyހF!#|t\x}KA'qDa@" > !!Uz4 Z7`*A.f1R㒃rH;DM,$SqQr% d:i%M\C~q1'd2i&sh%T&p"̋mH)d~j*jx B0K@ʜ'QZ"z #$ &jii!BȆvn؉eur,GNY0.!*=񬮫ĸ,G9 Mcr^mVn{ zaɵ(t,(S&WTmkԞEY!\X#ĹP1i c"xAZ1;p(DЖ!@04%aE\|Dk*gc5UVcҚ\DO_SsV#u]ZwE`: asN^ձX]a7xw}Wl;uxs?E?byQzt~AA8hn˫v?KXX%kaI{fDq"(HY:K/x =KU|>q+G/FS&zz g>U)ˋR Nek`PQu!p ^pN&Ƞ^ჩ9Y SB?0 B.0I!l?A | "[bX'iR gE`QR"Nшg){)Ҙ:3qC OC @z#G3Pa!Cj"C# AM,|%Ð V2dB Q~A +7K<ӼPD?1)2  IrwNhj 7c h9yuT<٢y=d$}l|8*PK;14)kkJc!q" 8 LԝDi`,]&`z~8~iN*ҧ,%Qİ0i0N5x* թ⴪IĪ՗I Jc ֩9j'M p+8g &@~"`'X8=l;v2ֱBd7Z_# b?65-jQt赌?d&Q[G-GԸ0G\|r\BύtJX7n``&Y 2`=e{WAE ^rb]l7x+oh_ҿM*L.Ho]K-.+|ٖh8\"lb۠}o|Q/Kp=&c8~3-]{ču& PjVo:5wcS7Up67qooQlHkuy'9:# 9_e9<$q,Y}qWyaqG|Vvlq='3BVW_ijZ_x.#JGS:X.cp~M~{ty?|hz@z\~ϸv_V2҇M៎{w!*kSTq#2F[#0E@y+Џ@|Z>]kg@}[>^?ʧ8/\Wgg_:m226e*y@AVEEw v~GBY$8_qhC!#Hbrz Ђ7EP5fwej= Flʷ{ ȁ&Kx=MO(7GަbmY;[]_(iصY4'dc0pMFohwFd`Ġ_LJqxzH[D``{sgaqML8wdMfy熆'(t(+@Bhp⇁}ͧA4hgpqg}n@uvBH E}6U*`e8mxta U~&`q-~ψLӈ[ŧň|'T% HpODŇSď֎Ǎ  9G YV5@%MxI U64EsO FONK'`$/yP#TD68i:Q()*ْ뱒\0VC3Q7My)YkChF0ՐpB4 Z$X#3zHoq syuYXw),R{ɗ.)mIyV?I.Aex/F^zR%P@ ` 8NX\ٛI6eXNH#i|bМPPQ)4ɹ4~P8 뉈I.gy9tPasޘpԟEF1&Qʙ샟ڍxXO5 7xP| FN@+!FM$68z`:<4>ZAj/JCޘ.Ф#RjU"p*GtUhjҦGJjxz*_|Z~꧗#ڙ/Ѩ_Uɢ Wi`m`Rx 5P%K]z!Pq  Jyꡫ/)a0 ŊGpMP[d8HEҪ"q=MR{ꭷ!J Zp84ҮzqB4r8 ŭ5鮌"EY`* k # '8; ұ)٭ С>b>׳c ![2${pP*D;:395kF H8JL۴LR`Q \`N3 AaGmd{fk1 ln;p;r tKeg Wз\; 0 Oi9ڸQ0rj@K KB{ 1ҹJm$p@gGp' 2Przk<` B{+kVۺ:/$\T%4QP%~);(Kk !> F ?r˿IN#HF*Z a rszdG–G0aPIpv<|> (@`!wqR썯&[ p`Ã`3;=ACE H6(j%SUWb[=]rfL#>Kc0X>pƷ@pĴמG}=H]؏ ײYقp;UؖlCm\M c Mqj<g]c cLEd+z:KPYq۹dm ӌL&+ G"-0ͬ`۫ ! 2&J{ e\ʇM=WmC4Etjq443Z\ɡ= ."nAQ}Dq V3~=5N79NbލB⁡_ t b.?@Y@tt]_n;&>e΄zX^lS qsNun甋y{~^,6nH~{=YsįaFW0sT阎薦8J.l;8Sϳqv[304?cHdPO 0P`6..@֎ ejnAe#Ұ!>el.6FN?eu-P%1 W$?U~/QBnT|-#2_#:tA/DEoGeK MS6SP>n'_X[/P^`K`4_oHHob&ԭvo􄠲Tqx+p?NƎ?m?_O/?o}ZgPR?H_p~ONď'roҏ/v4ί?{/dj]r:Ɣ/?@ aT6*NZ'KeVXQlV]jE̤m-Q1-)k#o2/pPiqt 3)rcRRss3Ԕ V5qNjV6-HxM8Cz:kyW [{X;[(|yY]@;ʔGK€ "md!pqT਄\tb@lLrQ@F|:R!x \#-laWX6 SL-z4* SGϜ9$CXi^TzSx2J"V6Z+X4FܺUؾ d!8Rυy&0=Cj.h:Wذ^ysg 6Y&9n}iٳԶ: o {q0NxI@c#o{+=@$CEcnU B;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroSnakeMerge04.gif0000644000076500000240000001405110534177570024056 0ustar paulystaffGIF89aZZZ(((uuWW;;zzAAAvvv###hh333QQQNN6:::bbIIIfff||rrII-牉RRDDeekk[[@@DI!,@@,ƎaPPʂRzzC"k h@C'xůT_^B,pk/I#zWQSQOc~EVK--FGv$x cIƜ~DG1vxDpW4KҭKٴSȄA>. @un]&M>Ha BD"DxDČ@nqGdR,zR  \q.Ċs!LVRʣTz"SXPLjb G7lpٯUXbӨ:dk[Kt]&"Jd ݁lm 7t| .^~ M 6e 5M4S1*E$IgvۢstrI'֩jx\bv;N=`߶|B|}RW_zڅ϶$9E6e]gR P6qa^a !S\w~0  f7`waip!8ȈW- @E*-zP.tx Hڨ '4I'DMK#$qQ!’d6y“DE9 M\C~`If鉚_i%X'sʤ`J¢wVWIGyGn9(lZrh~ RhNZi 2D!h:ݩHJѪ^V),b9FyhfՉgul N wG@;."٬DBR^X-+++=1m嚋*Z-]{ M,(A1~nG\X`> q.S<)d<%dȠ&jɄ<@,|ANiiQ'4=ͪVFgu1G' W@'S]]W5[7,A 5wFt'edHtW̍G^|wӀw|xV?bI{a}ync.vA:?K yfd%#Ȁ[P8aR:~'/ȡb@\wXHߢ㺺Uk_s~W v(\ g."D `+\fb!k ,M!}8 j)Wu.Hc x<-1тu"A-0҃F5BQmtct9jv#?G>ŏdBHƐ̟"o3I82 gJZEF !pH0L H VLd@`O A{LIQN\UMCy% 3m{) Jv o4[S7U j%wA0\1O=n+ W-0۷Ðj0vKy30_P%Ub+`h'dce8Q? Jȼ G@p ɠ|2L)fK9-d8rrB98ާ(\A&C,j/Ě9 ҋjTJ:0O5_J.І4{(߳=D[gvh[{t^9DM  o0yw6P-O*܃k);oYK_O]:}z|IqN\mν6fu݉9()GV:Q\oWLG\縯 YGzfz|whrٶ6H~uj'jp88Xog*J&YUr8crU u(!hmvh%6'(t4H +Hp悶Wbgm0)x(X1*xsuWt0v%s(~SYG7{;H FbOT,VhsƃLx{*ׅRR4aaW(rxnj3Aj iyWe)3ՂZhnma 93.aqY}[(h] ~0`ƈ {(lAUr"&Xr(x)Mu؄pM⋕8oȨhx5bWd,m)$S8rh|hOc6w긎ЎRsɨxUXI3Gh0Tr,2A4@h졑P )0bQp_x("k6.0y58:ɓ)?ɒb7[&r]PmS-HBUcP%Pp` 1)fTqÕ0 ҕ k)dyfih$j9lY'!Ev9x zI|egy(i `ٍ-90!5)\^y &yCU(( )yi 7זU oE0 81+T | OK\qaȰR"0.l!M qB#]%nםȷ K>L.Nmý6i3ۖݘɋnSWnN6,(Z^5.^5ꄁLNh(Ւ ;=v$P&Nu@n2@n#n$NvB^#6mN6Z ^5Nnn; >^5]*_ '^?Ө!_PA)g +3/7o>q>43/ @FCEGNOPO8Wb1J](*?.0kL7mW~&yBn_pOrzw//81&uvP5`3'a2r8o *af {_:}qx 8.opϹRoƟaa/x_Jl̿fߥ?ѿ_@G R*UM}P'TFd!ϴOp Mm*`nP A/0d@с­Nΰr SSO 70waTR͸biyYZW7wr-y9cR`wq4iμwO !4$Ti!S$1 MtFDL zhm"Dj$$K 8hP>iT4F3#b2FGOS!j35~lG+dU։(몃&+b[lIi4p]S]>A;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroSnakeVelocities.gif0000644000076500000240000003307110534177570024764 0ustar paulystaffGIF89a !!#"++24;;""((1%66""""++,!!++"+++#44#882!!4**9&&;--22$33)==#222;77==5;;;$DD'II)SS)YY'gg+dd*kk-qq.zzBDLJUQZ\B D++J##C;;K88P!!V55fm cdjor~tt{|c""m--h00@@$BB+OO&MM-VV#XX'ZZ(WW:\\4ZZ9kj)aa>||-pp0{{2CCCKKKWWC__DSSS_TT[[[oMMbUUddWiiPzzJ_aaaeeeanniiimmjmmmsss|||./02122557899;<=>HBO@  ''##--!!   ii   00 ) Jm$b2;124584:9BIAbt}zYFZoxkewp֔*˦0$(.78<;;9<=>?Q\\ECU\T@N@HDH@,H*\ȰÇ#*$@qE%Eˈa;R[ Qx'QfU$$ɋ:&ڸ1RA+0\PAlAӒ]j>=-ko9z9L&]̨s$1cA`b:M~rAUUY7W\6Zvru$[{ɳn[YM@a!ןR-'`Rt`e3DZNVzhMHy,zgۇe;1(M+ߋJʸԗEŨNUVOZh>f%0f^H}nd_5W *vb%匋56UVZ%pvZIF'|QIY@Nt܋` >)8ivԑHe xoJhXsvʘFLH{FI頡ek1Qcl`FFXXZerڥݩdGZ`4zj+nbp=b5${ZqllVt:&A#lGiKR&0<1H#3z<3FSPn-qSz&{{VӼmr 70+_6ܙ䶽;--e%@z﷡&K =q7JU57!X (%H=Z_ؠw 8pa$t&7AHArr*8/R ӓ^ĸGME;jM | G NByP: (E*&Xn! AohF E P?ci2i,MSq7cQH lHe*EWD163aILRA&ç{uғXt Ey|1C)UҸ-Gf: C #c^{_*;LmAh50*\lֆd1ig0b fa:3 F(O.ŒusO 4(Xd&J?7lS 0Ԍ.hH;38@*HF),j S4h ~HR;V#(ohуZp;SMmD:Spt?Qq,3 bIO O[TK.!K]\u+ zdU G>N5o+Z-^` LG:TXՈiG<, 4 ejyR,f*RT kiMՕ.umRG #v._]Vd+؜>epʹ" WJI~[i6xpJ!<=R'7aɪs|3a+\5/ÌR*>JhӼ6S]sa 0c*IX#&L'ЫN4ͩBE'hނH.}J#Y nrXX~g-taֈv[ܵFq9JIYݟ™rgGd26M` qTx֠Cs.U]KlC]F6d:A=;k> 0:v Mey6=oO&i4d}U ww-V5\K\J>0 [PcG8ϐZ7ɫڶޖixP~azbf GG63r54xŋ4I@GY͸C楮!T;co4K9 i[~f=iCh֏R3R6@wi~ "כaH7? ?W E~5?w wl(zSĆM7@l@ D){6a)"+r7Ǘ_|et~2V]TX@^SUqeX{$WZ_|G"g82}X+WWlAkP&{Q{8h"gT|fqiR6%;l;zPq@O%QP g'|уw[gBEHye3lsiַg1x W6.Y_{h7}GCȆ}ljp[„ @HJ W H)ǁVe^yGC!a{NjpX7e'Q` eC=T?Հ4XϘ~#'J~񇆸|sqg @ ";Ԑ 8=$: Jьo6v6pg[p8Q'zshtH P `-x}eL3C"{8$ӑP!I{ژԋ4G7 p Vi Ѐаg4ljJڧX2/q"ūDz WII%EU1 >y˺' /ʪLy18i&fEР2#C$!{șΚf%ՊAHYJGX "0U 0B3j;))JN~:(xV +S;X8!:mؖbzCzmzo5XDp 0i$wH{ A f&k$kc;;fKghڶ1Z/p6VP_x ڱ(KՑzfK<3K҅KPGӱU) "@;sJ#ۺ C˱dLƆ[BmRgL)u5=qM@ HН6@Cqɑau sr tHʑ5}d2 p.Ȏ#H,%K{kƸ MP|ɼxfQ W|{\=KhLċLfm'?=sL=ex0M4Rɣ4;<k͌.d܋j8Y%-[B,A#$6P t3)$/P¸ѲkҲFvf+ ` 2Fgr EV#cL`=mdů+jhSNp(HDpD 5X#<$ȢE 8-fӯL|@L{DAPQɠPNF D Z-2Ul;}yKNu 5K6; ?$t`pw=cM6<r X-ӃDmyͪ]Gb!839O=1*c[B1@aݩe? p<}*&A< ;lllPoU"AC seM=&}gI#o#pPs>- c~"n X$Q-ISmpPJڙ /# `a;?%~4!PNS V"#XItg$𦚨  à v ?ֻjn8Q+s.Kny@sPWlɫ v0"e՗i>͠%Dؽ+INe箑 e=(%$zesZ Y Ǡ ǀ s gθ` >dɎJA>3 Qn#>Z~ ~T- `tò>Gl>E +`0.+8b83RO# d=슽*ԡ~vS z:;q>@X l9) @hc澹#ST.t\ ꙭ*5A\=?a;?Vk.O~h?ApdפmВG@=.iX̘  j.XDz5H^YB4B S`+K&02# !S i~͟LQHAD^A!ڷP Fx!e/gڨN;v FϢň5nh1xE @ )U<  `aZt" mxQ`TF,eԩ1_\YK -WHUj p)%aRdQcFAv8Wݒ'bU)&SLa^BPP*"?]Kn*HPhЪغExv:*R !pYڰj #%?_SUy$`'$ 7k(6mvi;_SIRDcJA*,78[P K N3KQg&j-66>ݶ Anʉ~J32EZLiJ;QD`< :/1ċˣ2+ޮ{iCm06`ËhHr.*BҚ43!7Xw醯iy'5drvJӋF j1I nb2dK8c_9&ݎvZdȴ~ P On}v7#~\% "Ie [V7n+7ct*hbvS k/ &-̐TX7EsRzKB Τ 4QSt@l9*$#…2$0gw;Hxqn6xDAC>ref5ȵٰ4bC4a>|$"?Zk*u #ô@FHW$HWBP尀(dO)O `0dJHH$R U6< IxZ6RK7 ql]&F!v ;9UBO*Db0>\ 8ED*,LdJ[zeLJQƷeR\CV * Jt7 K_kYr$5IՏ{z8& <\1l; )O$]mP)(2ƕq%喦y}0 RbTbƪ0B!’&,?\f+pZz1A&DdVhG T0hFV̀]Yӎ7*M^wGÀ U8ԤʪFhx`pe}KJ| _Mȡzb%0!|"S&M(6~>!6Ppk9=wXv:=Ycd!Ԙ&CGl8h5>-q[2.E F];i[JR:,x~Gæ22\Mj`9§qj($R%LN1d(,yV 4A:𧶮#87XeP%SL 77=r]wj]2D2 0؎ҾC!_u\Mg+-QmZAdebwf<]o F8w-rWMW&.1'N8 4`_pN;s?˼B#/;9x!8I?r*#HhR!P7?ؓ 85<^,;J1%nQ`OJa҂[ 0N`#  4=a4HB;? :9);}P$r A2 #|! y"Hoy#e B<|zA+Ķ,.J3%o 0.L¸ɣ߫0d A/0ܟ;*s+5TB `CKlQDa<p?AiF C7j3@ >OQ5mtn1-0=яC03y<Z'(DW|1ĴE@I rC{@鳄s (8(Tj1#9Б2v(B/<7A ? :`陘KDC`FȆ귥70ɪ0hBCG @H"H3#h!:4F<x\F{"B$7Dǥ=֛JZ HТX٠V: =̬+L{|ʼK#6{G78F$ hM&͚#pTDT) pHN>(d;D/ 0J1ZLI0Ě `L#ERM X+E|QJKQ EQ2" ѤPTƧ$P@,B\QU0EBd%Y^4۹atV<}gb`؝[,Ҫp].)RJecbn.h8hc+R3ab^XbM;v,(Q>Hqf f攌߁ܾSqgl]O@c5f6|(b0ƵOeb'.1X A,^ -i ό("JUj%+0Pz|ez0·zH0^}MdVR pTH]ڥ]8 CX6irKumݑ8u}{ۮm^{Pk㜮hUUT"䊑ȚHѮSztK Ee=>v~ l>oܶ|{`^3_dl ff%pަ2&(DV^n}d,fqii*w"@~44kdƫq !c}ux2)ZCҧWSN`ҵ75pqCnq@m͸",R1 tI3p_z^BRӽG]F^ҶBЂ WrMq6Wxs}pvXV;tspnOw7A>2;tTC"i56|}o}|6svuM%?vgr\vRQ8%.xNiP϶LJ}(xMقlMRXdDWäw;DAza jϛS "0b8vw'xy~yo{hR/_6^n‚Ab%-l-t(iP· P"D@xAÆMƍՁhI]2f\L:A@P0lE)&oլ^xYeeZ l[7p JhQE7<&N$bO6R&MnrZ41i}חF/0^wE7 HO``XtFVtNc#=Vv_YIum,?zO?ȓ 8D\V[ZTXS"9QF5Q:Q>'t+-֘c4=Hf !E`hd`l3*"V<`t9JԖآo _X c#>%I't֥d]Y4v嘎v8]њV"Nɩ=0BONCK.4І(DA#hqmz=)HS$Rb(Ycݑ9[p[ԝh?s։QyA}UlЂ6d fI@nVZXZF* jK(CL[_I@ޛz- #g9V\pv(=6$: n+a'Sc%"HERv(6~{m +,N8PdJԏV]5r#[+jvc+sBqg:"oG7%Fֆ8Xġ91x k-9Yn6q2*ًqJO@+~ѥP~Ӓ{&Wrꈛe .ɔ|%;j㴚5 Y<ꑞ(E0?r`` 2@p4phYp"5 iG79NUS*9ub2գ]st 4?Q]-H`x|Ő'mf#b51)!Dݏ%*Lf*{jx|!G#C)PG?p&O|uM6$a_ug%$<ʵU9 (y:Zj`]Y㣶xqSBCQ?!ɿ !Emj68̎BRf@SNfKGHiA臛ԣH*r$4#9ɴYqd&L(9-@tg.!adOy\g@# O"D ERьj2Zs!l')V20|>k5&%*S /-(! eZ=t]3"MuHC)M~AĄUoJe=JÈJ6%/ 1i )P:ס6v&^픔QFxyH&r12O񤕱 ;֐t6,e2~Q< u,zi!9M5TĠ1Tz4odv>Jc_qlH^YN6y'ĆYɴ]Bєu ?R}>n`G:Π(pPj `5Yx[3=Q7%.g\oc{twݍ~4b<3sN@ʃ^?=S;itksnap/ProgramData/HTMLHelp/Artwork/ttAutoIntroSnakeVelocitiesMotion01.gif0000644000076500000240000003513110534177570026312 0ustar paulystaffGIF89a !!&""**03<:""88""""**+!!+++$00"99,11.;;2""3++:$$;,,22'44)222188;22;;;$DD)II-VV)YY2OO(ii-rrBBLKTR[\D##C++K""L++B44A<BJ@aӄ  ""##;;%%((    ##44''99BBCC   .33??   4:?HTv%m+2.0*.343839yNoxis+.ϥC69:;:5<=9<>>\WKGH@,H*\ȰÇ#*$@"$|DQ3^y'穜/ʓgP „ PAN@ň"ŢHU*O"Ppaaò`|뵥ٳhѲxR?a/$˗ߒbFjϜy6R.}8e55|B ZДReKcVK,KlRDgʔY ,Tˈ}*^\϶vq˖jpaB D6u-_n7l|8r4.ӗF&ͩ8N1@T']E1PXMQpUV[uj~Z~O?a4FX t_JdZ|0!s:QDE9T8]R)T84AME%V׊<SQ FO4$8 "u\3t0!Qn3^?iBv(-13x)$Ƕ{NuSNN<[Suigk֫TPHXu$`aT}9ˎ {@H|054pqslvoHbI M*&x*S+ A'M1 }̢Ons9!ɽm͒ 9$ϓhiFIIaR"/d@2 u. d1Y#wҦF"Mx%,œ614$Јi@< O%LWszGgҒ>U]@TDe6{Sbvu_Ũ(y5ņ'+$dժ xad-0;n AzMLJ0WeV[WQKXf35eUpFbxKWF)@S].FyRki2'mӔT)%ҫ݌ZPq;Un}s md͢_틪 E0+3.]uZnguCe_Q֌/ )[ض߅L ؑ'D _8$l<,7fy#6dF7Nr' aп(}a^`;O K2_e tB aJ P ]e0NHuc_ ɋoz"Yv fWM2%2xQ'ѢrI,.XfRu u[(o8Oq 7"TTڟ~@ǗZvCVJNq>nuǴ:%SʄB/ ZN'.P(% XD-eַSjxtY$81NH8/%f̑S<|S}Y9h {}foΐu/cXW>suP2yYMm?O0كn'.$o 5h9q(u'N69a֊;ݽ%w#?>=?P + ]qy%!-ze]8afxq6lwxc`JC"rW6$Hr7W'\}; p;-~յgƅeuG[udqtW|"=e>g#HrK v^PwͰ /rߗpK#6[#+~tXv̇^WOoqgOx*7Vy 6 : @襈;_d%qHAXCuX (RxXӱCYxgw:R A0 /wTu*"l|vw P8Hgw^V p^S W oYj?XD(Xv{L>XxXyWH .py -2}!g`?Fȏiv")8vCEC- kux% Ar*ho7sC%(e1aUW&Sxl. {`E-P:"fJyNԸ-r5؊H2YVxG i@-P ^(1VhzLY(BeiWeg0銃wƕ@ 8z+MB)InJGzk)y{x槮#  ! 𢐩+A<:U9e'uf7tC>g|&T`5H/`*,0zY&XDoj()tܶIѹ+ z* p1 9Qү\?:\^1"*^zɭnxeg7kP @ @ 슴AG +0($#%^Kb;|1kV 1:JB׃ 1;u[?ee*pa6zY+IA Aq# 5!ۦ`٪Gð+Y( + DpvC/C/0 H:B;۸qhRċ;0 [xOK@( E!`E$˻뫾Xٳ , U z W+, !p 0 ,S[4k|: PMA`*0 %Uf,qT)o{Fr[ >Xl֫ǣp= !$atI{绾hGf[9B9q+ KpR !*QY+jl,hc7ByC\Wm[ G@ {`⣴I[jZ/SX>\\?\d[J:kG Ϭ/zNT$5֋maIQ1 3a9Ӎ+ђ|~R?̶$aJ 0*Pj$ Ip (@L8ƿ ѿ=b;y#g/ ;Aʾ0D7NB -   _>YDȎ==ѕLXsJo0; p|GR({GI @@ 6Bػ=֑˔ L%n k+ >@ u~.{w0 1[S,L;ӻrlmś! P 0Zڲ89Wڸ  76@ p?L[ZJpg<6*z]P_6{lھ  fp 9Ӓ{g;"Lp!tmBWL S'> 9 iPB3X:?|9>{&Y*Є©I( F`?P!tK_7|i>y&0k٣[=C 0 wDg< Zt ,0 B sKd( +.zh7'& `B{Ӑ]\|&9I~ yK&JAHjh1zst@v)x>Ҙ{~ >ɒl9޾;~冴9Xv%g P0K#(0sӍ%rn `@֐>>llfQ!04{o'Y}hٷ{1{Vn xPs' \ 7۾㯞&񏪸Q~/{jV#0 ? nv"omɑ`s@o 1cX  sA/`/V%*UTy`7H 4!$?u *О =?g'@46[ z x{kRW"(Uo `1/.śW^y"\Ä%>'D(@Ax$P(%_œy 3ZjB /U#Mh(яF!A $Ȏ#H(1gϟ?z܈˫\iۦM;xޭ7A "+aFy7& 锨ђ))E ̘aउS^ 4thѢ&0@_H#L@?q@<Ֆ/hգ^;mm 6[BuRqc T%L3kAP|2`;9! ._h&6v܆ 𮸊3D̩k s h^` ̄L<|Л@B_XҨ y1 bހ3Ȯ189R3I0CL mrLX8Zn6;)S< .__P5@C 5-,r.":%Y2[lD(`%pnS 69d_ZQCG5դ@ lSO@BKf,$3Zr؊DUG+/ZrLDMDl[" V[ w9{y B4 لH$+.QEͷ"{(ds(4B i5B@22rDVu1GG 8lfh(T#^"`$}=n\GZV,4Z4‰[-K X`U]0@`XveYk{f߉՗_/eFf`L:2iST,U}(2f5qY-Q']u6%Plˏ9;#iiB\T+5`&qg\|xcumNA"=~RJǤ" |k| p l]8$E~ST$(ER3C!|bp"m"yIdL @ p .4Ns=UlW+[ Ff)2O}jd8M0WF@=%.OHNJLYG _$U[ ZS5քJ1q\Ay51/0{/lR[[$E+:3#"4ihD, g\>b}ܦD(P("H0jygƺ43p/P  )9Mk8fJ]Y,LR0, 8iP> $1yY0B@(H S*UMlT\lIXCSYl(2 k`&[9 أga@8$AJ?xe;I>p{OybC#I _# LVIxj >;ǽRHė5xڴfT/poOx9Mq _ Kǐ5]J/YR 0dH,jo뮋w6YCm5AVCpe\QVM`ApQ9 ̈hhM?7*kg(xkm2M]1 L`#Di+R GնoxB,n$@0Xe!5B|Eٻ!c 薘WK d /O[V`$ZL].)KZ*oV_"H'nl ]vO tEwyaFUi)ݞҹ4:}`5amcU P4(Zx!f@՞H'< axkPcUe:+Es>8b2s74a_kWjv9݁.P2)2iMk^QfPS%<^ଃ]8&[5DA^t>emd%^|~8EY:q9S> [ު(8eӫ.0*mk#"d]GP|34NW~m5^"z#*X{Fs'6ӞW :?QE'VP^#7𤝪1מ#)t/&+V=!MbJ5yfvɳ D"v˪5GXVy.z[š,lWrdoI?ow$8UPv X"[h991Ru;>cƒjDz<A?[89K,J( X:i*3iK) 4q&Cb!$B+ GTWp3=0#I0J$+a0P6U@0D[s2(`Fr`DAH7 ?FP:$x-xX@)A/l)0CE`58@dr:51>= aS$APp Et%Jc)պ"_%ڊ`IX8P 8CQZmlE[Z-9|0ʶ@ nDD3;S¼ KbF @+CSlA0H@mWF+DA83>H %F0T PflF7 ЎBTЀCm+=SDRāژG ̒!`\C(p*#;xP8 1[X#0HgAH9X|Ha%*ԣH\᥶;\ ❀$LaܜP2 `XޯHP=TTളj`CI/@>&pa_7_y [9јjTv he+(I`%5>C,h&NLbK>fd5E xBQрE 0H TChiJIzġC. L$,r]i#{6f>揍eN&), %UxTkWց[Fn@۱^ֶwV F{~{d>kh3$eiRQnћchnC`&jFihPA@CV#DڟϫKk+S~$ufہDl,IAQ$OV n~0lh Ŀc=>2fb/&&c߱kVk^XꢾO6K [PB!iɈLi1^l'ݮlD79Nn%%(6FksQ~xBnl~59c}ifu FP(j6mY(am˚ PejM^F;Q33-ov"MR>VnD\mQ l6L @Z2j @jW$nxAHjb\Yh*00%1ehn4H:׀%pniP&5P0C|3Ni{_u*WQ5ιzP-AQsމ^.5oS#p2,4.s|6o'jW>I5|0f|{`/b;h Kne5>5VHlZbEO hd{6GȐ]U?q/`{igv/x|Xvj΀ Hh]W0վT ! 0nBNV? {c6qWQwgvvsgrD/ T7#@`yl|-zd#wȬ7Gȧ˧W{b|PPo{&}0B[H (UUBT}YJ|VdDw_?>e57g{p$.h‡/PhHA \mZ4t*@ D(idI JN@Ynq xŋW-޼y%jѤH1wiӧP@a& L aˇڴj&̅5| h7ë !r0AFT)!@PF@Pr%Y#\I%X&gП?u=Z)jSWZa[cS⃈>OY|…;+\d&[9CnIc4unj|2d_LAX>YmPӧnmjDpo \p>Bm AIUCaE@#Ml'fRx%9v+!PewZ`{ 3O"Hm5f^!|RJtITF1T |їT?*TUqߒ5  V% :PefXaf4ew&>݊u8\yH#h>ciTh'j"1>n>8Rht!jأcbgO81|<{6TU=:pC[o"p "O!@BURG@|)F"pތXO k/͌pЫmUa])U:`us^9Y5"0zh@[h}[V&mOW6̖^@1mW[ql.ns(@)^<2|k`y1Yj*> sq&C_uKk."X0!_gi KD.h_H[K0qa|A#P%ݿO>7@XaO2H|@cxO sV/ܣ*aA h@rRn> T5\xXAƥ3UɆ}z1Wv;ZNѠ٨q^ܾĆL).!FY@5 foQ (D$a8֎2+ EʐQ|^< )RSR \!D*EJ1%tJX& kDYKע 8 ?hǺ#b@,h6~_ # mN~3&Q02U$ՕL<r4VWsGR}HXJ<>{TI2yL+\c '˘@(<7x)U6+zSb {<v9^p%܆؇1f<x93cT_dPЛ9KB^T;)_FMth$pɻB-|cOpcv4bE3(E)B1H}"ԗGU:Cl!W Z t`C*iABH HBq@>s/ 4V-Xs T4ky 3SвNA fS^eFkAQG<E@+4i|(@I4 n:k$JAq kZ}cKRL*%\9A xP@߾٘nn§Nɵ&-H.n-DuiK 7 .^mgcǬ$l3xCWCTtee6UpHB4PWpu._1Z)pvŁ4ptIKY1pw}TsDADl1˺ìe1ZnS-`wU,ey`Vٍ55!@4IeIwŗ,;C 9IT7QNBT ?<iڻQW^%e T ڽ\{ԻMOPJR.?g6TgqKǖxkbVBљtm̘A[<K}Fz" x`c6RQI^5ꑏzϨbs]|ghf!C$}tXq1%M:1f1d'`e|P5XGU8iA h{hK)o?pe~au\;TzqVIzp5 Noͭ/F>ތWC:!E}轝ۑ=@ꅟLLb0=-%哉WfpA)ޠX}p$mdtEQۉ^~_y a`KޅĊ%` dT% |) :}^te_ukMYkiLlJޱUt ^M NdFV WqpT`h^tQeItY%U|?TyII̡li^ Gt!|C-Y " rNߐ`Qxݜd Ӥ d8F_g@hTS٢pMja"𜣌|`$R; u긖$ceNĻǝ ї #LODOP:y% yp ?$Iz@MPmxKfȗs97*X}4@U\FN_XYؘ}A\LALW0DA@S$KDC$ B\fN  $Ʌ%8ڤ1 O$ qvsI2SFS:%B|zQ%DS"VFPDVݽTqXAF/uGRJfdVBPWœofzeBPUfrrP]qCsEWijF}&`NƖ ĨMV(b*$)L!WU|))));itksnap/ProgramData/HTMLHelp/Artwork/ttEdgeBubbles.gif0000644000076500000240000005026110534177570022172 0ustar paulystaffGIF89a!))1!!!!!!!!!)!!1!!9!!B)))))!)))))1))9)1)1)1)1k19!R)R1c9k99!)B!1Z!911111911119J199999BBJJJJJJRRXXkkkkcc!BB)ZZ)991BB19999B9kk9))B..G99B99J))R11R11Z99VBBBBBJBBRBBZ9JBJJBJJJBRJJJRJJZJRRJZRRRBRRJRRRRRZZZBZZJZZRc1BcBRccRk!Bk9R11c11k99c==gBBkJJcJJkRRgRcZZZZZZcZZkZkcccZccckRZBBsJJsRRsbWmccskkkZskcsk99{JJ}VVgg}\\kk````R}TKows5kssfuAtY7tsd؈埽}íŵεǰέͽνֽνޭֽƭεƵƵενƽεƌڪᱭ䯯麺ν,H*\ȰÇ#JH1t3bܨ#ǏC)$ɓ&S\%˗.c~N8sɳϟ@ JѣHǴ)UhHիXj=B>zNKTn%B"Fȶ!c$h5QlmӇ,W\LMqcEr[g7:D[wTFM4`$i(jnzhbŇFupbϭ\q6|:MEm߾%*5i"7N [Ѳ|e9FƩ]zQ =bnY.p"v":2m *&H"HI*ȤHV PH"rYHf^m֗-7B?:R&-0HU8X UFPH%X(m65R3ev) N8J:d*#ׂ7nhW H H(K_*  kxDH4"hQXϧgzZ=C7" dkuxזSIPbQ2P(2n&]7~g`FnFH$s6H :ԳhJ͠=ȡHP@P#C찈7 '02ip"1pJ9YdZ-.2mc= Jф2"آg;uay[*p/F8*DJhp:|V΍']꜂S/걇qyIu؂q!k6\`| XG#͠\)Zj$#N("7܀0@M\pqm]V麶Z*rSF`tE)ԋh.Dy|++.xWHajTxo?'f^ N0 9l к֥.TOGr h3LŖ *d Ybvu- Vx)HhK;qEApGi z7J\0AAk"м ƧCLF&˙ ┨D%ֶ ,e,o ! UaNWf4'AHM`7u %,t6iZT_NHٙTTS,O$ 0M3IgXB2 ݺ ID$fAN5LC#L!$Mh[8 U5@Y T ?XktBF;S Us-Hg:siDaXš@XuY 9Ɉ=zFpg*XIu((\+D準NSv+jJx1JVsVRCj4E>"n ^GI(/𪅈 vTJ'͓|:^Ӵ\Un *dP.E7XHL|!`}QM-kR<`m"0B X2M;m oi V=8': h c$f5TհDׄnmF7D%EXaZ@@NH\#Wv{@bakUMyq)k$`ةۣ =0ZA$08ҀzxXԐSKr|L5L! H0,`Jɪ]כ(`Jm)  /% M@@>`'a -zGIJoD:lшA(ܫVdlӻ8 Tz77Y@QUe6mYc`Q{dL*W'Qb(Ck~ZʚƘ[1Dy!t^όN4O:`Zp eui@f$&w]kc8 ߷7+HW$psn!OQ2%mOi(ap@F8*H'd j`OBVHrd5-ӌNp 0Ap+RDhŪih}JE5EСpeҰҭ4XH0'`  @#^uf6Q0-NX2"4 OnT_)Ǜk Pu;:8xx;av lWW.NY.{BUnXmIw-QW7W07zoߩP}Tmb4 = 6u6DEdVi0u%͕z/]C }&)3]fp~nWVX@7X3:7'^9"DTpt%PeNDgB @4xp{nP0FbIzj@'hnozA!bv[E=4H33; n ې2$00xKa ]PSTm900(2Q" Pei7@})dHs~釂1Pp -mL7B@Pt"TFC | KK)B+t}dpy sr)`(nc cB% [v&zZ{L$0 *H72%Z*U0U ]'A!@G WyHݣ=іn'|-R*gzVi&gIPXpzQN-bHnqHu& Q$x/PAhy=-s#v"FG^k{)#N)r,YMDsdv{\ePVE@UB>3pu@oQZ*C|Me?W2]H fX/%2t_o 'np&̷^ B1d2/+o dX9wE3 n@ ,P 'UZ[;YZ( ꀼ&zԪIT@+ Yi[FgF{IGä9]a&>j@EJpt@%W&s3 oZ`"(U臹$jV, )ecR9d4N6sSc7v|8 hTB{5TCvo sܹĔMDM# D?5)ņABS5D$%g"QpRBDȲ{I'%P˶[!) 61r'ZП3x'#`9 g pw АC ! 0@LrP*UO}! yz[C4j`XQdTZ8d!F B0H Zq TN $BETL @XK) ewz{^+[k<|VBA]hT(2NwoH۰$U.2-)Y8Ց[a4' j Р hVk@yPܛY9:-~gt`sdSH Ya^uV` qrWdV8ac4[h]ך dБ B%(C:b9wc:؅ʯQ RmZTZAcIgK0`uЦQ*z( =dRq}ۚ [RNPit )nw8ʳU: {=`FP^ YU BP@e V[S¹ )&( k _ M#]ֆcP%%Ph {|yǟY LU3m *ĺ5+EdBY[9.j>pQuMי`np|=EF N!!&?նU<$ӥX_&6A]*3+bG1-N8 0i2e|j]@ sh-Cۀcblkw[r ;7q@:Q6dsS vQP( \Uk @9>J՚BL+׳~gsϳlmj ^v(~Q:Rϥ._pv֨dh ,0 &Qە/ mxTKz]{&'~[wuن~/t,R˼w( Zp Cҏݮm @5` -@G.$$#q T!W\3?SPT57B%{ApCz5U!gʀ0ps/$"BBSR=:f0ń^5LԚw/&4r+Z\sQ Q&#x' @$s[{#pi5"FbL H'L~ X;`$Dz"U#mVO*QH,LڶZEu P#]$J HH#jz&6D z ٺz$(M#j'Ğ ܐ#׮ۧQ8 M4+EA$w 9 1핫 B$4TDŠEyq'M;jDJH+<@ɀ*ul`I$hCn2;LDBŠZ0ȟfJYmANzBa $h89Ay:}([=WL["䤎%⨴Ͷ18XVmO 4o5ZE}']GBOuBdoPé80b7ucg]DS.4ҧ2LIC4!$QាI%BtbYn& Z!$l7X\T9(r7c;цʉT< f KEdd6QRؠ 렻kmA KSbigzTUp4ާ 33,LʋT$ ܆JmmzwEI @{0 +ߓF:4DY`+-RX($) 7J։Yz;d8"#;L7ShFb.*"`%H@ [! ?9!9PBH uS:hD=H0i4 9OagAlnQxCL%Q&lU.z9`x1(aH LT؆,xZH!f-XV(R Ƨ]"ʬrnBP_ +G>'0&K] 8ad"ܐd,A?i¼$ A8!PXR AQ_a$h!8=hS2e"E) 7aPcD 80<>g)˩ \DS>% Л(Qdd^٠M1/hWJ^{wPKPd4Ջ/iHOY4`=**7{V:EC 39aaA0O[g"$zp)99bd'`44 I@M@,ɑ"xvD`=%* w¿% (󸆝>X eHHdH.Vw) 5;S]C!ᥗK%SE7[zi5S >\I8zh ,Ő'15DA9r.ZB UumLM[E>+2S&V83TΙ.w S%HR,ԀSԘ69HwNT:T!]uuA0`ZLA m@B#$0y-3df7C٤l4˛y'T{^f,mldZVr^|@4$!h )@˼%vA\8 ӂ$CJ)Ef l$/AJoc"0V' $4 N f %F;d֝DwzudX0^#:y^`F,D.bMJe$kB#B Ϥ ( ?= .z>-9ͩIީjDH7 h_cP)QdMїNlO1/Hbj 's0cuqE"KP`b!& S9`&$Ht'r[9(tl⩗!#Esg!uhɐ3 OD#b+df`HE@JijQUA+ [Hwn*nx!%عʲ CRXtΐqߨgiP(~ӏkN%@A 4~{rUJHy^wZv*&lBPT,d q!X) S2mҵA4o~%\V1%NL⳩2A\m Q\+}RDyxRR I;+x"-{Fmv5bBkߌis3uohu>wW -Ngvѕ) ª"v=xN?z YXO_k,F߸5P7Sf Iz^@sI$#+ ЀK12*Ix>HZF$ւ25s5Sڇkg 0qIFv,>y@Sᓩ 'f<IsHӴ8APwJ@@X$ˆm!)++! #nX&e1|۷1 ! BpQ i1 Bf* 0C+p[ZZ ZC'`PguH@R 3SȟhVT@4ۙ,%/2I[7iB @N@[;I+[6HBuف5*+# i:,;`b pHRBiq0qtA4Jj/x-`6AXFh5Щh6ًTh4TVKe0NvpMLM( h<\c6꥿CQAccPtkT}|Q0#`&+ nxt9EAkj0\IĄA$l֑:^b%ٟ?C @Q4 IX›"\!I,sj* ) DS!X{y!I5T:0LpIdChs1B \#EW* e@Kix! AhJp"ikuI0Ơ 9Ecl䄖\KĄ-@C~:$=GR12$I݌```>=#X?ȅagx{8oc؇agheF9 =>@0Ѓ9\lp`OBJ?8 =J?L¹3$Fд}mO5P3Aյ̄+p/q%(p0BqCVh(eZ 7۠1}E)ڈSSU!D 74$ -f=$P5TL(Pu$V:L UӄsZS쁫U4( P 󍟵NPt4F=`"[O%;C8DF`WtCf]@/8hDq[r=vؖH1MK  ZLH /EΣ{9bU \+ȀuI$UTGT @^Kk A B7[ݹ匢p^] ۪9:[FH9:ND%iq!QPȓ@B2pLhIDW.W`2d@3_LU7ȅR/6 H1^I.0j%MŠSP IB+*:+&@$UӐͧT5 5̀<%Qa@RlW5,? @EH᪄UR! [a !V^\3BIOhD[8aHK UՄA(:96;a6!ѱJcFӱ _  @\pFlr:}<Ʌ4  w0\3mhXEe" ƃs_5p8&jHTŨЀE-^f?l ˅9eb<(7~> N 9kzc%acR> i(!R X7PvȚ4Űu5!ꂞ.(1o IX AN'XHT%F0H={ǡP5 ZIي]ϿJ>%Jmh50 m iuDz`*znDm. 䁪*`UFŽ$X"3jZP$F*)s y bvڈ8j`>1jXc*Ey8 %q:%\"h5F\j*AN&,ˉ;8F'+No $(q(Sjb)ZuqʅF +ȺA7nkx[ACF@7⾡x+򵽈^ [#H+~BP)̛q1'HE4<ݭ+:jg*)X##amAO ͠mݎU*pZ2EO E[K3Nཅ]u0s C"f#4= ~qI:ĤN31k uDx\FUG87d&n;v?$D(5ΰ$Z L{ÐHp7;AJP _Tn̠@o۾>s'$"'fAL+l!n`?+#]#1TAEͺ7T os2ԅe Kgy&ʼn5hz)w2aH Ɨ hNH(miǑT'3@dw0mwઋ`7 H-[XNhQ6.&[')PpÕ}wAXA )yɥP, aeX$^Sxmՙ7w`,hdR&M*Th A NLyPWHZFP ذٺz֥DXqS碓&6$QH@vrBad>}\ZrW ޼z3˪/`uՙk07:늪GEKUDq0AEiD(H9% rPD(P5z$IPT%bd̛MnkkmtE __\;z*NaڶE '[j;rS#`U2XqtlS  NV.UHRnpL5C'HDÏf"H HIQOi:[xcwQH4K.p&-MOHL `ib A(N[x"4-XiJRHKV4b 5x N*ص'WԱ=nZ=<ڵϏ٥S!x@X=Y6 CB|t^"(L(4@u(cb[Kq“VaŞa$@m.Et "5#d>N;;*bHGfoVP'tZk( 4cI.ۤe,rEn "0‰'i8QPN4 2]E6P%Q?؅LybZ#1]+J,5^Hȵ|Ё$ #ˣZaP Kn;NFE=aJK- 'y^A.ǂ,2Mω8E#4ӕ+Ӡnu^*T x\ANvq4H Ed5_5Lk#AZ'jXA,w!' ᄔs;51ퟣ-tԦxz1#§. õj Շ9c1 Oy*k' Fib@N7,*qN _B8q[EjV rq"*v !rHs0zc>L+7Y갈F4y?EAeܳ &EFhW;ɮl3l>+#]D H {@.H|#nYLA$ÝpiHC!u "c$irTr+DF5`Kc04J!?ȯt$ K "G#%\n'- [4qƇҠ'NQhi% Tpo&c79,91䙣0є_$ FMP"RQB;F#aXhGԱs9?F*$"כ%m^4Br;) `:/.%Wvhi1r("H"YD.џZ5@``zdc 'aVɥ$1 >`Am!H8%=# :KR 9TK5De`ua R$ttW 2834fgZ5T! @Y"LQqc.rUQxSv1~h J )[dz=@5[z䘹\Z 5 NX )#c0#4V\WVmtcqrB'/p u'@2WDgZԦIh26bȰ'[B)>eR{46'? Z+_nvƖ:lDnE\ԩ5ZNdp[լiLˌfBBC|t"Y'p@3`ԫZ-!?:Y_ isA^Y>LIocIԊlX4Hc-ۀX$n$d"!Y*rԐlYJT=!ilĆ2+ ^?QfU=8{1ZaβLb3/vb->(thF.KFD"RiFz ;Y7N%\p. :ˉVF?z[ bhMA.,C{j긥N1-d&"Fh'DbP`j8H( j|t~MM}2Ayn1!l}Epaf—+vPp8NHK`ȋ (3[pq\jIH+bp%7'_3nUL;AA2qo0]d`;=%Q0 c$r#pV@tƥ[TyFplM&LEI|XP qDqB*F#@DLQE0'tPp G{G!^ESXZd:qG5E$%)×̥l 4n[ (\DpAH@Hp\:3!\2DU$}LH@ hKlouHV8SXW+LԓK ',,Ճm { d 6LT=BpUy eqR RiB5LҫaT|LPAé}E $BRG(A$>i 萭GPH@:]@=#f%R֏؂P[8VT$IW5R `SiZDl{>?Py!}ERR͡E. 9El{ NDU=W4A.9 R!EAr1bd}8A\H dEɈ)Llp QdCѠ!|4x s=i'(ÌՃ?$G AXQB \R eǁ9 AqD %QAĘ$L_,$؅X%|lh@PS#1]vMqcDEF"B~U?< ВU_|؆ aUHBN9F$c XD؇uE`ΈOEX徽Q*Q<+bͷD;p-B^{t\,m6Fܟ LɌm`h^ϴQicD&>Ja {:"* eyJ$g]c{"#T5 LĊgQ^mâ!zzvD|+  GPXۜtb@^ :T-dR"n 1VAD%+$%E #@#xV.1.|r A6 $XSVdPbp$.g:JT T@X0&wV4cDQ?V< 5nlLHDB'pZ%<\aRQS$'LX ^?eZ ȤEd@_*JKx浄'RE08Dv A*EYJF6B,IRqѐ-x+E%DT@Y[Q- KӠKdۭTF.| lQ@Ň !dP@3Q^%-6D dd i$PBFɬ|HX[Ob4Z\l#Bzf\AG"V)mh_.T_|@AdL Y-jEN$f(FmŭٚRmC\8oј*LlgzdrUdR,q .#-dD\"H晙7 @'$` ^LDR# VC6ҤBd, 2gvxIdtT t $WJN/]n#U阅jPR`jEl"6 }J̼e*F'V!*1#X>I-+v@-lq#QI!2.0y5'RD}\NMP]^bEJ (LKBMX2 b'jقFz@̰d)iazIFd赜GKLPi&EPUݴzN/BTВ^fjۘi! @^kAܹRl-.0Bn)Y,˨%]Z=i$U; hyhђ)1hEgڇ S2\s]׻%ѽdkQNK(,9bFoDBo֭8u(JF%VłkP2K)v,sȔtym p񀉂hqtPcEPRp*q*@,؂r.v0)p´f{32S23s^$ԃ(8"Y1U3҈^Ă TSSK[4QMQ9 Wb4SZVJd%rphAA= TW_D]@,bJiYSIq!f};)ۺd}IY]YFQ4r1k#OMצ=YMh-n0!#U-oM֕qI%2P;|NJv#۷Bixp[YM 6D*!ʎ|[Ɩ朔I9ukS,}hl 32'X xFkPO)&]5^$!&>+Iڢͤ-$߉5JDq#h&NVaGЎ 9N؊cPxUT]yʐbzLD.a0gbj@^i׏U ;)Trpx 7FENm ǃ=]H;Gϑ+9 r(PK"5䢝np1cM& xSWFwO3Q\Gƽ$%om(t9l#{Q}dtf#$7[46]/n$t́{RĂUP{yI#p%*>Gv F6yg^d%w@ΉP]gT ̮h/AiZ) LO` E mBfٰYĊ>zP ԑI XƊBu墶dzԐzVo5O %|HT''qSg$NԶ!( {hjTSVUVZAUgjXU%$)U.S ACFȈ Gmũ]`E'HഭŠu r#X NW]Q[8ueK0qB $lYIE+f»VpN5>֬^ xteyr*d:RM/uL12.ܶm1Z$h1 uXFDDz8kANTROY'bCBjKąz re1PÍ ƨ{y.jēF10>,%X.] Oli= ފoTXH,BF$-Bp% EQ6+R;Bd4M2 Q)Q{+BT+@"1vh"NRH WQDIe}1Zk7Q74sXu:pb)0NR"^$0m)kQLͭp4$`>"j(/dH;ꝒRiД2n&*F_6+A?UԸ ;ID҄8yDƴ O4 ±!ꀄ*E .,s<F}Irq"Q./1 ⳪x' QUMAA"vb8a' BlA\4bG@b _PRE;';!J}YܻIDV]rFSX"`utɉP tkxK:F4"`""(Z8#'A-eN`Y",(CD`rJB0Ѯ kb"-2ZTJQq$z-:!FRQ"Tb%)$1ʖB܀K6N2HC! ]^*8O;P7(aiЂ5 BpB8_9)HI!Z2~,D*?Qa I{1"#(!D>M>1ڐF\)!MŸ8a 'q*ӗu z_s!QND*Ї>}CBOi N}F7 *`A[ @[4PknĨљdmOPS/,am(H hJT2% 2&)v򿷈BS`*P%*tE(KS*吱B*"[)_֦fa#wiƉh7a{_D P0n#tBH0q-h,Օ@& ri- 0}Շ:Na ZdeXތ["F{EI$j”"H؈p`ya'pB:eNvP_  H$ա ˗V'ƉG:]_8hKeO`:K)E$܆K#ŕf> o8DIw!'TAwS'm[I7!t$ΈO8 QAXD.>+cu#XE:rcO~ n(j(+_8d <&eTiQ ~V"d L:I! Ihg@p3ˀ+BH@~ gׁ X)&%^#-^ڃ0TBm°Uz*5r ,@eq×+`'u$ AF~|V>(g Q2P%d DQ4b4e@2b\+"? ,p 0kIM?xl:\a"B@:D`א|%ֲ;R"O}f5㦿І_^H|?gA[WA}S_> |RO.?~o>~ԯ@ 輮pm)0'p+!P#@3p%KQ0=P[Kb+KbC0nmK}pspcP u { P -k!p c + a ϰ k - p P NPzazA  q q 3 1 %Wp  CP -1C= [W@AanuQs}yqyMe0qUgPpG;q) 7cfjkݱq1kaveQGqWP1pR 0R!QFa1#36R#?#7#E2$Kg"g0"Q q![q%)r&O1]Qr'P.rLH)$)Rr)RE &+"ONa+, "i*''u#+1'!%sRJATr`6TasA,@A2/ x*đNVVA 1!&}-g&}U6m Ja4 &a  @Fa9 FAH @؀ * f8Ua'u L rЋ" z z!γ !Ru7o<6'&/R<7r faHAaB';3 s9B@6ASA#A &EEax& &r! O@-q.c1.kH;"Ka$gAf"J,a#tJTJ#332EA NB,%Ӌ-t3i17Q5A6u./#i#QukA#-Q1LDRt&kIaq6'os' Lu%4P}#QsWwUW{Wۑ87+ At@)?26C@YySV?3]QYA)5)[[\rY(?t/P&TP4OOGuZR8`R``` vL R^t^GYI"tUaѕXq"S_PdA\\tG!+e[e_f;itksnap/ProgramData/HTMLHelp/Artwork/ttEdgeFeatureImage.gif0000644000076500000240000007503410534177570023157 0ustar paulystaffGIF89a !!))))!!!!!!!!))1 11!!-!!9BJR))))!)))119999NNNN ))11119919B1))9119199999))B11B11J19B1BB===9BB99NBBBBBJBBRBJ9JJ=?OLJJJNTGZc rJNZ!)JVVRRRZRRZZRRRZN^ZVV^L_v1AJRRRR[!^+b9i@qLwJ{RVWN ZZccckkchckskZbeiss{{)m%{!&r| S*a~J3ޫ{Y]?r\'{r3;r<_v'u`X`XFJaxaeȡ蝈}h+sN8Y5?;+X+WN~c+x8]T!8UqNmŐ] 8pbJvP.`+VyR'i ck9k=[aƒqၺi~!:*_shkR&n\SEH?!$8xx!G8fB8ҁ+U+_\!&]`+] qq@ޙ'_BZɤu+y8B#dF<2%|r,02L6:yq+(gJnxR_ttGl0ʅ)]yjs̻k\̫+xa)xifdE_q'ٮت1eXۆ\a+ᇐ_(kTj0)L҇m`z`Ǯz밻:{.5JhGHAkAkm߽ߏ}Eݣ{?>5fa};_0{~r S(D]QHB/` cP4l ax7 BA75z4:М) ;VOAъ_T˜2NQChPءmL38юa*A P=rc8djMQp"H\J׺x# o ֌/D!Fp-"H('V cXEa9Qd"i:FXG7PtBLPg,@.x{xR@=Fj` * ,J:V]#vOl un,':A[oY(@~e%Hk!lM57[f i[~7:@N/Ql8H 2]~5!U/cH GC>(D,qԅ} _uˆ0o $.VeF[E^$Q>9f!VыyNw o:"YϢDR\uƒMn! qSXBNvn\ʖ%3lpC{k6xe(ܱ"G}9yfAn~ @Ao$(E&f1n!| rxEQa &p=,^ַڿxG x|}Dae\Hٻ:E) l>l эBn>Lnl" X !~LC:p{]zqqe Mp P: P-P: Hp b 9 lgMxmu% ` N%[9+ebg/pR3x)P ZgUxdU pp"P ' ط] MvkuS6Xxq({H{` X:]@ X: cS:wް yW P J) ϐ )p A d7k`AdGx P|d GT [G^z8_[xM  @  ˠ g   0 >e lI"HWY In:'pyݰ} UpcyC 8`6V5ΰ]]8L xW]qVx؏xՃe_v i Yِ_6 q8x h5sJg ]wxH H(Yt 0` Px܅8`ZVyj ʀ ڐ Q R T ِU R΀QYRi zb5 (( nW] WH虨p  + б  zĐ P jmʮrs% `EU0@s zjZ) N t Pp泥X P  -5II` 5 >p@VP EEV4JIpS}5Ph[ lk K8) [>9"aI K)J PDžA : O %@Fgt Yڮ4+WW~Ffxu Hy5 BG 58?߸u:Z 0 9pi7pp>9&tKW]@c{NP8P>L)0bPU;V8Pa  5   ՚p P 8 z YzjW0uuEfjpjXc) 0t z BѧRwH6i)6 ( b#{5 U50)pUK-*0 ;+\8YfZ8P( [@yNs P 9P l np{{ j CpP p /+캥xS  _G KVL4 @ @ǫ. Ї'SxM`8A)U;--0b> /Uǫ / JhZTxy; V ڑ@/X ,iǑPV9JwShD: @ ZQ:Va0zW:  ˠ  c|]V @Np 6 p >> 1NW7T6b/۾\ Ҙnt( ٕ[ڪݐ π} š 7 P ඎ)ʺE͍ԱJRppPZLp3PQ]   ֿ0 > [ Y:u ^{ nU 0 L0o,z 0IC}0΀ --&I |VǸ{0ɐ c>ː l[) rjOİnN fu0n X΀ \p/ = hsK8;&pI њ=Z>@WT S y>5 0[ (hZ ɛ5 a/ ptOY~̐ MWsT \ 9[ E(ЎJPJ&6|bR^>ꥼ. %+nˠ/Ѳ y[`$T?`ꖛ3!⫀۳ի KN JWB^~̀ Xo0%ɍ6&;)V Z5 W H yȎ+ڕZ + .㨮(~Z,JKVOZ^ȟ]i%h֎@V~Ơ 5pdt/]P 3>S53Zt5 GvKb9G~ݬJl pǍ_$TЦL5*"I!CN0QWJb$"I&C/pGF^HCԬB)LCD  *#R,Q3L6ҮU[ԲQl_kRTgko/]|fLЂsBDLNH]c [L`}tF@(*%"M).V!p RHb#G`#+`*bSueZE G1ȉL[Zl ,ƘQ21^fYEff%HxYE'cQD[fĘYDEKr\ B`RVy 6{qY6Etb *٧aM|IDIC2QlZgZݺyiABuG1g򜮺DA']ɦnuƘ&h#B dVjJQpQ*qƞҴaD IB^P1&Q&b pHB®(U$L@*IVF*$FP2dėEUp|0\"Va4eY4yM"8e^p4Mǟ~bǙR8Fx Xba^vE (@As9G|Gd|j99uz`Hyn EA(ER) bACB'bK`EBlB,! B_VPJxQlX`ɗZBeYpbokb6q]Ewtme3D^ t'RP~YWYFe4e{8\7| QYg:Hd?ىưogqIGpRXZ ᥒ!*hPJZP$A2IJ 87E,aKoc#ZlLI؄Df\j;3&Zn'*0U7Ja1 YVyRzxEGĪh:g({2) 8@NNcP@;>XFCaRGp26Q^JRؖS\JUtR{@4~) XP$TEXld# xA 4"A- |;/AL,Q` hVJ$NLq@JlB,baZԞVej]kS*\^wiۘ'mx!$q3YԮ ; x' 4̚I]%^mq >RC I/! A{Ŏ`aemU^,pP(0Q1т sb$?3*xI?")/ld9Vbsd.fq_\[X/62G[L-ip  S|<D3RpO1FC)Fh![F6JRiࣈUh1U$Y1/y bi2`fe\ MdR(HAP .ҏ" F EEDF1Y`53QRމ 9KЄf:!Z$2aZ"_P$!E) = BYҴBb\ D Xu0l0 Vт} ׍ ZU*0ATq(b6 Q {O (bB4(! qba aCJWĴ QHW(lQhpZ7gSJ!zGAQHd]{NvIb!|lQ&2Qʆ]8ֱfn@!JXPkk w1$RU:4-+xU =E0p5z?W6`>a[4[o>-l!WƷui'D} 8ci)9HD, y[@ $6ʷkXY0?ÊJشYg08-:R7hsx" sǺFЅIINL0czy}}pB(_xz,B{h{І+ly,hz)Gh'/XS1@H)ZZ(2FXw.(C@-&зQY (\@HȎ`+̠ҫzHcט$!AKtu p+pZ-) yg`'XF M@/(2jmX1{{0Z(-F2> `XЄvڎ|!*(Qzn( Pv ;[LHu)\nJ0:F@nXȊAXp&0;("ayXYupgPO 0H8~Cp;FQHn :&('h $<%jȄYn,B{_U؇LhQ`NNzB$CktBKq!Q ρCm(*Dt0=c((+(1ƨ?,XIz2H@qL P+#Z Yguү_`_8FiupE'`򱇞Hg@$QtPcx@L%p'`F廐hC)*l0o쐭m܇(y1BЄ :IѐгEJ(3 $腆^0!3$K;F'5sD Ж3Gr]2ȬM051gX 2HI# p?#Rx_Ȇg] %N%LpB0ƷTr+|K\On>XM@KRLӀ" RCLuS0KŐ}P* @PY 1RI #Z1Hq¯*s- p[G،N#R=1, Bπ,C05f@yVmV`a%Vd]yxg%LxuӶJ3{L{ޓF`ǛU(LH2;)BYYYYo> U=;ڨ1RS%`, HMyULAUIxq!تy`OQMuPZĊ): }xd5@Yq:hLGnOѠ oEmHMS]mׅM![0X!; щtpN誀3CXXeH)SQx(\'#@YjآAh'w%g؄HR^d5i$Rxܜ ^YRC%Pɸ%xBɬqO(t0]op~aӵaa(F`^zH J!ڔ6?BPu`ĈhHO8ZЄ`Q`{`ExW"eHq/.dZȱפ.؈X`FhI G+"#x)XaneWV᮫XjGX4=&eH0Lb0 7|_X(|ap|_o]XHCZXgP~pЬIrMHYãP' BL(@w=z%vulGN ( I $DYߑ; E@ScceE3cT!Ĉ͑"*߿~hP*IJnU CDM @d=c5 z`{\=~M q¨qHN2iӬXᣐE9ҋ >49{S-sf۬J,HF T# \I9)[iU,E>^Q"Q>*@0mI&^T*#R,Ms,X`„n8ǗضUZU 92&&@XpMLy%CTX8^(*ċ2Q:PTׯ[%)<K,?,5TL)R]UXiEM'D8C!K&"&H%@Q8pk8Xa23X7LP H 0`" - Q qWe=9J,ܣ*؆nP 0q9'uy'0(4JCV.ԠI?,PQdK*I,„*X"!Q ?w.I5sd!P /PfL,m,!-B 1.ңb2$3#:</q DW/$m)UVCR&̒L7}XVl0H0q3 ? -g,0( QB!(>KrAP&^')ܢUTEMt鄰ZkU9HY/"0DHtbn7- L̶6;'/ (ғAPS𯀡)Je](r$%>4>oq}5 A #.s2x Q8b@( 8 gF8hDLU h) ʠ@IvD6&݉NwKlҗ zs_z]=~ ?40NJ7fq{\D4D2E Sx͸0iѤI@QT 8j\bxE"0.x"GpkP ktYZc`j#Dh$X*bisBFy: c@JpQZŏƢ8DFJ"Xo$;Ny /W18ꕵx,` /ChU4QLkRl:d3B)\XeJ ',A9桛L|5!( BԠ ҉00Ș<ŊzH<;|:Eu^i„͓Ji L! S*BP,@eV|V̝<2$DLCL@DLFQml@Oݭ̢H*H |ExZFJ YA.-A儍 f@`3h"'2''&'jbYu&föĆSPX*V,"hlGeE(Ni@ ɝJ7$PGhFR!V&]DHt!*Bl,CJKt/0dܳ8BZEج@5fRhFJet$h$%@'*CF2hDBbEfCb6(EC2[ YT,c29F&-hF8\BF& .؃dnfgL $h˔1Svph~i]Rq:LWi ӱ* ǰ1B)B-F22*` `-kcWG Eg a>:LIREڹ (w X!q+:[B(T B$BvK#dB"U lKd91$OFDFt.K?ԫҏO} O,&@˾:4LK+(H#*p/$M?b&xFBtD33 r} :TMl(; a/dBF0D!83H1X*t)}p>*\1D]od^r(2D"G.BWʼnꋭe2aôF#jMT @ 1.q23*^FIR0W\*rF.o$ 1nu]d4#t.TAK& (;9P:C4:B-$3h[Og6yq]10;dmE~{w03qԀ#B&XF{EO%åjVў@#hV T3,914UU#xDmI^ kٶmDsmM4N(bDe -č)Ťb-VōI"aDٔmun ;}, AN*Pᤠ)T۬( J@QC Jʱ &z '*i2Bh޼mvvޒC |wy4gʌ Q],uf]*+˘LUҙ&H*B /pL807GE<1DI|EY,LE[8 g| 5F!$H$L 02]!ct)Al"r;xxt4\9M9̳M;T:ij;nPj EE No 4N@tO:'O@-T=/ SJ,OOVmQAE U$/(壯eV*d^4tĕg4OluVp=ko8l]wͶq7m"m7^tዲ>_qWu`eҬJ93kTb>c2WKH%.A /TK2 oۃ-@1/>9f= dZg pkZEky(2 d,K؊ӝ|6U7qsc]G9X(E521 ;ШM$D (xa>⨏`(aj}lfC ʅ ~l-C7r! y[W34gE\Ss qyREʗ.iccO!LpX( E4H, e`঍ϴPɪp2P*99NnSg{z=F=ٞuӜg;;|jDgy(s*ԝ eg9O鳞8*΀ƳBċiG(8 1D"lĉ=f"^DxYbPZTըIERTQ0uhjSJU&Y*QUJUaPE]pAo&7s a6^|aծy^Wկv]` ְX5 X>ֱd){Xm8+Dr* 4o}Hh W< FZtK$6uD<R1TMPLS""1 d6š3m#DUpBU6  P"mla;^FBy3 i>"KD+w XV)pmp.Īlj`/D Ȅ&&t:P2j +p:r~<^93F {,AA Rx.n, =ݰXˍ c$#) ;'^[.&pLT6`z]D%JNX¬R,dB_:xO3]-YR`9a LVoo2A p~KpX[=0AGOO0a>"L FM A(8f$p*nfAAN6B poDb^: I e"OB.]PqΜ6-m>xPPB˜蒠؊j`֎Ϳ\ a! oxA2Af f ܬP*XZA$Q .ar1AXQpa1AA, tl \|$-(R ^@fa܁!*" p]o @^t:PatZh1 [/ ]ʯ!|!̊ !$.hP% a"n nG!0 D@ 4ab7`Bf"*PT ׁJ! !ڤm*pAVh(TNXR AP<b$j@uM}p`~8PBXAVrJqYkROD&jp@H$B5a1MFCjį*9m @Ho"a*6ՈN` !$pWщЈPX!H=[ϞQ&phbSfs82a62( rq!o  26]n)7zN 33 $/'A=sGwTrGyHuT'+ - MbְO C-k rD.rc++|3*aV$FV8zoDr'.LTX\TMF>x!jaQrAQURQQ'E풴mr _$x!O\/N Ag! B[O(6R7x7 7jt aR5NT<5XthɴՆ2жqQM@mBC 6!UQ!8wr z3_ra_ySھѺ/SKG#2sZϊW ylf8HSIyFAזm Z.pjEm"@8QxK jנVU2AD@U.%$6FYn{šAYLDPSTMEA*!+2ːUp/NU,@*@"7(` `4Rjq4 W5*8 Րtr WM S ŴceJnɑ4\O^С^paoZAaz*@asUra7*pA|`+5 aVAAPLP*NvAfgQ\SMT^/Q ݯ Ag UUCx9'ݡXioR6~AD$+ r ~)*+M* FjX81CLa\VOAJ~At!gPءtr(` A!a`(AA*j4cx!XA * ZP@`W$=cYAwX?Ut n`b Z`r 4AĘAXx{A(؁tr`&4X46u@! 4s6a !ء9DY9z!a PS}QѢ袩Q}0qnցO5T*Mvq!-xa JAfԯA UdzZ.AVYx,Yr]Oȏyг.PB>-a%Q!2/!>*p'UKg2bN6 LD.AV**VJt-qf!Cp&2GnaĚ(s1 63&SRplywZLMSD"D`lN䌳5T+  Asat4j!X[o%oxe[YP3͚^>4 Ns9p7op.7nA :ר+=q;wS|`U+R!+A F7P𻇣`[x!=Gʅs>lL6pmbAy6o_Vᩝ!Q* :aB[.ϛM,yxkta@bAO gڃK*[M?ZA[I==;90A|w Y4W_ p@anLBW*A4tor __,"5 ծ$@2q|6 ( [o̕7rk+SA9 f]ybґ|t.c:gLҖ醨aLv$;pa)` b!*BTx4MgRZסh:| 3C 8|Uʑ D(Wɍd b5)GFt0ӦYx$1MpHҨ&C(3̚Բ5k3Qv*牍/^̰)+EgǹmUSS z8 P!Ae"^dL)سN7ά](RH&س;Ģ 5HT=& @" h q#&tKFT22t(UR /8(T›.$M6ᔉ-,:ԵW_.1BD$"H&(",h% / /*Ȗ 58",,(5Iq!`#8b /$e:5ɳBf!c.aLRFUV<*&D!2?y!-C`7R!JtR   hPD9IPʮQ, Ē3*pO^g1J+,ԒҳN1/H7 ,P S΋'zd{joa%Aԗ&8ˀ$>t+?;C3TI"dbP(>P&&,A0u#:("H!S2_ *,.)KZRAtQeN`sRʄsJs+"3DII,̣=ӏ?H:f*E4* "zTII(eHFp¡7~c@GTeVZn"&p"1(FypFV1JB8-% f$ERb)䈟$e *:~ch2x'hB#\"@(` P3ƨ2X/{32aM@/!8N8N*223U"ҘִU$iD#"aX$# m Oj!; n>H%@|u GxYE0q1jVRǂN2FQ#ZV|bX '#/*!P0D)Zupe0֬"%d# ܸ`>URPUS\/gQUQx3 ^8B%*v#DY( PW)xFPrm-[Q9 0 ܉yD%WiQOt3!Y(v0D T(,2S@S,\L(1TG{LHKEJPAY;uғ\:np %+U +@:]< HC@~&곉UQE⏈mtF ^ýHB R@8/H^Y[tb:YX'"hmEW gdA ( d32A d8"Fbb$C'h+Zb8.piX\R  x @0 ]"%TtFbѱ@U,\&P08D"ZBć2X/ֆfhp6nHwP T)Z;qR dHzҔ42bKkZ%n)u,T A(2t?$O$< ^B.ȯ Ty>:DQ"hs((K"@&"Vb +H|i^ `$qHZ @1:h+\+q13N# P x  /L+yK% Z$5vaBItfP!AteQG! hmc@81Z̊{"O\$zIbH _Np@*q xy\KeS3D ,dN:(S80 9F 0$$>)tv:J3ːN(` DtEb)u5N&iovi4o " gp[Q~jυV].r%]ɐ q_mPyْ/^-@8gK:PP .P"f `60=J#N`Lg3{ c0848iJ3o`iPccIfJR(;*/ m`+Q u$ NJRX/Ē`b .-#gϘ,8KP#RDbP  `de P : P aUS/RP` @+ GRQ@bJ<#Mz3hB-q{3ၻ'Re& yaWIUFUU>1>3(fxi>$*9"b: l/0e./X}& 508! 6 L2"@3BT]8r`qW NӠ i9̠0y N ƵJ8a  *ĂeBt$0E k4#OX000EX P ,ST P TiT?gs.# YyI ДyVCf)P*_7wuzcF a +/E'> -`"iQt+)zy#G0eE @ !Ka/ A BjGIC*OU1T/)p"3Y(T Nm)_IP (w @ !) R KDL5 ׉b  ϰ>̆Hh5`DʩLZZJGZN u]T {HR"9B021r!T!0ū0ϩ!c0P jM" 9w(-3 )ap#Bj 芮麩?*JwSxVOZ/K0pW/HR Lw!@MpT1!S"ӫ|zz35?`+HA V ފHp ` p? 8 7{zNjT`EOر %%а+YtP,Ϻ2F L!SM> @fJU`Vdsгaj Zz p ` ` ۸{[F:97U) _rT5@Kt8n!ϊM *S:Ь)_;VdBXlbAGHߐ K { ]@ p˸߀Sy3У(Wjt2ֵEú00F1v%!p_YW9,t{*J/M$?*0 P @5p p T Ơ K].t* aQplz8O!r8J2Ц/` YW1`"%Q{rt` ele| gh|mn iL/,?B ǣ ;Ňpȅ,"W C_[MWLqYm([мcձʨ|ʭʯ5 WC(: ^Ơ h -PM+a1l Ё{N2UbMW0c 9` #AUYU ,Ͼ iJӬe+Q#D42 kֻܰ !/۵vOu{- 8haV\M[U@XU?cs,qTHg"k(K}(V !Fܪeʦ0lJ7!PFO=mZEIR,1$ `-BPBR&-w6=8j;D^(rT1_ Pn- HL"P>\M"*QЪm{1Ш5Bf(R bLϺ  yAjB. ^ %Vr%M@Aϥ*3i]i+"y+'_Q#ؙ ( lI."l[к<-*PXK 0 e,+ oU,Gg%q]0 )tt,UOR[u ,TYXw'Fb,dݮqFFOP >0 LY 8!жDK tcp ΐ"{,5 ް25H5C@=ˬ&SJPf^gJtU !ُ1K K(%R(](Ny 0ODnX[tW? 兄[X՞?[;ALڹ>erTD&-e) ?^r8 D TRE  PD{s= ! !2H.* 0OSS e4J3 Tk jUw0uTT^}ςtk+Q LPK4&D >;M!8pK*>Td?1 P ctKI R W2KC< ؚ[;dEKO)vY@S1zӬTTGC"->#P~0u!->"_; &AE0 OVb%B aZ0 KGDf0!.@Qjz*ݪC4(B0-L.*AUՁ0DAa$A!_͝;4 ݨϹ{ 1 xPR+E$ F̶FQÇ T‚ 0<ĉ pՋ׬Ulٳu9sXU2UiӪY )#R,M3L3lM# QD&M)3lC*mT:[~VXe͞E5x#8 Ϟnbq/QJUI0xp C ဠ`SD8UeKXi-꜠NeK*i͟G^=HZg pRxcE2q HE qĐDDRO4$N2ƶA'>6bi7 _gU )z 81L8iD2Lj)$LDGt)Ҫ3̴ڛ'g֙eA Mqgu~e^lM\2)*!P')QVq'P&c1tq%cŘfgF/Hq,9)e%XNQVX8V; o>0zGjjl!*~{2[ FX;|GeQE_Dnq-r\rg2WB xeK="7X1Fg̣n*c32_2#NCJŲfq(W '@uIB!.Ԃ@ ()E"(GdbH jP14 8@*%$lIZ,c EhBmH>2úP>6HƌpSZţ!Q4QŰ,ɋ,e(Ri'|S #@ M& %/6$&., #T8ę@FtECTI"1IJןf%!rUP*l X˝d''Qӟg]JrrYY`g8&r@/A$ AtG@s P^Pd31QG;X"Up(θebq Ջ3&zB=(C/"L"JIjTX QHժWZPLD(CBR-W10A2ц'@XE%;^d$BKb+Y! țql7N$|Yoă/`qZֶ6qbWe;&"e|p$HPE&6'!pZbB5JЄ|7+K>1IhPP0ΰ.E;B*q>ҚM-,L{g_ۥ'bHڐ|!RrG!xW?PBmfCL}lƋv`І/xE<G'xw|E/M?ӽuVP엸.{d\b%r VH٭|d[o.! pc }o_,7a}o=ѿ~{!ϟ{ȟ~;??l@,J5K8d㽏cV@d@CPh{VcLAVcӆ\ȺC#q $U+\ȅVYh`#V0""z85jІ҆fe,ejPjnІ+3e+M؄VIhThM@T@#l6[@ ABCDCc\C9D:L==;D_XS*o5E6` oUdEUWtTU$g 9x $+Cd oTkkT>"FT>x"ef0*5c3m$04iPw?kC&\\@T$ Dk-jVFVjC+=@|niІif‚,EK`T0t- MU dC8|KlʫJlcLLƩTgK<*{@;DH|F"L\V#4$D0\NCl:V`l8?d-nh,BdTVKcf0mLcB HCLD$Ϳ4=l5eUPm;LlI'PXeuQ' Jx !%"5U#U%e&uR$=#8)*R),+)/0R0R,S/E0US2=5E2}3e9:6e:SS8T=TA4S7%;itksnap/ProgramData/HTMLHelp/Artwork/ttEdgeFilterBox.gif0000644000076500000240000002675210534177570022522 0ustar paulystaffGIF89a -/-%0FHHCRRJZZJGTRR^\ZZR^^VgcV.ZkkZsogccome3 = J)Rhyw{ssLdp{{w{x~)1s{{sx{{õů˽ʵƽƽ3BBJJJ JJRRRRRJJJW ZR XggckZck ]gkssRcfsw{!!)e)h?i4i1s,zA]}vtڠۭ֟ڵֵ޸޽)!!))139?9B1199J#LJ!O&T1Z9^-e?BBJJRR^^mOsbs_ossss{{씃ޭڱɵƽƽŽֽ޽,@$0aȌYȰÇ#JHQ3jȱGń)F0[0 *W)s͛8sɳϟ@ H*MtӦPJڴWܚ%ɄC@Rʕhњl+o㺝 ܺx7o߽~Y+k_qNjK82ɖ3]\EHARA@ H%K,ϰc˞M۸sͻo(UÇ>w7УKG$ ^^iߎJ &P0 ^j(㋕뿿?gxPqqal@k!sB' 'T @L ylb%:c@(DYF&H$|NF OV)T^e\6e_ bI_*8Bo\xjK2b''H $T"@ L26Ar.@ѨDg\ bPruQP1p {qt#sPB N{衆~'s"Fb8k\xg`\;T"PG6,H@Bt@͂HSrhap8z@sA`ג0Pyu u=T RX[ۤ/i.%LP E)@!Wh(n^"(*febר6Jot#F7 N ƥaR$CHDz@E@\A?5I W`ql`',P \0 Ap 0L Yx0 as;* \fnPPi>3\&5Ylr6IqR rXC1ya d؂03 (QЇ6TEP} ]` P-\!P `,"MP4<|Y)m ~i9Xtj : 6ͤ3p HEjQT*թL}Tժ&Y(լr5^VU5`}NP䴁 \BJ8a GX>QO"ED) ! 4Vh @* _dIX Zh s/Xa PЂh@YA Qt bh(Z+DUpЙVVmKqk\* =mtjAץu+R7nڬa bBrz4L|P5/%؈E A@BD^ +|WւP!D s*^X pq?̅ 7E1[bѶ8};c Ә11q{X?np9\G>,0 mp(A LP. PDÌ2+E$2EŤLfxγ*V>πMAkU7E'ѐ3ڐJ[oY]$21`a ot' CHuy%D) + kYzֳuwm:^׷6mkbȞmlf't5 jS{ƶmno{vMqܺh@va=(%` 8\ -~{wo <O N7 WKG!g| =.C<7   F0Q C3'c><5zu><>9us CosS=XziuۼCӵ/V.][g؛;{}^ DlX3wOx#~'|??W|o G1ygKOzɣ~@4jBښ**gZkjoڀq: ڨymPv騜JJlڀʧ9Iک :*:IJzz٪HH =:`yZCɠK J:IiZ髼ʀq*zق ڧhzڭ׊ٮۚHڬ z Yc*j0jZht[QJ k Z J@J պ; ˲L겍jʢC 8 *jZZu;JCiP+Rش0 @Y:FIPi*>Z Z*1{j{@K zkGʬ^wKZ6z1+گJpD C*ꃕ {˹Bʷ۵[ ; dkj9*Uz-+(j K {TkٺC ȫK`FYջ۝}4;HYzVK`[pJys ˽G) KU[K ̛<=l @ܛ+˔ DʾSK Ii »ܸM :̚Zv;N` G̗< ; :0 9Z Ó3lP P@zL Ȃ< ƴkX_ Ȑɒ ɼ @ ɤ,ɡ, ʪʬʮʰʲ˴<˶\˸|˺˼  pȬ|ͼ<Ӝp˜ܼ <\| yt؋ ,=]M }  иKÈ^Lez8&](M*}+)0245=687:<IF}HE @7`  9`S 2p7`1*`ee}21 2js} I* p`px |x =7 2P` 9 p]76p כ   ٦`P۰ 2 }6602M 0֩؁p Qm  pp RLmpM0р}p +P1 2`ppެ=50p+120    `l+pX * 50I(*p *I>@1@PȽ*5paV M5mȇРgx5י hd~\.n~-m׭ne2R +V 60^2ܓ* -nb;~ 0  < ,7p1}ǐju|W~̱8}̨50`0+P`2@ڛ00-а0  ==L,PE 9 1  pOo R @R 1 RU  pUNXS}P_ؽ PU? vg0 ѐ  [v + 5@ǀ V]YC6P0`E̯/K?__؟KOx?/?ꏃ3?O( X HxxwA FU84!E@1!E$Yɓ]ЄM0z$| 72ttpco 證Hp^\A=x0CwNb!AhTܺ k6dbk,RE,{`UVXl9[3 {dc*/@0¦Xuk@Y e2=eŭ j4,=emq|-@ott5<pcw`wxa( Ys/Yoe˗1GFSwbH`x%0贲J3H걁{np fx\I@w* 'yK{Fcd̄RLS%HYNUJzbp R] P%)4hbxGn(a3pJ{dpX"ᢕ!Vwl87`0ډ!2Uw]2v㝯@V8{RdX8ST$qg^via%a/vxc/8!XWcKYd5>9exgܥrwH6h覣zjjx Uxv>8ϣw waߖkn[zg~[np '7\q FgnHc HUl1lw`ũ|Yq['bWk_qmvU<pǠzR Vb!Lqǩmxl5+l~Ǟw ڶ!rfkܾ\x!ō}` x0@*0 t 6qؠGr T[p57ъ`ȅ ^|+E0nыc<< r(ph 89"@x+ȀBB828C^հtOZ0~6<<;PL2Ge@#hlVZv*+zrQ=^9Zd9 0X~`[ۨ(@]oV0ޤ8)s3 I hX`圚D-DbAhD;8?ԲJf03R$md[4ivd71`vTL:*"KIҖM4%%#QfLCY@EPcĤ7Tq\Mq*5R$uJƾ 15l *[TՁ@}+U,NWN08Ϩa kΠaqK=,e{flf-b%f L+?x mmk{-m};[򖶽.qe+W=np[՘nu{ݝ=}nw%%oyH֚WP^wg|{_u/U@s $_(Uc_SD#A=3VUU8s<^Z{1cp 0!K|E-N"W0bFXL|!~ `(RF_:F#LjA2 OL!;3Ve 0<p 0#VPq0 iv [)G.`XL HǦۜB %  QBGF5oz1w6@K2V M\̄i@ Tɽt$@sbN#sG4<ns B1Gb0 a dw(7i84^8DAwyLe l =U)p?⃵=qg< ׸MN/1zWAME#Z͇V;9Ԋ6=9чs]3O:ӝt7]k{e9BLNKi'w.kW]ʸk=i{ #$m3Z!ט{jOvݥinY-x%qI,"O/nj[;mMMRTΣbR[@U=*S)3 #@C_P-$ʊ쀆j8Z? T9,)25S%C;DT67?QC$(y?,÷c?B}C"%@RRYU0 G$dl@ 8(I@O@VF?pV<`? ` `6 P`NF``d]@Faa<HHbhb@Hb*ƀ+%byH#a 6Nc!^c4u[g^0c;c=c>c?1F%c<6>d<>d(x{؁BNdK>cDH)jCeց8HRneWa"&cF&hWa eh0cYe0TTe^vcO}3`(Yf a ha@Tg6h]wabfa@HdJaha @b%'x3V愰o46f hlq"ahXhXi$< !䃨b@iXbhpgwuVhdngvꉮn8FgifA6itFb0`nk{0JF>n=`a|蟾akFkfnu`\rd qց/m `Xg mti^iS^svĆcĕe<(n6n6hhb0>&i2h~>ndJVX4*1MdG_?!yv+<߶Mmb:t\U4mJZ6 i-fb&$)մgtmfc`ji# D%"(TcG8(бhav6UOtu!S_3"Xp8G% {YM7%TJ2\gdu{!ӊ)hq#t衉(lZn$jhi&\La$ gHAX0(!FV^jpٝSem_cnٞ(X|eZjiae٘ci{ :k!`{)cL4x Šm!(ꪨV'9'S[U+{1ÊiRX6ߝ ]3h m§b2%F3^ \I˖]qx93R?86لI[!`AS\j w|XxX%+{]UeSVԨZ0Z  <.g˚R<䐆^?Yj _[gI%Q +]"[G]3C)bh H*kt9yZ`X+dW2BCZj= ntpيeݕJՠӎyU|;@9(4qvEFZ}'*9`QLt"8 cԝ.k&ȿ8:Ԙ5 kXC`bM=B]Z4TjiA+fZC4XR(TTVt TwX 4zM1<"NZU鞣.AR7S #1\t@='z&0`;UjuX3'4L `L1/Yn&nؿzΏJ)M / ЏH?HV>ͧP0/55m/ђ,DԦA 9b)aGJ1TD$6IR:K,%/p_'Qll].9yqP(}9HM. kHMfN!}0$&k紧JqR2TdTVG[ +*}3bCCl)N0 V۞mpOSOV%g8x2c}:Q,ѡBBrUvHwM ЄZoSMv u(\& DS,T[;:ZZ` W^:Ђ0<:XsAum8$8*[F7/ɺCNkաnj?kYIy 2PQkkfE㸦%ѳ:z0-HM!Dڝ(o?2#wJ~-?ptN ? -;qa؁@-^+PG87zpDzoQ0@.#@CbJ;!5q`;,OsA}Fc P:91< MoӒ$?Piɇ;HDYE'q s'E (1`G9.XNAhɣq-cMei6ӮF5ƳLTW 2OojB1]v%`Q Z<8M؏="3!~* #DNjZ&VE0Z6%i,;CLS}F؅9ۚEXD,x Z"m J.q VJh @{ڗFFX xFe)TSJRR{L&u|N64Z3^N7 `kO.vCuC(!1>p UF.`p   XH@#2`BR &R87&} G/{1':8` jS$ ?b`!R+r6Dz7$@0 ;w d%7B`/]RZ }&i1S$! )!ks/ur_) %ȐdvЖ#L`y$} 0  xctM/@apVzp(u!!ĢՀ+*#/RWG `&Xd(3mɆJҩP oK jC%K={~1#ݥ}ei2s ]e1hBNw1/+b Ϧs0J#{nGJM7  z6$KV5WQs8#7^tq3{f~(() eIw( PPD`lHh@ (A)ъS3C?mXIVgv-pI\E5"I6DGC" ꡲh@WӉ"v6(y_~p ohcWw`FVGp,b".,s0 ;v ! @ *N`ng}K`OӜ˒( @ sH[t'BI4`UU[- Z0(D;d(5ONN2 x0J;7`BMc+D+@ =' K)`gTDx[Upү2aeQ\v!?#E #}ӠSxȲ#돭8K04裤Wg y)s%z5ZSX eihP^Hz$+'TO1/uRa/ق(YBED4 qQg ]JOTx,CzP ff{Y*B#vn<WlPl`?MdN4ďD$\ HjŞHGg .2 k'&E|?+B7}[dv/Q5l?, +}phɷŦ o b윜.AU|dA:nI\ˣ> !ڲB4Zsbs~ { Ox˼D8 <̴ ny)8Gɪ==,Ph)Py417ȺDgT<)n3& ޠ ;& {;ry'$}`tݡIYT g<}Բ"PT|s>e0?cpCuR0` ]Ļ`Q&E [^K"\4), =墘ˆ@9+4W A@AagYų0t>@kK4W奄Ut䀧|J꺋h nh$?|K`R zp1GY-B۷cRh{tWe,.5 f )g> ~Xn )  gRoAwp=m&էцu!f:^OxA6TL?#oT6mDYM9qzpgμVLQ9pi5t蠱>XBQ[|_>fuԨGhժ5B *(^JP"FcBPa BʢɎ'd@$K2$ڕ+)<{l(uDpB(t*icJV<]fՕ4I%W'CU.X>kV-[f9rdBf5E!Ĉ4HߠE bbjZND9,,0n7 kپ!ʨ )$7dJ87-D]N+ɟ@]X%; :E=TA~e %NAd/ByNŽBB-M;(0TdOZ PC+0.mS(nCڈX,c;0*)2d1ete8.RY0LHl!̼^Syz?OmIjfЫ.4V:C['a JCW㬲 -*>$sOZֱLk:Y.Xdvz)dJ6adEO|N]difXkCQ9.]$4%b6G$aΎ)g?OBLAm#.t})<>}6Лfc! 0E 8cXD|`K[F`Rف~-8qbhacBx$03EV&r(jte(<$& iSFCpܡcdA" %!&*;h, 3D<"mςDT:e [,B/V2OHƫ5yQe`(ʟ!'EZ`؄'JTI#b6-U5L4,Pv0)Yв1  P ?07$T2@5h Q\F+6AY@E#,! P%2JYJ3fQDGXa :X` r^}מ&hcBG^$SN2 %㇚pEoQ/j܄HFĤCp'.\A m`Ħd"$6ǨȭDOD$.%A0'-@A,UdWL hJ p#PL$Z7flC0yI)ۄ(Ce\6(;'8A-C[&)/m:VgMJ#U K>ԢԝD`v%(7/p[S1u.l2VxF|G+(bFA 2a*fBU@BՐ qĠL VRn!ӃI消 6K6>V8$BXJmO,bPĐse%X_Ȱb˜}.|:'602CU9t!U:$,@om,XZPQ, !-pŪOoNQ]sEa 2ץsE1#2% TV"|&W 8a)MWzI;ObZ!$8>Q8(&a2<,1u$U(<А>T ]\[* 筼k|<ĠM,/3[dgIw][("0C'j41OPtF7o(Hhnv&X:=ӄ(ix]j?,R=#hQ]Z"E'Y01BqFC4,V"YhF~g+;^SkTJcPq=w  /V#A0(H^( Ze19:Uak Y]TS ^2VXH2J%ЇB:y(:H :XMj/+ <0}X%` 0@cx?Xx`\G'83p!"U Dzʚ<\"42&CX4$ /38d?|?8h _6k;58({ @aaIO|B>ЫvH@ɹ|$ xxDzkY@xxC0v#\A@+S! ML A:Da.O.Ф T-$B-xP#M%KCpxA :%Z4[ # ,S'ah-ÔrN`j{K *Z `F`tQ#*b.L$ : \N8@c))p .XKxӫoŌS`癅X( Xh+ؔ͡ DԳ6C(3߁Ny[0l@+`'0]< ϽKE\LY 3D,E$e& > G4Hy$& , {0$Ӕ*A   P$ bX` UEk J0DB$cLS8W8Q0VVQȄLP?<Ȅdxz0s؇Wq`u}x~0pWP>'DXPXPąPPpPFA0??L9XEB7PWWPxP?hlEsqq8s~ WmUWiWhWPpixta^#Q OЃ:`܉R10iS1  D`_8AyIHN MEG[ꊅFBAx(`Jt J%#\ gX[Hil=C "1i/Q&@U8AX@хYUTK *Dd<: %)L$D9PTh&:A13!+Ԃ'p`EN` KVЃ#EX 5/:hz"J^D1P" 9hΐܨs*?#SܹѾ@ ɹA4%TU EƪXPBxG\^11>E "e)=QL|1<LRI޵=hO YJT€>]8ȜC]UQ!Hm181FIͩcQ$wJ+>Zt,rˀxNBE Ɖ˵@&[{6ɥAh8d.^$y!  2PQ#'@Snf Dlpⴟpb}3hV 6I`Gd7D (x'0I jMt  l*yEXH) k#:H-S X^١nox\ @D: -:3iP(0p8N1F3`@π&#+` %@Ex4OO+lH#҇ &$'SInߒdjTa ~OdSj 1$=Eo70APM y*LE K=ҤP݁|B:ⶊ`T5Mr[xG ji{D G!iC`>#N/򫜫)|}ӵ$J\Hr!e-&0r*A@\~b"M@^Kh`,s.3;CXO6ArLMj~sXF;؆xGh NT:"1NhvUK4/{a F *K$u.':$ KµgVlV`^g`C*Gʊ 4B9 m0fz=۠ PpF1;VSktrH=T!]b#F,p(E9RlYM5}HJȹj@$'F@6a[PuNQc*wx}\2xDP9 6`uEA>|"n*)ar2N)x]\XXpHSJ˨y XJoONH(oGsOj䅐8gNKX=z5@!88t.X`M@X m hir.ɧwZj>P&HQ1]!s̴GթD<؆y?p/p`C4Vd-^ yբ(QA$@@OmyBG|`A$|RɔdAРA=Ő ')._~k]m4&} ֬ZrՃ:cVożm}Tk\MbJ: *DZ `'Qcƒ!G6I(v<9 $*tQ,X 9H*}!umŭns-ذ:{-ڹ>bEw-uԡex=U>XPRN=< υl5bdh !&l6h'&i,Iч'$s&v#%PE峏>U"RH5rO5XEGYV9ZtЁh(} ȉF0 /Ԣ["LCFT֞qZAwZDyfZ 2ʝ0 2""-!@E*bS2v;3av̵_V\hgXa6R$4%-DZQ.P 2|iҝx~ YgB 9lc^JPDN"@HE b>ζC>9"q*YQ֏hs6aǶwA +0@4#E'. aKUҞ))da&](XZ.\F ( ,ݱOSŸSPiSM ܎<nu?TޜzZDӊ)#%RH IRU+ 3zh-(Xqj 8O5 $"mQשjʜ6OmJ1cYWyL&\++P]`"JJӈ KaRUbQ TbEaJ%hr)^th$WR=\L 0Ӈūʎ%{\d QVWbtQWN4 AոƴP .쀆>mt2A#6+A%lJVk)#N( }1+lDqj`Xr Ty ެX` [>iBt0E28dT\{> w0E_(d*xc^Cr^,cVnG* &갆QFop Vo Ep.kІ'@J$y)Xa4ĩ%h8S5qA;E:tU#K67+gvA`@X_<a%-b`T8-|p $,!BLcM Be OTbs,_>`o#%DL DaPQ QȈwOPpjJF0Vg,# pJ3Oc;VOYO@ ĤЅVvh ^4!@]vRmWAe(S*J3rTV0pO'2 R؏jvÅ>XGSАQŵrb[Gq|WY@+rA e#HgPlJQ^R ^)%y@41Bat0%#4TbӐ3OA}HvŀՊş:[Okc*5 ZJbDYMT=" IK9xMu`*t2II…L Z%N+uk.R* l7K`zg% mt i\qھHa VXrQ 푹 ǺP?+i[Bƥ \NH|,*"/|4[jCz3l O"Www8{*MlTS\,EIK!P 8;==ZXZ ^TMU`MxS`Ցh^vWImB1N!2Dg`FϴI7X,A՚uQԓE(l`7EAzm&d@L+x(BńrH*tyH\!$FMݟPu SLi T C1XBeIIT6(Xw5bkA^RTɎ0!N`=FF=%3aZ($FWx$rn!#dh!WT2ZDgz]ĊH@NمD+iNP\Wktє4f(*\`֮\'AyLVAF jT@.鍥A> w_jNE!&쁒@Eм&h_hۚfĊkdƅwܘ@e i1EI7ěUoT6գhpQXf&B:P^C*}%F5nҧ((|UBI- ^}jVvPH"+#m'\Fȥ @8"x~%!lC+^W}nydօفL \{'`rg%b?vi*U^4*u=IE+`춼̂bHYB1IKgGI]Ц5]g|,IUK QL|dJ;{\)IaG:C %N%R' @hLgzV5ЖD;%fb^4& ȁVxirzh}>d{T%dؔBHVzɝmjNA4wtaCVTX% mn\]rhbڀ<%*FAL(TK&d)A\FW6T$Qޞ.^|}\ H mcy&ϼ.M& AJ/I \IZ^Qȁ+`/A9:!ihJ1Z Mƶp@] 'UZZvLI Mg0 Ɖ-xfR&\&n!}(J2ð~dV|S2`d<ŷMyDAT&Im%R ({`> d Sl$jAa]MMAkTf9LnA5(\.{@{Ia%U$s ^К1,j 6Ѝ!e{iT8bB)؁%dHX^Ah&bANrIf%"nE6 eVuCO0{kej\$$טo((-65|P \ZBtBmش~߳G!i0A=Uq&D&$zW+J5QZ6#t\K\M5jC6݂U'a#ļ2O#N?>KO N5J]ףqTKk^J6\̧'|GoH\}FIjs+勚Ԛ~~ ,AViZ?YJ1+fzQTRRJ%4JRTWk\% ؈uٵ?>KdH5(sBJN_OBk'(qbj5}6]T-p0sKB42\\Fo׫m%MnJl3]5]L_QE=v;$C+lB10<ɭԗ^to&΂LQw>?#fSUueФdF>m!ߍh>!(}ǹwƅte}H5)L4$6S+P]F`$52d_Oz㼳UXEmYef7'Xr!K0ݘ6KP>7ܮ]%Tڈ\i|2:*4 2{8ƪt0??'yHn_ aJgr\˛ynKdB8TR?fcݻ Չ#ZL0׎<ζxFQ'n5X:n'FEdkwpA$05+0zT0Vrk ΐ8|RT=GA$tAj``wO=KˬlFRgT1hLA\%A%0̶Ț+ԤP^E] ^p&5-x fdݖTERgh@Ƥ:B=.5YcuP) /=_5b65 QHzCձ_D,!e&W]Oq K$ZG@Lor5RةD)Q>(B:ڵ5CnKS:v2s[vڪL"-Z>! عюlH5Qم,zڤiۣy7DiG=4UFT-铬kDdoCf4hrˊUKQ{u#1,ķ&X t%vEǎ΢$mHVM؎ : Cls星m 3lN 4 <MJbB*s;KB+L):q@-6++qʩ?ڡQ; \BDjtMDpC~zƞ͂0"Q`9JP]DQKH)K!ЄM*T=:i"} Gm!Q,XOSϊRZ|II;{6t`(ci3t1f4U.1FPϳ(N=U!QXٔ@40N/*%m&*V\}L1XrV-B҃+ =~դ(B/6'f@dEjg@O/Ւnw̸ @̊*>0%Jxބ0F#ML2hH).Bbedeޮ33٠P{e\ Or75 .OϪD3r$;0B 4^6E̽C+\vQ/+-LB+dZ>J sBeU&CJ)V+զЯDF5ߣ(bj-֨D4QXU^Ҡ&8Sr$m^&^15[Y.R4k]!Eeࣸ`4k { 9LcxV4 𛥻z bȞHӳC;oYђ[O$u{Z4a#2.D ƅ iJQu+bVsn<؀17QؒV#Ö߹5θN:L%OƩ r%1E-0ѯKq*AW8S0(pfZb҈tZ`GQǮq]v^1U%Cۤ'E6~{-E'I]%4 bTl0OL"kLpMG G]Vb{\2 Ux(xև7a#n6x-kl4%m++a "*H0ΤEFE i  < HLb遥(ؙb5ȃ!I4XLv]~V):d\AoǫK0F1 nH+x u\j@R֢x)L 1=*:@m$,)F8[; >dAiBҏQyLИe^.!RL`nݍw GP3kVDӄa)\0Ӽ;z8sX*I@"ZgĎQIX.I4 ލ-Eb sЄf JHƲ Qx`x3TFa]rOi%A6=RK ]z3@ emò$a`̢@ u`F=Q q20, 3<4! ,`' R28*QiD;XRj@M >bQ8*|>QBbbCAƣ`;4S $##؊M00%9~ ,pK/z ÿ/ť*Td0 a8"+]؜B08zp(!ʇ/ZjrD;Р∅Ġ,$5 r뺁oz|PZpP6A)EAQr q1!!܀"݀(2f q#;R?#C$GR$K$O$S%W$N%=%c%eRg&i%#!'('''(2)))*(7R*))+R)R+)2,.)R#R,--.--Ӓ(2//S/302/a/-.1,g.S.S(.32-S,W!>4CS4G4I-)s21S-!'3s5252a.cS378S8s8X+qS3ղ'5U:Q3.m:Ss2'3Aa;;;}2xS??.?Sa#'[aNrZA44@@S >3C7T>e:sA.T.AP! PAFIPRjtDE4Ds^| >AT9i=C5CJ4:SDET . @; <@L/a`DaD@s(`J  QS,K94K-)4;Ot 6aE7a@EODt60Tc*)AP]pJ< =91RJ1!<7N U FSZUNAT c3V8=s13a2Y+VbIbB4@We3t=r>1,OfM6_!g{gh.(:#fKViau#*iv$*UVkk6 ;itksnap/ProgramData/HTMLHelp/Artwork/ttEdgeNoLeaks.gif0000644000076500000240000005112210534177570022145 0ustar paulystaffGIF89a!)!!!!!!!!!!))))))))))!)))111!999!BB!B!B!B)J!J)R!V)1199BBJJ9911!)1)11)==%RRRRRR)ZZZZcckk9!!B))1))9))J11111911Bc)c1A695111Jk1k1k9999B9999B99J9B9BBBBBJJJ19JBJJBJJJRRBBRJRRJZZ)ccB11ZR$NFFRJRRRRRZZJJZRZZR99Z??`JJ^RRZAAnRRkMA{ULkkJkkRRcZZZZccZZkccccZZkZskcckcskkkkZZsccs^^ccs5s9{9~ ABBBJOJT&n%Z0n7qBwuu}{SVsssss{k{sswkkFNs{^noȩT`kqv{{{ӵ|ƭƭǵƽʭενֽƵενҽڵֽ޽罽,H*\ȰÇ#JH17o]ܨ#Ǝ7~$ȑK<2J0_tIe͖6sɲg̛>g(bH*]ʴӧPJJիXNeV׮ξ-VٳhӪ]{Ⱦ}ك[oXȲ%X$_dbuڿxȮųw8u!cEYbd+?~ DiϩV-  =V(AP9{SEK9)̨h ouאA}C@)y2e,?~f`A]s)daDIbf&ZP}a$<밳~`r;2gЙP$A 3lStU\v(PT2'` ;L{- 3Xt$#8]CV 8Cʰ2q W^UB CbYޚooFQWP8{`k‡@M0P  E`Lȃ,#&`5d݃O ddj|.%pI( s2mdXBm6H`<Ml&^&M2I opsS wIcJYd=AR{ԗJXƸT1!'sz0ddBQ/G;!dv!f2%""D@P. ǖ*+ѩFz ?t8r|TOW>çzS3hpwpP  O7;<$ Vl Guv)$ 9s` Z݌ SӶ<sz͡qNOI lu= 3#lU0Lm&ZAGͥUviPQpi{8MuS rbaYA6E'pT +)$K~d`t"Mkq7;MY5  i@!iq꜂+q٢ }'9;E"ePGJxN{:wnLuU0dL xF5;n( AA`F˂:GjwlKhq4yDpa5sOa\YyY^4@J0  rHC@!+ U #Y]tЌkXZiXK]|cҰjT<%[*᡺H+CFbp@ p pk|2vJ.c<*Lsp}Kv5yiZЂV?ی<}m"P!̎Ta;yP |W:iSNtErd lk1,,2&0c"ՠ)Bޅ#\q`P P 0(4E!]?/JQ mYIj[E-7U;G\|taTc<6~,d`:o0SF~P 0z&juI b;-.@~@pP]c]tQ6IZnjm~؏ ,&C#/3eb5xkD(RraLag  RY$(rjT"4A"1QGwz'% P%6p b&08Mr3E9IP&W8?iI*S- *!'v0fH ,D3'BfI-w)Iۆə0N@4囋!Th&TxIP,!]IUsahR: $By~9 j'9U f)JyG|:8Pr!Xc}j!)w3'JL될Ą7 ʣyjKS!~Q!d4I@~~. kP7wq0:^h6HvE`=ËSN3x `FQ2] b&iZg'DB;2"jpviN w1/N :`0v0Y3rlOY q+QeS6WrгzU?,з5Ibvv n$I|WQZU$d`tHb>O\}1c+}ֻ?:OoYՀS3sd4m cn@jDK%7b$ cUԅK8QRH3&{3OS7.2460® %"$V7PFHhqP!-B?<&B硄Ig1Db e:aɘڎ'̼)UF>s mjfI;bm~ j%p4at7*pw͛PR`hh?KWUQ k g?"qpel,Е (HPőZB2SD{a)$6 {aPX : ۋq ̫2q59?q%& Z,/4#WCxTzP$;#Q%il}(*8.f#7{Kg2Qm4*& 8#/pi z "y.Z |#} X{*E  6l&֦r"r(,5Z{#qB i *7P$vF+qmi Xk7K+$F[eQgCrʘ:[x 3V_ir7tb=MK3㇗6 #X 01J׸ڰ(\;T6,XJ8? Ⱥ ydƽ;fPkYt'V f6ںxJ-@׏ى =1,0.*$QJmٝq56ӛx!TU]@H Y m`~ p-3 5ډ] n=(}WjA, W 07˓4I]\3y(ddpTke/RZ02m'޻  %f'[ӟH ֵ0 >xI&CYVp@3E;s L#u hU}+Pc0z&М@Jği^¹@ ~40n4@?] +P>{0]R7k78*T?*WNTNgʹ+i pz#6f( q9v%@zaZ&-6QЛeQaU@1L@M7r9\ve#ۦ4f vI`/%d0KQ'JG#K ~p<_$MCG,)K0vМ)sYAE F="' v8إL^ym #gg !O-RCk͠qY O^M;\b Su#q4ȭ: 5~гW4qv_J NrP~vŻ1:ȀO y܅^&9ߡK`;uմ8_t!@aP"GhҵKWYdHAFb͢k]j98  a,:hU>v㷯?~ %ʯ(Р`SQ~VYUjϰ}cTa i~!ĎVȮ]Zuιנ!;)dЮ[w'( RC̵b)xHD`a\. rB@(,턦˜PDu׏>{|kXe.jUlXaksP/́ {}/JMF|S/(b')ASZ1S(!$%@aE2.;Xd"hJB;NэC{f0O21EJc Ȉ.-.v IVViIuع&=@HaDYLkL$SVFתc:;jOf d҅V5XQ㵖M!ܜ1hcSN<VPc[XlD#*NbLNsɟ|Ǟw9dKaJr(4l a1eQ!d6mb)96&jHrh¤%C*+D[*-}yRIЂ^'OxPBɤ曫A& F6)ڑ\f(,3/Ž B;(aH6kL׆ɔ=ȨVlaTxVj9^z(Ƞgׯ"ǷNS9.|CLEq/)BDP(J1C&ZЮ}'8ٞ\^Z"e\j PbjO/u==Ab5frg 1, F?^Q E#8  Xue] ST T( 0Wy:]P ZN.z 8V dHCKc$SbE'@Kl-bBtjM]B` ^&!Pw%C.jHE Pev*O`Upn<>`+PHы^B14ZD0Dl$>&40AxAA__ ЇG\˅O5{AC9)!7 #,\s !=Y/h('b%@ pYA"GFx MjIc+ۀmҕIn ZSNOZځ c@Ժ':Ѭ̢rz?W"X0ZE!A8/` S+a(Y|>_QVl9 5X%*d$ON(ZT":bu&x5XME',E}bDX3҂Lqc횫d!]s-c$eSu.#-F Ů-"heU?]z 0="y& =sV&t#vy][E+%w=0 *(x&GqX%x{>r_Y@pؑPC!9𸘻h ڋƨ;J 0qGH:`@x:hi[Fx#\1+%8+N Cmc3! )*%+3HYKCpMy:'hp=UA _Gpd+s"CNAK(K1iCz؋ =$7:P0;ڪF& /Hk8=[MHaJ : ma:ARErb0 -ȃk&*ș; 8W-Xzx,8Bd{Lر1 2peL DG\ȅ†P=5;LOZQ Qۧj5Z>2$3 {3!Ih'@GAH/-J@F_kĕH hX9ZD.uܠq#V @ kAJ ⸗ǸK EPZئ%cT>L(#ͩRH1Y@GAZG. ɋ !ahի2:=W`WhpdpR??7@5i(y rWrxw(~x|WpQЃ?5xxDY^ȅ ^^PXD p @?8Q@^h#x 7P@E8Q^@^QpE787 cmWs |x~tHpV| sW}^Whqvxe9eY%Y&9K؃5-03~0@S uhZHiK ZIM`A0>v:(10X6Y(]Xk VQCJCUy 8MY<8&i>2 ?͓owɄ5p|(YC'M&ᐟVRMuXXRkmj_"ˣOajY5{+Ϲ [P S LX `s ?#SxAXXPG&ٰMy9d`yBA@`HL@Kr.CPxа }W9YF@6 4R6a g 1y[:d;aO@.!HH`),+|\+C%!6͉8!E ΓrEfqQ$v΋*Ƃܦp|GHBFAPȃ3xH+7]g6EЈ yb&%$f%No0F.!LݬRޤ½WC_LhC@Ѫk3Dd,*Z_aġ#08XDß@ί;.K(d8yjH)iwYc؇rh8)uFXY1%D+!(+Nʃ7SA(^=;e&C U ؠFk(dډ*ks`}8#HB3vj@ nf,\Z;ѦL;Љ PxpMõb%ɺ-eG@80MJMKmlQ;2HXAEgz4WSPh(]%I S$PȢ{&ӘM8Ƨsh(`LK~^rK=('`DIBHC;d`+?nk8Cm& "ɄJ#+$`yߖ’ {_4j˂IڥՓk63 : Lїq}irx`C)&Z8' Ș((G 9!ceJ TgJhj@`L  "DHDHpRx(R⭋guj*֬Z~Wjϰ}ִj3 \sعhgx/TL:(Y`1b4 @ ՔU(+K"$[;t4 yKN)VUvA OO=~S6u)ӵv k7hsc;jB :h(D 28#Y L+S %&qFQ&Ya'pE(x e;ebG IBA<%)c?􃜀5M=5tUQӌuԔY@yEAFxTDEag׸G=UA@ˆ&8NM@K1Gz +ȜrJxPAI(dQ{>I3? lMi&sP4&' s7B܍J($X^udrGY&֧EdI{{P|!8)!)@KRP{R5E %Y&Ґ'v,AUDè=dBFVu @c br*'-\pCy4Ր{ _Q[: T*r @QIșIIn F#5Bz&#a{еR_DòddRMhm`F#N1HG%~ u x) J?lh2!Lr`iRyRxv Sx)@DJ?}F5Z W X0`o)O91(lY# E"hr!H $9  ʙK}`q8Ŭu0N)Cb}J9FUp*Nxd'2̌k["ᎃP]fIѪAF=(Dn1D(wx~H<1TbS=xɪ0,K6?-+ ZЙSA b8>P3F<=r5JP%=܃kD)8.PnY3$B ^@ kE(", G<:d$ƜVInN 3j5ek4Q@+֥QF _)D'_J8U$$NL /J{t(Nih3q2Y*>zңbK(G`[@L rI+^ᝎK÷lsIbv쐾Z5 \{A̾^mcۊ"xOj³D3drBnTңhʟj*c 4sXC!r49H,/s|~_^Q jqpfB)C[JR2yu(alyQ\AG{)SL+Q%:t_At %\Dr0 tV}׾V잁 mRaO]RýSX -ZY J]0%yyԢ25")[|akTD\` $ #WVă٠ iecB(*MO$Dy$+nRCmAHspv"NI%TdHIUx8?Aaj\hf`^iNh8e AN-USI|@z(ƀyRDXdQQRaڎOsC5d "B*REi5h@!x` W…dD>h]b2S]`FH٥*Sn^О ԡա DPV]teCJ*̥aWD4h[|3'q+ͦ]2\́H; fjjZ8nͬ)"XG*sEDHA NB jM%@iff'_RBVBJ^!9B/=L\Fx ׌%]em 'hPoAP(Sm4 BlR#٭0Ihx^e-S-hQIVHmPIiAⴎ/#| ܰbHKh^l%K!m Ȋ DTĬ4ăLr%bu-(+Llya%AhFQkAHTQ%f(GK%NEGXar /нIkj D붹)4iW< VJTD 0|\ a(UĘLa5еN8s 2;\6E`4FH5H5}",A9} ]|2q/P5E UrubH$Žz* K51[DHƄAVsOS>\bwHTlEfB)D4LӎpH S&Me4E(5%? m^vL  LG(*%Ҵ9مAeBmь|]milc{^s4|M'!eiO(iƅ|uG,4]("8s_4]>cfP4TUz QZH*taފ2Tv7z T̞|uxd,Fjy$k9芘EH־/A{++<j,́KdfyPx3Xfxs݉S"̸Q@:jbe=.x_ ΃0W16D)8 6wiz4PK& qLA Mj=$\8艃@lgOHdh mPf Xq+Иb)d$&uʯ!L{@E ExZt2V2=NKU\.ϰ|| B96/ $c̄ )0+B5گ(ȣȀB b h".LjPFDd()CB(uA;- *t ZR"\b4Ґ:" ~\eV "h6*: !:ZB JXBb -2찢*S*tk(F(C9"Ac 2hJ%I+5 Ȱ0r&;j~ #O! JBa%pε*䔊i%J C >"#S"*8̀ -2?c5&ճ=U80B,<ɘIŪ* W$,(8* ܜZ֌xuI(JΣ9N( *h9XA,%#d`0\C$ԑ d@&J)NxoKLlēΤ}LtJ )"%@O$GN Ok'Mmo8jH)hm #hn.Ol[ ӢV2$'P:Lԯ ~/ʞl7LH 0 nVN Ѯ<0SF9M+ԬݾLT3>˯l:-:j|&z,OhK{q@tڧ eR)+YBl`8NsTDG xxA0-EhL\ (!`x9v>0@V=[[ t>RUyO|KR@M  :`FW(*!%8 8 }R7#u) x:BfYTڅPmprdPh`'#j |R(O'hEblRhV]*Jg ftpFoB -c=4~lbZPy1E0gE$fp?íl!]qȃj!kE34BCaj,vB#p+f0.!82HhU'ɄH2:eM#i^POjrE ;!q[Rv941.d! sY56އm#R=Qz#DQÃ;-%j:'*o +enJo[?X{I`E+$* R؂~4Ԇ(7c{=y3\Nl:u,ΊJP.,YaUskēLe:*TE/ Ƞ-Xc*X mhP1YB0"x`Xiy^U* Z)\)-SAŭkvB c q#dA P54kP]@r"EǦ} Ud(/Zy( i@ ?jAJaf$)ÒI}; 0IB]&0>[5!#T %Z5ZA`qg>B6i}j'a2oވ6B'Z|BQVK3{ 2RJ ӂ#ӥpK S~@PIHn!BAFA%$V"9;f5X RHDZb(γ2015OS܌ijҞ DzC49?KҎH`\t`TGD&UɆ0W~$ZW)gH3ۯB(v :XNK{t@TFn4W |") ͬ",#,`bB&:npǎ0n"9h$栽<08 g`#d"!.t.>  & 20 &*A K-C|OTH2ɨ!߃@dgkoQsQw~uq}m1o1Q Π qTEQ!qQ QQ1qQ ""1ّ/#)#C$GR$E2! 7P$%Wr% %GR&)$g![2!A#S''((?2'y)K)[!o)**%&r#& e%U)!--r.2/.r.!(OR-1R1&#G!wR2ɲ+& R u(m"r3 1#,R3MN5SS5W5[S0-4r&r0}6c6!&=3"}!s((0r)2730cR--Ma::;S;s;&R324g(k38<91S62%q91=3=u6iS8*j!@ATAAaP&'@)2(S39=SUR9CG?)S)39r)="s9 Y*AhFkGoTGrGmT!-m q᱾3ma*iq(!T6={3>3>94?S,r6s7??dT!N;P!,Tl!RAHRR!H!!N!HL pZ(V!-C,yMqSެT3&A0?tM)=G0S3g!FQt@,!̀ f̀: 8 5N6{*P2L! 2]! EC߭=E#DLWBD"]uRSVOMTA@H,ƀba Y`[ubS6Hi3!3B ft TS?=T$kr+E-B VV(E2_i3>3X)@9Aa vkV`l6A3'k!q paepA`nas a5r*Wr2EKWT-U]L1AA/sA<442q26?_>5TVCuD6+oqr6r(_q@ ;{;wD7a+,sRT q+EsoikpE wVp4|it|W|ϗ|7|0SUqUL ?'Y~t#T?g=We}3u%!HA؁ۗH{v~YWqsy' 9rjmu4}2i}vIWDX8 ;itksnap/ProgramData/HTMLHelp/Artwork/ttEdgeParameters1.gif0000644000076500000240000016314410534177570023005 0ustar paulystaffGIF89a!! +19BF!!!!'" 7*2,9/)=8338@9;J=1?W3V !NHCPPJ]LKMXS^WRWfVWlkm//gk_?8cxuoqo{qqnqu}sz~Ú9ORVBJRN^Z^_mkl{N}㇝ؘ鱦ŮëƲƵƽ֭Ž˥Ư˵ƨذĽЭνֵֽƵε  !!--))!!11--99U(BBcBMMRR^^uYqqpp{¦ޭ䲵޵乿ƽֵֽƵʽީ,"d%J(\ȰÇ#JHŋ3jȱǏ CI'*Q43P4yB͛8sɳϟ@ JѣH*]ʴӢM@!L0iL^rm2سdњMv[p}+.ݻsՋw߾ ;80Æ^ڭL LYU!TrV&CSMӨS^ͺװc˞Mvm!+<ޤ}O~5q+~|(uK>E(P`׎%_n*5SRBA~rΘo'X}&) F(Vhfv:QStK`ĉP(KSp‰&x$A%161wnī'/8̈;.93agw袗餣nzx䄃nOܻ&{ݡGH'O1@@HJ_MaN9FFwDNvvLQ@l@(C8`сg[d Vp`) NЃ`?Yq;a"+ eڰ/4 !up4a!PDb٭xRHV"B1EAx&(O 8l/kY%@!ShGc8`8Wđ;/P 1BF@ P>U9_&<\(0JIJSpb9Z%.w^/IarsLftxAQ2S\&)MdF5MkRf8'4Q^^<]^<' yB_" ?a HPBI(#60Gp#H!<P`п?DQDо }P`.ISl̩NwӞ@ PJԢHMRԦ:JNӍQ@Q "d ?<4#m Ϫ"]Q+3Ld &K]v5#]g\a`gPXͬf7zֲIhGҚMj=F嵮-lg+-nwN5E1'E* ExX# N1@D>@*2ceR6AJhJ ޾e]IBBV/+~ͯ~Y% qQR(/K"E i'/ 1CKZĠM1W|*vqabX81YAꊚw!322Vd& *XA$P7n[ _^~mݎY]F 4n3,g1*3a[πMh<І6shF;zѐF%FWzҖ5mlN Mg}QHuհgMZBL]3Ml^vld;ޞPj і쐍+>;pE Bvn7z{VWn~pw p;?8'.78#s/}s\Fя^t+Pgzԧ.SV}Eyvc՞m_vם2wv=Ox~/<ƻ={Oap HC3qopEwKӻ>'Uq^=o/׽S^O>3~/O{!yhB go߿G>p ؀   XX](H#؁) p(@h@1X35x77H>(2980Xp1H<Ȅy5 xdNPٔQRYTyQI=Y _`9bYdyfhfؒpr9tYHǀ~@E0 Yiyɘy9=0 )  99 vٚ(鸎N0p69 7雼Y99F YٜȔd_I ^aiY ٝ^_ O(p) 0^Pɐx@`8`~09yx0^ָ- T@) К,~3)?)ɛ8h 8P% ).,% )5gJ5@?*M٣D@:HR)(K٣=P=镵 8` ^i V^Zʥ #0T0Xj Yڥbznɞp090- ^`) 3Py}0V0@<rӐ )0`0&0.V.02 &9P~Щjxʪ0)@<`0r $9y 4ɛ/(z?h!<F10 0 1 2$: hpyPi@P)P5`=`?J)` JX;M)`"+@  [ٕ^9.@X;` : ʳiZ :P 83k lʦX@8 XC(- 0@&0)2`r dkh2@VPjhj*- jx`"sr00۷8v"<)p[+<ʸ;Ә*j99P?p1"% ;?P")1) Y92 &05@pP K0`QJ ,Kk(KTJ T5K//P;0:]01PX8:;l%PP.-*90 ӊ~%t0 )03`K:)6Ұ%pVܰD^3 )ê; < +,(EӘ vs1P /*% %p2 'ܮ1 2&p;d`K껣0`KQ 0 $,0p, :0*|4[̠%:l Yk 13Pج 5# 0,+9f00ɀv+J~@ >fPGM鱦Ί JgRζεp1^ 1n `31 6>X`G 9^ \]=-m~,[^%\295y if~i~,j΢l?v~x~VmX?}>^N^})Y>^^8p>^~ꧾHԁ܎^~.n im쾞nNʞlkپۮٮٮ>iݾi`nَ~nԸ^n/ ?$O$n 0 6/ 7Ѐ<= 8? ;o>D98OLNPKD T?F?Z\ok8 [YX[_Foiin]fEvp?j~yoi >2ҎӐn_?o_/?_/`?Ͱ/c`?_̿_P+ Z NǾm`n?_/c@~۞kҘF 4&dXBB(1bE'nQ̚z!C6"f)UdK1el 9uSX5 6iIFs B]TWJUPn vԲhMkVZOek]yk.٫pC<7"GnI9| ً*0`(!‚2i !4 Z@Ba-`4hp14HAsR{IvF/rH +M7"@7JHRʭ> >,f†JӽZ"hƋ/ ,a&yҞ(f*sTK.ttˀL$T5;3o>:y>1S=g0OmAhzQ> XbP ehoXw"mͫreE q5"l9s:veq2]VB!nm Br/ xJwKyE/N\3P N*iUO948-e`7Af1QC=A<0 _`*i@M%L#xG{Aw5 6 4ARA,P(]#G!ӉB#.iDܖe:q s=69Nȉ3vДV7]'Ý(Eh?~E88͉eȶG/k 46-f0pAgBiv@F9,h'?t:)7Qj4)(!FQON:QA0M2:ec#YFYV=-TH&ԡ "S9` LPK՛j,u@=(FWX9y`+ P<@I%K 9@SfPU^ 0` Āu,ۋ8,^ {7{y\c)>s$d{RƓr8o^n.J棅@zFl>LQM Z03|a>3"13;9(~Srl6 0%@ b6Hi  -ϊ,1Xn`u ̀u˚T^1?v "rZ[Tӹg gJ"e궺bJ6mi@R&&ګn' ;7={x# ز衞Py2}љ> 9 B⁥J2#xʐ?3Jy3Iҁz4)*A 6`v``&&3 $v~aaa.^q\uX!_$ˁ2C E5$ٍ8C)P3C̐>ՙ C\:I'9* Hb.ր3Cd腆78b? :]VقO ނԑmO[Y/b/8Tm\>`\F2pa b>e`fhheCef\ffT3:8(gt>gtNunrVt.3`dXXiS b1Kb8?N/p`-:iŋ FFD.VH*.`@Xh<CЄ6vcRˍ9 `$4(T%>CnHp3+M8QUhHX-^vش K!Wf`8VqP/^Ph h+ЀXjq`**8@%V3ql8! hHnC ؕB2b_mpn^f+sw6g.n&n~g0dXXlN.v`!@ x"FS]x-DH.0L@DP@`I`KYh+`9FK3u˞cP3p^&FvxhdD.@BV@oD@Gj^*D ?'?lW;ە z=rc('_=sh  E1z,Ϗ?hr-ݓ>kΘsZ"0s)q`000()K2N/K2uS)煭N,..,l3HyLpl`KB(@Px`vDXxpȐP-;?$ 瀈IЀ_/o^0~=+QMhAv@(xAAH0gyv9Д9pkguQ]7>@x6?gn_Hb2Kt)R2`({N/OO6l%64#QԊlvOE0xX~F`GJ X-:/L`M QȌCr3X986L 9F$l`|F$X+JxD0E0}DX 0~ A0B8Hx^v wȵ4z͋_|A\Nb$>^Y7TMP 4^KECVx@h "M9^ g‰q)Z" B_Ɛ㆜/ȀR'Cq)JjUZN&L '``q†$0[0Ȏ ޷pʄ,21ybMĉmY<{&cE I bƌ/r̀!@@,dGܸM!3tT D2u:gi (q>$bh%<,D)Nᅏc1ݼ*pQsΟg$b9d`Uv=~؆n 2&h"1Ġ\H ##q0^yQ@kJbP15YGmAI]IE4pU8ќ4U5yU$)8[R%EQd]Q$V]ry՗OIfSVdZY&v[$Ye%p^qY6 zᕗWEb=fωU 'h1Zi Hr3즂 /|*0̠s`t~ˆ&אI'x-9V[,3y,[Uz)HwuOR}JqY>ʨ ,Ս`\JQ[aE4@E50~`YgTt_l4A(,9`6P-k . Nf='"B $,y_xI!GJZZËd hP479wNj VPǵ9Li#l5L(@1@I'س5q Á!F1!A#4n#zG=XXHZ2Z=0ij{V0'?`iG&BI5F#BBDm$I R @ARjH܀bւh)F3@#rh 6< V(ਰO9 w F<x@ p4q"? t2AǾ C,rRlpAw \́ `y{tŎSTR : =q"9Qt"aC@;xFf`$ȁ V/ 1vԣ6cabA I/+&Y pf|IlF0"d TNi:s'A}(D#*щҁ jKvM0mļNp 㙁 /\p VPs؄!A *q9=%& Ub$@_^40^"хUjA2@LP||#y J0ИKGӤVp<8BdBp3r0j@b(%cA}BO0ְX'[05g"(H50@q >W X|ny * 3(+wcy؂~ Z*Ặ.:ˌj% ݁@H8B@Ñȁ'\dHy,. JM 2Px0Ba sxCNԖQV扊"C9(3Y@ :ߩcN@ jj6SZ֑; k $-A"х ^p)Bu0-@4; YHm_^LeĆ#t"#|.P 1Ma@t %G%݆vdA@jM4XrV5˙jAJSe1Dƣ q@`%'ٺ^u0CHbVݥeL3$P+ay?~>X F` @d V<SpxP \A@ )`:J͈C81o 6:޶@[K[!_O 4eGvh+k~@[X^5Z0h\=49{7R} mS'])8e<_)d r`캀 '`>F,NV,^f,Ŋi}e Q tRBP4@) )QZ`$4҇\4TO4פɎANNQ @Ύ%*Q>R)OH_*n \m@CzhbhO~S  c2nf&X2Xn^.~z.bn鎮nfn...Π)0 㥴%H@qF܅ژ0ლkPQ#gF`28FPm&_VYJMTFh fr "b4]>DV@fT, [= /YDv@Dhii22L.=b4$4b7p =Cb ׃ Ӱ p p 00C=A<0;;inVNGA Jy$Kn&4H`.p!b-w)u "$hJ=pe-ZOP!{d5RxL$rZB|_T'gZf L@b bZfFdAB0F̊ EJx4r)2Y~uڧ͡jF.$C ;<$l93@<4`343gs=C3w8ss6/:s98/;p<{37;?s=T4::3<=s.=Vwf>sg9eW6?6es3|\<tap/PL¤ Ai@E1*ϭ/h˳>s;6f=ssb@?{6;6fg6f87CA89-@DSIX N]<8-dD= ( ,ZoZ$oRPnGz D{Y'`"Y~m:mL P[H5k鴱 (ջdޛ bJgK+pC3x6L6xC3pwsC7?7iبq6f mBo 4"F-"y"Cl ݤqclfCr8Mn) l'M\4q@WѢB&-Rqfz[6۲&<~\AVG/sAǏ9.JR@ .fDfϞ/F`;Ĉ !-ZP3xʙŊ/T`HbFlc1o hժִ\/u[_!`8Gc^Il.o 3v=zu'f Ϛ-y3(mqӲ&7bc)4qBq0  - ' C0B KCqLl'L(Z0*qd P>+ŠB ⋱J4/Œ1Ơy 0DqBb$;j@P0D >># /<3N5X{6" 2 *I(ʸpA$aǞhGM{C@P N/IN<O>ࡹךs5`ecb?->1ZR kn õ6\pE\t:[w*<G *8AQD@/h4*R20:* LA:q`$0RtUߚtf6A(؏K8/N@3JX1^ża$ s2yÌx\ȡ`ňy%?-p(s][qV*/H(5^1Hi]1w׋/)Wb17!p&/#4h$ Z\h/;12>SP qu3/9*A,;\ d&0_C1^v^A ^ƃŨ1;-Aw@щO"4,ͅ ^x @/:71cFq#" `u 8= 6(A H@mĪ`aJ.Є@tjЍ0of5%[6YX8nIR%K.xZ*f "B\ x!̓xF /"NuLt#2l.Z,,("Vld<Ad@`RqLk'#J()PЊ|LT)P u? Bkp^!4U}5D /EsL3jEقo(.IU\ab &FUtyC@x~j͖u2K X A hx I'pEg0 _t(iKF*bLniR&8D\vY@I 2Ƀ |V$k˲|rHkn-C;Ffh pK8dZYlaJYR)Eņr/,-bQ䐂17ús"(>y :g髿Fn =mKâ@݉Q2 T] 5ṽ2p6HA ]G.}>yFwm9 l jT)kITȁ"T@X٦`gdG. Oh"#@ ` 4 >97#A HZN0rR:?7j7/Sط 0h{?D'4/N/|gx/β|`>܎,z RƆJspmW H ?606!6@P=®9@b&`H@aaZ@|:`vTL_,(6bD `InT`iJF-f@/d!z54 08K& w08zff΢N`4\ Dw p A ! 0#t ~C.\x+x:la (@bvN4ƂYPY`p1Jwqy{Q`n{k؊11Qw̘1Qy) @m1*@ PplOL'+ +R+q++ò+R,,,@6-R++,,-,R+b/r.r /02 怜Vƭ$5WoWTVXIUAUUQ>U6`6IY5DZWUWeuXm$OD5zp :!n`R |ot1B`.#(H%cEIE9~v=6R[FD.~# ]+4_Ղ^@u fh,|StN Jx atM?#pY5?Z^f^hcZVWiV=5@`SVo$jSi*jҘklauk iVmk**W[v@vRpUnIWm]k;ZVWCU--jVXkQZvVY*Y7}1TDvHkb{O]63 5f@imt o4(:a kd`Hffr`I/4{zkㅹ`5r@6l2(fh,a0z7 "8IX Tq_BEaeZj}$z`'ٵRQܔN 0T &`UiaL8VY`XPf%A\sN3e"Xl_y+ӘCMǹPEUPExF )%0%p

 _q>Өw:XLK综r5^tXDonK)YD7e8PaVdЃNNo(E'|@t xtM @ ] @P|ceVnp[wrG:TMP 9 5 YOp W ,vM;=s"Lֆw$w.v 0v * 0[<6ic֘:Ntve|(w׆b[** |BXchY&WyU5h1I>a#tj8O"u:S6@si WFnxSIt@5 HX!00%'m 'ѩ p FKw ?dVdUijvaF!K`+Cu :'q`UYfA | H:|$ `B1B@J< * W n+Ո'OAkub穟W]A@{wkY(i􌿉 bfXF`\ԉie^@8& ]V# =)ECMxayG^ҷG5Q8puhH`H0G| @Q~S,8ofy|Gi2yGقyץ}<רfeW `>1`>`Vǥ@'|}񆘊IFQ5@YЙ4]% -WtԝA0DGW6*+\ӹ[YۻCE "rz c>J>`p1~p@`ir:MY Gq?nF}p4o6d:?eyǃXn ;@UqWMye05N;};2?\JH`i,`Ѽ= i}>wՙ R 9uaapw=͓u)IFA-Az uܾ-:u ya B, sI2]JKIIH} Iz: N:Vg <}(N A( xāM(t^YI`'@5GI~D} è֙ =щ o^&.I+sxoLa{R s}V<um@LSq BXq H$JxBa  (hRlUP\G"cj~}P|wD>8u\_ hET.h\L$)(HğcN<QqVAtՖ.8(ȱN':oY4no(SZ"T 9J($H#dКA `m:h0VpkVH̭& #anI:}7C@\]H?3@:6 bdH~|O.>0b?͠bQl6R9ʆL!wq:h9G)|ّnMec#E|J =I2&%&ч3]RR Б IwfLNrR(A[VcGTq 騉O;LÐ()]?xD Jg^l`0@vhmD".Bf0bXG(7V ^PԊ̮^,X D@@l `*X XP& pWSAtj r E$X 8ndyZI A.҇W~'QkԦZU(l~ ,2s^fP XXA Mxi(G?,\ !dZc ` ת @@pP5@Ur 0)S?-*pe|JȰM C`,gsI~vrW' x׋uC, چtVn+ HVt ,`ق(]o`\h> m A͠1i܂A3LzX0l _:Snt@[M |Zv Ҟm%gmsnmlyyܡv½ z16m`nrdR;7$\u Ʒ,a  0VC6} 6Ɵ xma%79ėd@$k@;mX?p0y8?c@ĻlB"! B=ȃ"BoXo#lB'@'-|jBkB'l;"D.B;A8o!Cj&(_( `'H"Ȃ3 po"G=i|C"Hmp&'`Qlc` ؂<`>iC\#( [(p3"\!(_XA8Lpj'=f==(H=fpptfP$dF߻ {p!bb)0]&!"zGlkFSp ȃxm"->(qXpLJ  .|&($K" &; qʷK|xA\,]0# , ^ <)^<'|F7A\@qoxq LrM4|M4M&ӔMмJ|ALMߜM,͗LrHr |NtNL)dqx?tNnP ɼNNDOLdwЌOlcnLPMԌM[mPP m=O{iQ-Q=Qk0?}Q=l;wQPyQzR! zz"5R$U$"= ņy0RzQ)Q,ER)'*/R&P"%"R048R=? H9 =S==S?SA=@TC=T@= % ?SG@EmT>TKB5T?]G?TMMTFTONMPS ;itksnap/ProgramData/HTMLHelp/Artwork/ttRegion3DScalpelLine.gif0000644000076500000240000002230410534177571023553 0ustar paulystaffGIF89a  '7#$(##79+#!8*%5 *= 13,:3-,*(0/+331'/8(4652GXex= K"~;GFHHVSK%G 9D8Y*Y 9P/H$&F#5[$(_25gdvwi!i 7f"h8w%x 6| e=?z%)}/3O@N@RB[IB j]D:AaMiTwB|Ir[``b|{bHHHHWWPBMQNP^]WQa_Xii_rsnOPsW\gggasseywi~}yhicptrkwww9-< 41 NCUPHxv\\k}ktyz&5' 6 + 3( 5CYLT D[tjqky DHmguixEH  !2' 0$4  -$,% .&4 .(60>MU7F@NCS " hl!##%&(*,or¶ƽĶǸʾūȮ̲ԴٶܹȺ,H*\ȰÇ#JH13jȱǏ CIɓ(Snȯ˗0cʜI͛8sɳϟ1Y£BѣH*]ʴӧP  /T%SVMԯ`" %hӪ]6-*0Qx@1K*P-~f LXoSkP f&(p[9[*U`$1"SYR)XaI4;D Ywq u d -PAĭk|LCBY>l/i!@ U`[ ߀ ÛZ@VZ@1*A*oRhX45Z '3"DP4 `LcaO )Jq50AXDHV|Xcvdp`s&TVB8*RH !D `ZY- @  ,'A8GTm0КIZ3BJ+DZa`͗E V[{%QH(̢ .080LP|h0[ <# .##fqD 8%L +ԂK-3l6X*<D0||p, uVBA ZvȰč "&Fh$43~Mنw.Yl P@HVL힠3tK.RfœO4O i x|ܢp2aI PAk?j@ H ܭ`XSi}WxϷ ,,llIh@sUTP, ,RHXP1*$E XŸ"0t sV B!E|jז Vb^VE+P x @1g]dգ&8 "0Å*f8*`&J8"YԠ;Ӆ/䦾tGxQ CDi @T8;=`.z \b!±'H1F1P]j@CBY.ja B6c?c c>s8D)n >& ]`e r0#!3E3Cɾ@ (+)--im -9C8VQ ] ƴH |)B!ZB`!LB@11乞u--.Z wXA4T| B`<>Ucn)L[a\N0S05o N -NwpXc D1 { S cԎ\C' L0 "+8!8XVB0,bBh ш@7oZaA0p5QsegBBZ :+)%Rf  0|9;Y?^t<4@$ZʰɦR!E-jji%s1Eə%@$T@Zu0'h!LXx7X K C OZSV JDUa`" S -`72f6yઞ݊BDBn-Ok%8 3j}ϡ,"ar:q'.`! I[ h ~?$iQda5{⬅+X| p1# O\0H^b_TTBͼZm*kCpl,a !?{lC4AVIZ c O1hx"%sj_Q#j'E ^L۸T,"{lk0>y,\!?0!GGf$A&E PN9q}+9SDCtP ^A\" R{'Dq]7XݧAsr v ;ưmYG4!Ddgzr Co p1'1`[gcjHx`0ca! .'pU qQl@Vg;gtlF4/Gwr`z}\C \Aax`&x6EsR P -'ME?wAgp-ȅV}| sV C+RuǓ<0 qv@yfl7|h0jx` @7r<% |"x^c#V8xlGr&C! o`C+gua'W3K p QlScrvtXzɔ](`v(ZaJPx y} ŏ 56x؍<1ׇ0 *7kZ02Dp@}#5LjYXxGBp8}Aq$Dqh3 C2Q}py1 sȋ&XYtl7 !V|Zp)" nPzf-E'ÔqY)tU d4 0e0S> ̈́r5QT>}]Ŗ1Y}s$aF hX%4+p Q5@0UY/X95荨֝tT'dt 18ѐUa^)}g Ÿu8c_MAƙڞ  8!;:贈;:I@׏}P5)nJVJ[ hyO(XQ' bz( 5q0!4` 7 rճGAia*J Ypq;62]X̦7 ;`޹j ]h JV;L@-bMUTyӺ 5ݳS` :P 0 @Ǡ 6ڼ }ge EQu[ V P 57p ; Pteլ䢇ܝg)'ՆJn„ 7}8؞UnztQj;% ^̰vx䣞t]0g.yP\e^HY凃DSvuH?8Miz} < 0&I|k wO໋lw\4SZwߓHѫđOJjIԅjT-P ~ ИP;Hs tpّǠlICG^MʭFTdA>H(첛L} ` 6 ̰ %YMέwE-ϸ{:3vҋX[ȒW8QҜaJ'0  )ʉ~ mnuS˙ PM/-F+~EǢ9AJO 3@@ ; XJv+O[2&+ @%N8s􋚃'MYo?Ɂ,5Z J  W@̓a[}\][$] 'Z[.zzvKhZp0P @ ߮WdKf`nfERcAUiBCfX*1홴,͑-;zM&鉧~h91.0E&BqJ&\z-+.3)*TO!R=f)q U֮UgXW<-D81!]Ez w`$T Vċ}mq,Yhz8gOI͑ >_kVk̹իVڋY3j(X#فE^e%]%:Rr⬢Kh/5H sL ?Bx/P@[PGǚ c & VnPC|,^)>(2+= IMʯ*&J#d"aHa ?2IEC^KԱZ<1./ p! LG:] o ,gaj]Зd,37r?.5=X>*0٥0`, $NxbRSI\[Pn܂+b/Pz|>+&AV=,]jv"N=@wc~9 L`1d'qC;i,1b­]!">JdhǍ%>l *iAL )qծ[ڲˊ>4^z55AV쪦a&D:ĬgC>λԛ] o?f޵_kK""?l,b tVO3!E1sZɯ*@HfE5> c:).j £/ b杩 #5S#c(`hPuB}VIᗾg~ cx7kH <8u! n& A2ap=+H2^l2`—JXh0̠}"DXyX&$M@ܵI+#C&0&0%=G$Rw4J%`[&edu Px&(?)`(1]'|(q\Sp#mJ@L`$R,u)J#Q5%IsGD8 $D xB0@X'aZN\ՐZ$ kvI# BF014 Uy+,CޞG12)FT Un_68T@D0F2ڬhH(*:Ų(dY'IЌTPU Jt32=E:|Q#(G3%|nqT/p' Ǭ 0c?+TD[Qd3 oc^ -!0"H D)=fˊ|:&h_*` `P 0a@M/Zͼ=fz}XՁpt ň@0T4 .&(OűsMC٤uD 7ĄV it qMol|-<{)X@$1@bQ"V-3`ܻ>␪$+۴  (_1< g2g  7;_mP\&8Uj x)`+8aX5·0%66)p Hf O1Nt%} "TP, vt_ݩb9i) -yWjV}b}jV s x׸~S_auf~Q1غuh8y=v4tNQP8S2tKOKa]kg{v]s{~w]h~'| Cg|xG^<+O |=yЇMzԧ^g}]^=l{^__|~W> G_ӧ~zT^Ͽ~_z0~'x3_7_t̃ot۾?tпKkɳqpvh v@ @ @@u0@ @ H\tTDMHqXOO E.HDNK$CsDqpD=BBDn؆ HȃƔmJ$H2ƟTF<<`>GlIv؆<@I<ɰGɐ ?DJT[ Rcks| O9DD6E@NDS# % !)+!"%9֏ %#%%'(0*14-2:.28:>CECVDJVVagv¶ƽĶƹʾŬɮ̯аηƶ̹̱ӳص۹Ķ;,H*\ȰÇ#JH1Ή3jȱǏ CIɓ(Sn该˗0cʜI͛8sɳϟ1YBѣH*]ʴӧPJJULVׯ`Vl@lE Zj۷p* C[˷/\jL+^c<&X˘@ΠC TgѨ'k:-԰}:mh|N8x[#P6ܫ7>VJ廩k^p9<y .;7 }&37FNp]~i b AQM p1"awݦm* #qlPt'0#sHyXq* 0H"/ԧqh!FQ4kAFH#ȡc{}3Rو"plWƩ aH# v 蛝9 29!?f rH9!u $0"b!}@0]Zjgab+LgFj[ 0:]Bmn! &R zu[CrH#'B-sxݫ  Bˈ#0)s|Arj#qNw93A"4Hh: clF3d"ܙ9 A.+o&3*."rWUF2j  H"82H5aw9‘ga#@hy3Zٛ W @c*xŽI]򲛩="J)ȐϺو{k`nb± "f#Qc~Л# WKB' )&PMlݗ:+H!FzԈ =oAjWҳ~\%P(n B>p1YNxKh5NZ"|MSU4LG@P:/x K9R#BBH5R 'Ђ!!'dO DPQyXdN5&>E HME Q!19S].ÙBp, C(D@,B#I&9Q4BL% B^8␆H1F bS$=SɮPA AC~ZV#)9(3+\%+('P =yKaF *( :\%2Ev3թ@9?RQL$L,iTtv mR T'NP2Vvq>3l'% ̈ uq&B//ht;fG[M [HA>hR@F : 6>g,|peat/QjAT:)Eq0 j+"~i|6= x&aa꤇hY$U8fѩlZt)VCa 2p{Ё`vY\0:ohf>p5 0q M$k ʔZ\Lz$u! @,pS-HrrX[.uD2:U`D aýEu!ۊ Bn~Zx=ehPS C/tAWPl["]#*\L0pŀ0tZv@9Q8bF$ϤO"OHo! Y{݊rnJK#T}ȕ T< - &ɤȃ\05D$zɑV5Uy"vi%lF9N~2+:=Lpg>tiµ F(<eXA禘,` VDG %D֗,NRnZ&Ճ p]\ `/tQY(@1+lrְ:>_^@j\'  '  t&Â&p[L%VB..%T9,a1>u+F "SvxX2Sa6ܯ(A;b/+9"+b [" -XԺ&h` 0 ʺG@+C1ňAd 8DZ Z v/c XPD'@:P^Bn ֯AKLX^#bH!/+n;~߽MN|b6h$.z/ 6Z %h>! (y1!l#nE+T?wZ̐onLJV'OyY}*)r|smwE0]mCi M"}.03˶p;"+}993r)&]K=MHI JIZv p.)}FBH[u0P ] M}حpx`F@NoXx)C ЂW=),.IDJ%x{ d@  vtup }jb Y2QlDex{p6+ZR]A0B75t gx G{@ K = ` v%6}ѩPx®E%T:eH4> D&:#Nv` = 0ʐ:  `kky Ktjʈи#bBQ?JB.+1app%@wPPX tp9 ~W Jۋd񪱊6ɮpWrDIT2Q'5QK񤙠!Kyp}yuK bPi, 7jsn6 'H `bEQIT9l0v0 lHpPj @ ;@XpP Hk'iYѱP3056|E44qcTr) V™m xI $^1s9]o0~-o Y^! @-UV\ɱx]^5R-q2= l^Wz > @GuJk+d m pty1CGIKttq0H@v  ym͔<`Fu-:kvqfV03a(l!7lXsdYY;|-͊"s3 5pۆ plp =`!U,*, l3y݃lȑ+ *_ R%- G|z yԠdz1r_t_8#P*]l58u9%x O`v `zۖ >@P~w'n(} ZQF%e^0#yc 8. hz ݍ^ j( v ׆UB)l֑*@H܌xm-Ppį@q9Ď\ ;?16 bq7 uǞ Db8-v ލ+QC"q5έ|F ypxWˣ 9S]J 9p7@zPChBmȠ x gvpKxO^*:8 z^ -oL x M(v 8$P/M9- ,?.WǦ nJx=`!Y>z?^mշ: w X .v@6⃾z澞0ꦚ01;@- Fb: y@춁, {zQ~'ط7T_vq:P@Đf,z9˱Ano:PXāz`0 e{ w) oPh3wP̭[ N mʪ;İbMq; +}idn8 hde)%  wA!Pypiş J)8  lVva9ljykv ila'x!V0]{6u+ <>fN{\/ "1vw?\blKYޖpzKM `ҔiL^•K.^ KW\pՒ%(Lx, YI)Udٲe(Hpi?'{Fz(Q^ړU-VQFKW%Fs'XiO)GYIauժSV> jƫvڳDžZĉQYNőʠaƬDbjTZCo WQR{8 k!æ2'Vb+LZť+R<oI[zypnQN8(<У2RX(V:$(H? ѺBh伅 .Rܳx;80P>yp0 gL, #;A"wa&ɚj2"%P4;r 0|h)dIJ5 aOF!L0s,h 3JO>`=Aګ+:kBml#56(H묡$\۵&[gl%[1\1|WKc!;qn`tK7tSW}u[wuc}vk}~rgw{w~x7xW~y]w磗~z꫷~{|}Q}w}G|w?~P$`w~(G@FPl`>z?fPD`?oh&K>5STCȌ*YܢE,qwi\#ݨ6Qslױ9k#G:1L$$X3\"* Td%)yG"(?Έv!)GYҔ9TJRyF$)iI\NҒH%qHNe9gL#J9A%# JxFsdsJP6qe8:s%:J9mcHG;9sChG5qP,f61nS E5yh SF2MS,́3,@xf;%c  đ#$=3 !F`2hjԓ )pT 6QmdcXG5*գ5Gfa FH2hZ% h@uw(.@hG ی3,C ha5piҰ} e gXB!Tt2$m;Α k#*k r6Aіh%Kh %| 3g8ÊVk_va BFfa9} Q Pxӫi6@;p5Αc~۫A@3ָwXX/3QCwUwDx8,θ9~/wxGhI>)>򄻑bFY7$2]D=QZ6G')YeWe421 Y>xWY{sgvyw M:8hHGZғt8"-;Cӛ:8iP`)}jTOV^t]j}N$ ;itksnap/ProgramData/HTMLHelp/Artwork/ttRegion3DTools.gif0000644000076500000240000000774010534177571022467 0ustar paulystaffGIF89ay!!!!!!)))11)19)991991BB9BB9BJ9JJBJJBRRJRRJRZJZZRccRckRkkZkkZsscsscs{c{{k{{ksss{{|ƢŻƥΥΨǭέֱ̭֮ѳ׵ֶܸįȵ̻,yH*\ȰÇ^Hŋ3jȱǏ CV(ɓ(S$˗0?I͓3oɓbΞ@)QDwjYe"S>Jգ\ ]~%ٱPVjoܹhܵ+wS`mի@ڔDQkt/skޝzv8ظxQ55_'`r_Nq͖Wq}c4bhI>-Y8櫡4V^s8=vs}i֖V'}߮^ן\UwS}]]Ԡ^ .(c w][vӆxQ(h(,0(#8<@)D H XŒL6PF)TViXJRSd!dihljn9L_bAŝx|矀*蠄)tB!E6裐F*餔Vj饘fJ顉>駠*ꨡrOHꪬ*무j뭲%+쯺NeS$6F+Vk-Q{ű+DH¹覫`n.r,2oЦlp 7L0\"z*:ogw\28+$l( "0,fq31@23D,aH/@L7=D7-L?ݬsN`-6m6P'qt{ XK^߀.x n8-7<.wOO>DWl8[,Jn+@#x>9̎yCn{F{>ZN:ȯ鮇xBD/B@=O?˳|CC[4ϐ-hW/uA0 H`P[ ~= j0\Ax *@@o[uԙ @3W: ΃VB!1ޢZdī#Ap$ KО%2P0HY4D  @clp |@Gq1] Pt O@n%vPB}6&Y0@ Jg8Ⱥ:\ H`f_[ /(Ex@.`J:ѭmS@`BƄ=!J P@Pt0Fy.]+Xeg6lcl@.Pa.Z)@ϚXNSus06DJ l&.DIvssZN]˜o\' K΁ufnjl`؊f#;YppӞucf1ȁv'YWos7.ϼ7+YCGWogO{i%=^/O!v/Џ~-[ƿ?K^_o-7K`K~S #<̄~vEFs475@788<<9֧ӀA&PI6u;e H}?<` gJ6C퇁>p?SC Z @0=D$<0S4}6S0 ]#م-x ]#؄RP" X@0@ k <b&Y P+ b#@@ P<LGR@D`%@QP$@iV v4P}}ImՁH*f fXx~-ȉb*@&b߂ '(|8x-g@B:DZFzHJDZ# ""P:Q:RJVz OzZѥ^`b:;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionAddBubble01.gif0000644000076500000240000004676510534177571023157 0ustar paulystaffGIF89a UV S!!!XUкffj~}mD(mfff!DŤEE/X*:]3ֳrf<y))(ff3ᐋ–˷̵y#Dy`̽IJJ)HyZZ[sлKInj斘BBBxtzrrrVTQ333ٿG"}fffSеŤG||yƱnt:::Ϻ}xʼnٽSRKdc\xuk¯Ι̿z!, i CYCI'q"^X ;X99 77c>M>Pp;X7I7l "\- 7>0`8M8 `҈ $dlbA>Ȼĉ#҄f$3=#F8À"y#lZapBʓXHIȲJ0VȀ+s$i 3sĉC(P&&\t oq,# `2ljॺسkν{ߺ-qq%͈|1xaݹNoKcsA4qD@qYj7Ar7dS'e 1MhMGfV}0bI׎p6[(YD5"N1@ @)DiH&Gzb| ah]z8^e]C0cd9ʚAYdI^8DdK7 !NHvAIDbj衈&fRn>tvnz]~jڋꪬfwunxCBE_7D뮼01װk&6۬lVkN- Ayt!n*kn{]P@Ahko4o4o+ޫ4Lh 0nqw ,+ndmKS HԼǩ2Ϭs5!*H*|P TsQGgaUs!AN eaga.-.Dm7.T8 UvaQ` !L s0ngw!D)B ZB-]sk!khF h- {@4+4!i!̛TסA-p'C;5Qn=lj~V d d-k3p_``/h@i2 9A՘؂' 9m~0e z XNwӞw?X)fIģ{&Hj:ؒ Bإv7p, `  CA36;YһxeAҘA Jb:HPPL, QZ<LBZ59 l31/r782~apUpm ֡v38$hM.` ($`<˺ֽVu-anˎ8!8jJ?hnpmW7)H <\05!APmq`2/nh{PFM,q΅t]X 18Aa=1ycJ RH't0G#<2SP0q\9 so]H.!X `ns7d|QNZca|q\r],PwvYdw:,؂,L4rG^D%dfUR53P~Qe8k`` E7 V8P6=DGKaCnzk&CDCviNGEfES| 7sb L.|p%hw&`e`PG0/؇0W:8}8O)Wfu?55Zp`fQ7>'oS#EJXcUvK0~PEtenR5֣r(ZЄ `"hlw`rxho0WX8h7BT=]k4j_k~LyVNB dbHHJE?6pRdt@pOc樓!Fg!i/&PPx`ݸ4x5uBX~QcyCoXL8@extOO7 U`a8TD' `i(nPT 5v& Xe r/ 3(4Og_:?\ד>_0QpSyX x$Y`P uؒz)3sTEj$֎QpcĘ8N7d9#oܹzUgZT=JTL'0=B@9.dW6c:F`35}he$HѨ@h~*هȒ8ʗ35x_?PUsH( 6jVJX8``u'Ycb?̣E`COj/5zHi6 L:jR/i7e^5N`@hb9E]i6D`5@!6<2H@դI:/4'K_&>)R3T 79cd*wD z;2`_hۧ_+Ĩv}S2}r}wg٩WLUHuէ_3QBE4'>7WMyWmixDU* KE kЊ~0׺&w[V%Ǚ?vUCg8TIRsT0EJ99ǘf(kc5QKd (0DMպ_Q@zpTeTk;nk{76XTpZ0_+k7OBz[bƯPùc bԡ׻vЩ$L(0(*CX012M<,3 N0(T,| К: LrwNvc69VrWyPdDyIkF ï$9Ap_Hipu=\z@t _Љy6PL`DTCt1\K|V$g@@{ = l7e}9'<DR=j&@EûaÍ0,DnSRfBVcyjRHn#ȑM$^-5<`˭ߋi}OJWOw9-3݆^^JfPD uwZJYkXZ\^4)> V/L2n9^ecf5Z>|yZJGPeHպ3Y]Fa݄6zEU[9N vDb$ uJnkmh5PgN86@̈\mjR|wwL`KO`hլߗc5@I\b/oTF 9!xT*|4x u  BK^|)N9@)9<9(VXc FvP$Ţp6TlTQվ<0Cv~uLȗylڕB;UpBg1D=ȀTL{- CCV܌}Pb#_0N'8<ݙx&WO#h KxzpFX*b}s•(1wxyЂ)b 92gP^ͧKJ|e:L+O$rSN GZgFP(K4)+gVap( =<W0 <` v }#кb1^ >`N "2Bsd&yɝl;PN(A(2h;vܼ vAdڜg)df&ݾ]D6SCaH̀J~1۝Aat>eWt "0N_-dt.i0+3~5lMaëd&G\ %qǻ_eX\Cϳ#^0i50qe>gs$}oՏ3p ĊW~DXkK hW\pI-/Bwrsޘ2s3Q' {HۥC Blplur|(>(gV B M?}b-C7jj>zP>p+Pn?+i^&ow 6/AI(>b/Tp\plIhrPWdcNEmNQ1\`ぬE6nQ+xwjw0jf%oX*H6o>e{]6")LV<0j3 2UkWg˗bpcC$ 7 N9t/Yz&gD7mDb n`c=FƐyu( ?A$@LЭfǪ#6P a£Za2\05⪒V"[y1\Q{;D|lZ 30;J$%rI?- L:j\mPw@$$5Z] ~ n0)=2?!-D{|5&t&G%+U Bq~0HsP߹Wʵʗsy}{uꥌPϰyqU#&N^ڐ:jNkɕ('cvSvp`Lzʲ雾i_iI$505rYC;\0k|gɋl7X8Q U%H1ao@@ v@ܪ# .*T 'axhb(1 g0a _\8Q#\Fx7 V`5  q@k ?esK#̾ņ ԐQz HPN``8xL`7LRaiz&bi:`'Q!1َŔlɫk\l Ơ SNapøLu:9X0i>ao伋pxznjɬ1||K0kn3X[ʷe"*OR߀*KgюS&`\L&Pem5/K{I:O}X"}e  P P % e%4mWu$Vͻ!bƋ`AJu  ǫN Յ~ -[0 P6\miV}a= 5 ise *A`QUH{P+6Sѿ* %WORgT՛˜/ b(0)I;B{EDNk䎬& r`҉҅^*M苞-9a.wxQ?m6~pȨ%i 24@DNF{5?ni\ϾK =t?Y  8`HВPMF{@nXX,X>]-.nn^.m ^ o/?k~ɔL*A%o$)'-&H@3@%#';m*A/C]@G@8O,@V`0d[L²ɣ[ a/a'8(sf]oo8o0sop8hfpO+~dVXpno Oܒ/`8k{`2S_8*OZ+Mo?8{S`2H0*o<n +`9o/R!%` b-Po-'mphg /8e3A%i@`Kw8;,SLSBBSLLJ{:k*HKoo % %%% %x  /mB:-{?*mJԧKS/x%R xۼ{KkH-Z$g$*$m4:hC(YQ2KN_ hh%@s-ɓ^YYcōˍ,5IH QѳW6. J40-]T(CBBJիXVE⪫ׯ^JV eӪ] pau砞L8\4촓'L(^̸ǐ#Kn˘uL ΠCVћOoP!Zv#pӅIͻ NhAμΟH_COF4==EMDӫ_Ͼ.(z^*(&`xPF(Vhf($!H@=w3(c y08(&%z"@P DYɰx!IhSB$ l^Xa8AAxMaLX`԰Gt2!#LRßV^eHF>F"hO$+ap)xB+QCV" 눊jk?𳆥>}*k+Xeʬ>+4 yHԐրvZ@ǪܞN{VmZ WpPLal;!CT`8|wUTB<w̡(81GA 3NaA8ql~<#CIxTKM@K5_οѲn76 M (pg>ppbf * HLlc ^@ 0 _adRp@I.s;!MB(@AqЃp(y` hP<g3Ic4 4 U8b\л9GA3"P6woR6|瑶)sCbk:=V(d|#f9hL@*|FbGB6Ʊq}#\0 Ѝ  rH B<@N) `z vF,.ILoFh:h/:Dg¦y 87'D i0gpN]j5$ Hs I!kr ŐJ'-Hgޞ<єvoEG}RHtl%M#=< uBL4Ac!rA ǰn,O@mB$%@D3rKT=3( MףL1kZ};"\vc?8B`}!݇>oFb d;{ 0j[څk#XYZ&A  B8@-tUpp6b-׏ejn~lm cy`V(x S1A T7mEy6R dU"Ϝ`5߱'QUؓȑLjR,1XXϺַ.)W SDqa?z07#rGiOQsuy9cW0kn%22s_~2;hh8e"Yav87=0K;!u˧lu@ 7 &PGC\V><!soPwmr DhuBa2ׄO`8\k'!'iF!R$z(G~oYԊ.-p" zhkק'{R.e!FA*T!r@8SRp)O b6Pu$8h!U0Oxʇcf-@"4Bs {*&PzzjGJZyJ21wH:^S!1n'\<0~'Pfu!c1TTQvpI"6e ,jo +ኮZ r4A|(g=)wsNPz!Rf)j!Iof`.PbU3n 3)@ ѭ&P&x&Z`P&k AD*ie د}txDW! [oa2+3V3wN@4p*%" -!ˮd6  Po`"jr(#/V*uPbU"ip8wO'S!b1A >mPWpjE*anp` l r <` nU$B;Ki#"+6-Z=;O+¸bPZ!<kf۬%b8ùfZyar⪮jzr;C"niƻؿ֣3Mb8fjUZ5 ;! k˹N04 %j+Ѫ&*5B/u:+{\`ڧ}\ 4=I&!M*=ިPѵ=0P0`1)L0$m']ϒ>C4dC32 !.GgV~'}33Vsxm$eΣ[WkhZ^h`+Ů-|OQ~Dw2 dBI8{3<lqa0N`l+XAITot#/Ac5V(_ 7 R>"6` S=9K^-gnѢhŘ|PC7XI+1{sx'Tb.o*#b UK֯\G=b];3S2_xo~M2+x͕>@:ރ]ˑ +̮Auƶ7X,ùII^|-`ˉɂx@H˹MΣև6f.iSy)2!t1onS$GW\5H{T0? bkkХb&l oҪn}f%*eƉl&&SDZÜ;=j/+nO oJ Qi@#7S=--GV 2j1S(P(`}!C2׀J<V>x$Wd62]Tԅ@m"Vh /i9~gF<-uPYESUXEEuVdf PYrt䄃ZjbjY&U%urQ@q0aD&$4AB+$;-&4#NO_o@aWۢ'N)z'\%ĈP8ꉘ'4H 8Fz V^1ZH.]|F%m,(+v초$;@rEDT~(1V\{d˦g6|̤P#M5"9sFVFH$ԉ@Yaj:#bC4Ü99M@T@gh)]~VL5aʲ o7-$ѢmM5f#g:ߡm][~syD !i!g"q‰a[xCc~pWƱ.un (|B a <lM_!8BL&S$ȢB\t2Z܌M\Z5iGb,Z)}ig~r0^lD<2 DSf6D'!WصPGN\kA!k "LɢX6FWjᘟpN| $vq8I8JqBp]'Q`!8\4f9+!vp1'kuṔ:@ ΐ$o<&B010(s[WZ ,<fU&qbGQ#M#Iy:>fFxᅗAg6E*3Xhk  <@iR_y?Hm.GnZּ>A2$er\!#KG`4uP!JiKg'<k9H`t7{=EJ3dl8RfFj7 X' 8NޞW9{DAnfcD~= Nfs=6c:늜%FQ'XP iЂLJ.d"ͩp"aC']Rz;@ khUpoH k0,pk`i'*Ng*t*KA% dJ$.A7LCExai=hO.xg$c-06$^П6,K(by:%ׁ 'ha_t6 ӂZdb L*p~э5̆n x'P-b6MBS:%CC@d L p:pdqLx@+qLTCA"P/첗p{dP m AЂNKZj3t0'N3h*æs@JapeءR7˂MUf%@ȅ !Ck$#JԤ*uKBԤ"!(Ej Tbr ƃJ|*P7Ĵ9JW:ui A0h OT)vBB0TV+!MC/,VwW_ sk h%$m-i@ȺֵV3kvֳ8"JP k*w W+r;4bd8[ҠW±.Pw-oy`^`neuչZ! @54׽ŮUq ho JxKEBLת_ek0;)^x,nQl%c iD[pݏk HCaD.d9 YF6;HyT,ky\u$d.s`4٠]C_`&H :g<0sgAP1@t G  iL`Ӝ7PzԤ.u}?NwAmP L XZyHuW=Rw:AàM`(/z p sGtg{oWu%p̖xuofZy$@@}mG-/1(,`|pWn߶mNxdGZ1WtTgsu*_9_8rQUtDfst{TXs^8stBoppkhk\v`0h !CpsgST\Fs\Ȉ%n؈(}@Qȉy@,P]-(@Xmp(H?!=Rx@H،רȍ(Hh爎H;(Hhȏ;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionAddBubble02.gif0000644000076500000240000004461410534177571023147 0ustar paulystaffGIF89a LKRRU۴{wkff̣'J#(mjq˽wws==fff׷:]fzy3!Dfȓ<msb[-+Sǧ !===bm"Ƶvtrkƽ8?|SSSκ]]rs[b?z[[[LLL3S򾽶W@[YR )))"}F333׽||}auov}ꐔ̦Ƕ/XU[zvտ33lt{!, XIcIH&Y }}222MMF,zJM}M) (I s"~C [X 4A 2A>Bek8!Ȁ9kL|H>lO^xt aM l̪]I˰ 4)lQCI+> ˭ , 9ƍ (P "Cr OhgMY<)9.k>љYF!Yw1A, ?M$W$BسkνËޡ1Ib{DF,)>7KMko$ 5Sd&\HH6msLCH4a 2\j]nd6L8tBqCWO\௿,p'0 P igwb`G!B u!To$|74`ng5\bop$P8aL@k0G@Y*()q\ WS^\Vgٙ wm 4 -xl(:jj v% \Q@ d pѠo"5M  P omK!. r@HǢU@ =CG6!utZP$9@ t0p0h{^X8 paFpKX D"ap"@f0 m|+@7譋\ ٨'D"h4h$ֱ@5,Yʤ..X)Gӏ,{`@B. u1|A #B?M 00@r榷 3 gmܦJW6`ut^jҚ88 *S P h5"8 @E+РgbT AdAܖ6q!6D=h4\ajx 4H0ؙ`KROVų}a &;`mP, 6U kK@}: 0VEϬvZPAn- [3H!vͫp (Pٽ 'lr-6 ݳ@* =Z 34T~fL XP'pTm_(AZCJWjEmo\ZxEr\,7O@  rx Rܢ'zhaҳmT|HY-xm+Py |- e6`8pD@?a" aַ\ 'wYPp:`u@AzíRg9 5'%@V>/Ϊg}a5J6 xoWffiu<; i YCD]0Tr(Qu797yPvJ;$;T jK:\0P7pTp"@vh ws@?p:8&|l6cs!e=2];fnzHVoSb9}3UExF^@T^{sn7C,FdfLTKWj{bO us/T@xT?qBuo?`%'(|Fs?d5Dtq^\86f80GkEs7m"`?t}48h`=prFH@D74DRGEXxx<<@7.H)hG+y KlTQC$=7VtS49xEgIxOO5bi[>lX@ yMVP4pXo"<tyT! /"@,9(X8Ӌj@?#?7dLvӓSN0GC[|sn^Lc[KٔQPyRi}xsn_5d *Օ< wv5 <՘vْy6gP5Dm@fH9whw*h4Y~8X~~OX YM4EXSh@JY*p"<@/T`!Z#'ixXG3P` Q$<:P9_շh45v4ѹ6MEiיKTO5vZ75) FyWn&n؞BE<0? )TPuٟɍEOg]0kCLCCugs dLQp Mi7DJP5@Fd*PX6\WPWRqWr5Ȍ\@ Doɇ*Nxi6:XS+PfǦWuDEKp%/if\Xod`65% $pjDj4WwJ98EeB ^pzn$3{44Xvv|cm-uY=@= D@TuP^z4g p}^o5}xhL-*- @Uvcjj痔dIEfS55Wj1ګ9VwC` ^"M|<ȇ;P.wf2HZ8F4]jųCuг=[Z4:PbG5?DD #yTEco >`s۫wh+Tqpwl0~L0l{O`9.:p{<v.Y8sZQ=  <;YH9Pq㨷dRf9zUZpuUD!JG3h<+h0{R u W}g7{v56I:U=+hhu@o] 9#6$\@FQഓTwe38dcH7UT9W~8'~~h>DmKu< Þ;l6㇦FKPx` hJ[ab0Þp7GUXU0I_Tb|GxbPN@Kko Y{sfH=K4VCU/xn48>LPL|ĕZjlɝ$$༉ϖC>P]7%TCW6}Pə p@ɝl;N!7ܡ"9@mB=8YrGPCvl4DƉpCh A9;60sJ:|}|ڬڮگMcEQDp ~ S^I9ݳD@4l ^ 0YNݳNKhͨ.t0w`JJFw9P.dzzXP ,Cf]^dy$B +94@e`ZeWq@?iJ;Z:-NJpFŗXv+MH#ه=:E_}COM^KafQZ |^pSVP3ʺc?Tv$  5=;z@Yq@j`r`wP$Eh0=T|bz>\VCƽ,4&u[4hsSoo%R4{[e_9MgI`{b"F{`mGdm06H2f`@#Ic`eiBQQW HZjJZ"7 뇶EQkJck&eVi)YSs :W wFUfhh Q0c3T120:`C( 0HР-2uɀKV!Oi@a!.eXL+YK\)^ #R(NtQ!),DDTIl9p@Nͫw:rb$ & NA)#i$tiDX> u:Ѵ'4ZWa3:&b mNK`HaנbZb@jQ4Eez qr[S"Kܶ_ %Dl5!&b|!nN=^DPXv~;Z`} EL iС-@# 1pGG}J&u@Zq/0j 4Cl@ $^C[aj\vj=ihr#+k .D6wh4oJ9'#J@;]6bA`Zbڑ=8m&+\WR.tg̦!Eߎ{nȆ21\ZQFܱ72V_NuJz2VTjIcN*L*nEV u)4)d{y%5(<-oJRg(p@^ˈpV B $%8YaŘK@P 1tԈ(#+ 6p.@-فJh7f(ΰ"#Ud,K!KB%qkDصBO՞'qOC1Q A@ W(<k&H@yb z?J^䄬PU``V1aj۴T8=4TrЃp_ )^Ay5<ȭ hV j@l276~K֑0P͍:BR=`+ ˘5arX/Աd@_BKkRe@6'GpS;wP:#~T=OUpa/|4r . &-:4JtaUf5UVUAd7^I{xF gK OS 2i#iE,bVuPTt/tS ;,`e/[jv,gH/E V`a HK&Nb) 6 FH7%l  doP(ujw`Ƣlڀ +8C< wh z-h,IUi_I0T &tKx/EYs"Q `)1*8L1X̫u=`sb3SWI`ut+`aT.,ky@Ô\/wTF[KxeBÁ`(>@FC0Ҍmh&@!= ]o8׈@5gO6P=(  ( ”Q=dьNFdrJ@36zL.n8my?l4iۤN{9F¨@Qw{0݃(ʴ7mZ5D֑${4f#sv"I/n=Rmɱ, nϩ+us3pHS>>sFYք0OVZ }PXX'w0%x`"cރVs _ iha:u$'YmeRV8]Wx(e8nX 2_\ |w'Q@2z$ГztpZ#n?d20:@5 vy=DM8EJHT7)`PN `@0wZ2DD=Eu=e`zV7c,Diya#Bbw~Q u0K4yp2T;IᆒIqp9$Zq\owKM!T|24 i"!fp m@8LiN5@\KI7z Q0RA 0ZSY (.h  7ږbZxY8"kMQ&rp!N@#W قP@—2zW\ʠ `\џWZyPqetKIt@-9e঍d+`AUHhД [!*_eV jE194LJ"?|qm@m:mP @)]pgPeI@ 6E@+Zn _. {= %PZb 1~aA+$bmZ'/ [g`z\pq g6AV*utus$kIDg|Bi] Qma .dt[ib;[$*)l;Tq-q ֭*ੜzy/yce *&V+)$v%`"}'[4 Y56 D!Yg4bDj- bֲ}ci]UN1)bйjz QPdwD~Nɯ틧V4 "Xg MdR0sK&% *av B @,U*P 慞q0zZP,\d:0\4wz¿Px1WF1ğCN$5 ci𧔁d' 90RLKkMHK栞@k^ QJ?絎ں :pBiAd'ů:C pp ``3ɒˏll ?5P 't+Rst" n5I ـ$X`LWZ5@m ( c,檒, i/#–@\s0ih'՝iё\`7`')UPT@) 7s "ɚ1f \Z ˠ/ZA|)9o @N) ?T@7[QT\K%PJg` d @빽Gyv,M}T@!!!&o`T`kvݴyj`t8ռ˾ 8SZM_Ͼ&N=xl7 t0e`[uUVaVhfv > h(HFHPV4hc 0O:`]usw޼OwNal@2sXe@5\F :"pA]D8!qe'ޯ7/T_@EK?cL`+0w^[XAuC +PHCP _=6_Цuo)Krd m%outpaPE+ . iljпf 04D \ и`8q|=@L+W̗/H: b&ߥTQe,pcp? | *AO@B4Ue|HLbBYt[2TL*x  BZ\$0{ܣ^y@ Ne i#'02`א8A*ZJD,,eN!GZhueBK6/ɑ~ -Lh4\B#gN)͗Љt*pT'XC4Xp" ֠\#+=a^ȅks%:8(, B!W9!Sc6Mu'ȹD@8Ș {9/ +1<6/Rqtc~Ha .X~@IiEeT VrłP40CM*6 aqxBL5JUJ׹JGk^OeW浮v6zʆI+Cp r[-,Nԁl3;7dG@ H@2ou @ֺkke[`v\[6Q+VrD["ͭIlp$   e[(Ֆ@@oZ[vm]yZ2a#(Z!d$Ui:≪k]:`C]1D.+]j ' &GLb"/UB-Aw d B b $Ga)0yo9p)eNʔ{*xbC 2q?B&e4m/ɳ%8-D_Y Ԑ(*+3,4Χ ={ڈE -'  HьP`HZm Bw٧>M`U1/!Xuum>޴Iakap mL *plP+i7 g61؝.ϭa P -R$Eh^7/d0_δ;]H$4k@'s d :^@vP A0ϷN ENh7vÑT@np F"(7`n;n;;!p)a@Cʵܛ,Q 6 Q07 LTC>x_ZpСNLk#*졐qX\59&ykcF OGB> wBwvZd5!t1 m !*p]PkguW'|ؕIBm8g~X|jw|7j]0;56"NJU!Cb3Kq.!dww׀ {O`G~8W/ǁDD?EKJ<$Hb_yQX&S!C"0Lr2 Wfvu?i_$nw%5#4"]"-n(U]NM"*B'hc;!#]^4B`6`On( P@m@g=g qPt0C E-G|)b#*b }41Q^Ͷ5@ y Pd!6;(JfSj @H$@_0=@Np`m]q+~ybS5["@;iSy#9sd75heCNha:רr}qG q4UCB>$D@_x2zDq=B9j2%1Rxjj:\SjBP9]?`|1JD5@%HB" I!!S2?:pS( A#A#/z-!6J^ȷu9 1yNa@ɣe:4g8NУ!$j~I @|ڧ~b (:~*+i!]`:eK77$hHjc)VoKXEs!u!Cm77.$Ѫȧpil7)Q $7zX=@7fB8iVM!&98opT$p!@ oyp|a< N@|:IG8J sD!'#7eqVӣ!&ToP * o`/6!@j`!@@ %BbJ[-C7gB}:s; KJL7S7:* @j !ۮyP "6^>w"8zI"+5ts#~C}| #᪰TZ{*I/h6[+hģmSRj}1r70[ %k;;+]e¸K+il#?;KŔ`.ț(ٳ((ju!1vi<‡Jj[!t;SiR-Z*F!++?oN401Dl KIW p |R`s!k-[&s28tc2 ;lKR@Tw(*,LTi@+b-븏R<<(ٜ:  ^r+`Ux[z<y/!+3E7x1l#2Xv0/!Ql&6:m܁j0jm-񋽯}&1s9U;=|#+ΕD4HC $;`+֏py?E-ɹ/%*J\i=+ s8|=m߾Kݬ87BcYyJd!%?S)< d3"܈jJ$By(˕!e;=6~E)(DWx;qqcӃ<+"s"DF@K$Ё=["n!+g )R;Bw]0m%U}X)$hFv6b=Mdc+@9t@NL'D1]p(2t7_!`C2P0垘%#;pX1IÀ 8HXhxhp)EYYE#bEYRSCأDvQ0cVQAEsuvqDS.>2fpξ>2/?(TϿ"ByTLDΨ38ʠ<4.yDsF"zPQׯ`ÊK'(6\JfkC!qD+;Rw:=T$IES Й<@vSA!R%0aČ!%_~\`B?;(]8~QtZ&$|+)õլ%d)Ah81bB =pqknKɲi{>8ԫz ?Q֬93z,>=V:A+S, „A _pYNR]/)|1P d:dcRtTe9%މJ-y|c"c_||]|( А H7v4Gy Aĕ\.E?AN< EbIfRLFY&%R))DЉgP%]W&RLtf)hJQj.(Ϝfb祚j/Ri@OYf@KT𦉛bb鬊\Ɲ*avl"[E";褼Rdj dKSL@b枋ci!-T;- qZ\i p"ۮ*oVk?uo\qoq7e/<Љ"[?`* oPAs6ߌs:|QZ<TBmSFtJ/tN?4qV_mXom5]7CoX a6<06k p p~Wp3߾@xux/x?߭NxPDy/>y?>aS \Cy$ ;n;<}Κ;@T!ES! 4C<}C_A_t}}iX}=߁d~/'s<@<.B:م`#۟xu[*FՉx{jaHQZz>(=A ,~jZ)ЛYrC}4~B~0.@&`? |9hfD17yvhqOYPm v+HP _7#bD~ QU0:1BnwڨB!vsŒf0L3%mNȁw1"QPth8`#jBbZkD[ PHG.P< B  kr 7D/G:\!pst$3Bюu`[6@P̻=їj@`: uʳJ)t\P%,%HtE/QV`0H⽀Q0-a4&"\"*1"%J60D^{=kzh:ը]St P@y.^V`}lmߘujev@rX_ vr!XE'`e'KjVleknv-_=ZӒO%hk_ vJ[ⶵݭo w-q*wm.rJwԭujww w-y;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionFunctionForVentricles.gif0000644000076500000240000003157510534177571025476 0ustar paulystaffGIF89a%$&)(,44798LR;GGHHFCPOGUUWUH\\[P`^Whg^poUsw`]Qb`Uccag{zpokqqmzyv+ &27&)999 L J(DHÆB"JHŋ3jȱǏB)$ɓ&S\%˓>rQAvѥh/H*]ʴӧPJJիXjmZѯB ay ]V(nyĝn]w݋޾{\ '>1dž#/2;0kٲ̝A96jVz%`q۸sرwԼ{ \5᫑#/.xrYM}u؝kvN>u5[>ٲdiN6iæ #`7ݐC`7հA|B CnڅZv a #H"k%+.Ƙxk4hc⨡=: .vܷ_YDDNDH L@ Ip6Pȗ0!D#|@ :^`bι'3x&睃b4ΐc 8HYC (.b~Ytu<Q(AGP %^!yPЂ;+-h5IgRH!2!mɚB>8Űf-bmk!~DiQ4jKʻBqC4BI74"C&5#)B .c#SP uCkL4uЁ2vJ gP. 2HlÊ( M伮QAGr #C#c ӌ "P0 pb2_NGp-SԠ1SBQ_J ekGתK,2حZC 8Q~Ӭy4\9謅>zMQ F!m@7LY.a~O=I?o# OXN̡L+~.P tXWؚ?\Mj&( R 7hApPj> vQ]X& KϘ)T Y XYq@! A≀# 0y4v(hƒ! :CLޚЎf(;ț|bV+a"# 0,EXGG?`$%GVr%=GNl$(APN҃S ]'<&IЄDBtq0]9XD41ȅH]B 5b,%L3SC "A` $!(a}2 LLd-\2gʅݭZ|[/C.0Zl>u@;Z&jek'^-wɮbKװ=oeP+]3IM7' ]p!8ęζ-[{€qp]"dW`"np6NUL]qM\ 7~A b`n 6$P]8y2"sd +2/6fd+6S͖us|5oy.t> ynDh0uس1E#0A & !)QNgvN{:ɜ3iNzӧִ.9%ZQ4x58Xֶ5wk^zJMbu]kմ:سm f0mjK%x4L@MrNv~Ÿmnz7M9 8;'N[ϸ .!l`H=A򒛜>T򕻜.aNs}0O9q򒯜?Ƀ.G'ɕ.|M9ԏXѵ~fOpNxϻDn0/O;񐏼'L3{[~4hڼ,?{-`vls謲ZL|c=ӨjGˬfwllx2db.\kf=^)֗譭~;-v^}ޘ-қ^$x=sڬ[kn~>.^K9l>(췮ޥ)Ks-~NNg..>N)zNn~=Ξ~~N!gy>| PZH M|{@x^^x{ ?3~y㰥pҐV/x u  pߠ `vQH>_xmP  \v]@@N7 ֗_PP  @ XѤPxp`h``Hpk؝? |0] pv Mٱ 6NGp`*T\x7^~esz~X` /m \/[-w]ހ50g_;X^ Vc62\EvmFUe=sAyߔ[>e; nhf>.W.VŸeiAdQz䩡 逯>^ '~fUu@-v5xQ}vye;?_U(S w *KH:UIbve>{ޡG~ 鷱݂HD6&\d%-JBR&1INGq P8$TT@{@# Q:7ˈ!0vnO֕3Qi̥ \Y0MoQP$k^dO{B(y9<|l֦JyZg;9wsNdREawf WX!tXCgO+;$:]z;3f=NwOPxln<UR'~4fb$L?I(%`KD#(ӡնRw1xsy#GNL;# j&l4p ~7M,3]]UǺvUg!+`6R>~h>^(42Z@+PM,ԫ!+#9ؓS؃U˪ и@т ,=?pߨJ@ {T`=0%xaKP (ײ>*-X2@KzSá>D6P (R 34 B t~(p [1 :",{Tt RsA5KT:Ú1 B#%ĚBBԘ8&r"V(#J%#p"( Xc 10#0' …+ ߃= 0?1D-z98#XkEF؄&s~ȀU̎K yhh ;8>Bl$`kƂH2h>LBLeKoSR4h HѠ~ȄZb+\VH˄ F ِq1Y+i8TD H&4OPNxD[IȦ•}h+qɦܫi #I hI!xyo25'-s򷋓]y$1G"K\uLLLGQGKtL0 ;nJq'mMMj+*QJ'ʊ"+)ĚMb $ڼ͆M= 3ɌʒAM&N HgB)GMT̐KK-:NM0<)r NĝC6c:t2|λ${EεC O8̉KΘ['|NTD{eNBxA=L&OU8o21 uKOжց ׂ)>RfZ*Hx(}"ּ~tx-s,˃.!8ꈒHK ؗGRCoih (Bˀ+ J6*"OE}`LHL5ߨ U ix΂tPL@`<"+5PX Pt !2k Zձ;Ê iĸB3;Q+كM )0pPI}1mp(-" 狵kP D |ȣ`rײp% `}4p4-M:d *ruI+~Ƹ`U9SS̬I =ۏ_1,GZ=ʱ@JH 9t/l ( HVV`tЃUNUδ?O[h P/ux H0 t5q.{TWؑJxJ7t*C",-"7RIи%yJVvx\I$thr[E~h!MY'weΊK`V`HSใm`M >n` `vӂ\ͺdbIDxY=D×sh&)Y=݄*j^x@ tPE$wNhgTK,D= մcuG:IIҫϕF*bh:~B- 0-0ϹBPʣAH8 K@)F5+NJ"fIElL1H o3;m/E5 B֍2ݦQHi˶{cIKқzaJcU=p 4yU؄ 0: K, K8y|:uRU s4  $5]2EF.Lc)6:f9柌|yHpXd˵']i@+\#M rBOs 0C'ֲACc1@l&5Y{^gو+'؈VyUE ڋPhE +E ę$&@bN^ahlHPP>hI覭.Fև lURsYǞ-TފfVCmJ(},V km'B ]˄hl!{ [$TFD''köP]lB5\("*"PLG ]Mp!f).^Ң)_'r:a75rNoa1+?TVO*p+O6#>PGQOujDsb5\],5PQ!đm],O3q LF_wT]1ULO3E:T:L>HC^4Jw/?sP}2Es] *1Tu^u_u``/vc8c_c-u.tqa0bvmvocvn'pwrw_(sr?pq?o7wyvb<szzzwmwDO|^8fxxxpO{腋xpx/tK]xbvcyyy~gzzo&$Up"=tp^yzGzzym{zGz_{z{_0{]•B/Oz HaOXz'|vMzN/8XN |o>gO꫖G|ǿcP NKx1ЄH0a ߧ~'O׏3^H1`^8}~P`c^z 1b2e_?!#x%n8qbĎ?$ɑOzlRɘ)GBGP!N[$$0@4Y20 r<= q)(mI SO!Wh2e'.4/9vۄ>  (% 5\lj!71hAL/Y[XDYw7$lxCRhˮG=F> * l/T# ,J*,N\A|܃bvlOϤ_{g܍6bsuvun7p5>VDrE^/WMAorc"꦳lzJ>a;MN;4mQKBFL#lr8 W^|A /O*-+v{W߽{˲\xesʋv {sgA+lDf9jQhe[HjO⻜a,|! c(Ұ6!s>!_8ŒÅ0b _D.1lbYD#RQ$0Xbx-^Q^"00E81`бu,`챏v#(H; I<2"#)IR$&3Jco$b?Q<%*SU|%,c)YҲ$/}'@*ЁLP- }(D Q6ThD3ыrt(H7ё&=)JSҕ."A%(!4nS# AJ)PEiNpTTM-jOjT#LSzTtP%jN VFdkXVVbu[ ״fq]S*l+`+j`(,a {*]cXNe%YnV,g#+"-hOKզl@;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionNewInterface.gif0000644000076500000240000015512510534177572023554 0ustar paulystaffGIF89a{{pDF$fDꪯ{!!tһ?fff#UR[ZWīsssfscSQBO333riLį+|JJJޕYa9ƾ8֊+Zy˾ܽ3pp^Sknmi՜k噙!yS妦[jʧȴػ))(Ͻdc[RRR:::aaBBBl- װPo븵cc@3Z১{{{ĨޙĶ栠!,/GPPGss'+C,'PGqGf0_P2޳އqL[ȰÇ#JHŋ3ȱǏ CIɓ(S\ɲK bʜY̛ jB9d"DfˣH*]ʴӧPJQbي% (XDCX˖mBϜIݻx˷#= e""[1P@|Q `)`b]p\} wT"I)YaH0dƍHYǖ͐34]gxޏP &E&06mfD5`C`gl} !7U^@t\d)!ϥ`PDCك'd2Їa 'P$`Y{VH QZYo)R;`7UW@EbfN2R F*) fՀC=1P'H԰)M A)8Fo`b =Fo|fD ar  &kVnjx8AP,`=|C )F}2X 'GDGt`k=D1ǻ=Wa.jXteŴs^ MeTABFfmh~+e7 j>Q ohG`m,R? 0/Cjܗ8 !fh㦎Ũ+EP ztoT|7pjo!3z3iXb4 L&Ɨ=`Fwyg">5?D ^F> o d0( :p4}oxA8PAy GH(,70 ΉW]2GBd. qGɝj B#F`T`x,Dk` `X"PxR@d>'`&i ڀ= 8`6.QjR3J׺٠4jhU4i2aP<8`g4VR,p *6HA ; X ĀB5CY6aR Nfw|0BVHYȡ3W}<[58k!pq]OeY:i}򒿡@9L4Ϊ:xpPT"Tq<FhѠ$g3? 湻-2*&P8ֹ`@w+Vհ>Zۚf02nk)!'P0(55>vd#`vji+6>mx#ݞA}OrvMzη~ϷNO; Xϸ7{ :(OW|xgN6w@ė-@ЇNHOIAPԧN[X:Ձ h`NA89pNqvOO;; ` ϼ7{<ѮvǻU]NP@(@wOW?~;>-O[XCI {{OOgih=l= zHwg .HHYpxh "8!Y aa,؂.؂cP{/(ZH"H>@?ȃDX H\ЀJU`gyVxXZ\؅^`b؄P8yx vgznODyOlzg>~O|OhsNk}yw]J0JxHhpN8+%Ћhv\'pDI@HNxhNԸNv(`mmZ0ܧb؎8 8ڗiXznPy@Dw0y`O,@ \PD] Y v՗0 {mW[؉:<ٓȊ@ؓ6 pLpp7KH5ىAi^iBb9H~DT(rP0xkٖ=׏$R ]ODN7w ypɒ,@w yz|y|0 Y8YcDyGVٔM7?YPV i\ _9Iey1Rnyry18y80ɏn7OnYw(wZ$`ziy$s'sH)D`IF빅ɔvp%𜷙<8i9X؋9*7ȩYi%h-ٹ)kRcpw9XKڤOk(է] wNWar!|`ܧ,OZ]{v$j'Z,z: 1:Ki7@ Vɣ=J(XxHjJ討QYWzng,s|БOn%)Nyyy`h)yYاr]U)H::Y1Z* ڣ??9FگBZFkۗ h`"{˱v6@lGwZ|py0}菗Wv1+Y0@}ȡz8 P;+ؔ.a>H ˰YىxH{`PD ;\`o(;[s{k+wizڅD X;KIa+PIWap kx@9}c ټuU;yۼ,=+۽;;$ ˅۔Kٺ+Pf+K{Rn p닧zں(Eˊ˅U@K\s R  "<$\&|$,\J L+J۩{a04 lx-[X@@T\T<Z܎RlVswjd\f|hjlg[x A*By5̔;c;+KV xĮ@áxx\^ Z^|[ɠ܀`}ʝgN-̅)(([Ay|.jFi"?IH"9ȴXZ)+X8^š଀őW𹛛z)Z:J˞b4ˣX܋ ͵8\|B\.k,wK0Jx#[ ܯhi[ha98d{Z<  ݪIP܊9<śLX]m|w`; tmsteMf-Qlk`?$``o}sw+ W0m%Ђ]ӤӴt 킴_ӄ}DVѮ8)K ͤMդɨ}ҫpj ^)_p VR0R._ _   p'r^' ).w.8G:Ӆ\pΘ؜7؇̻ԏJ%pߙ-n}'`-+ e ,_@p8 } n$r^@6 Bpf@.>uZXϰ pKN{:\ʘ#8f1:ZPI@ϡgil<-BrpQpRi'#.~%^'ڞ=_$ھvB΅/j/ônҹÞ( .,C^wKn'@ ^9~zCK : .@0;<'; 5A~mho2݉ i,#ċ|`hcO>h-V`e8;;О@;`xH߇_ ;TxW _o>AگLJv׼A)뿸/ԣ[}XU\{*H~Q TPZGy0W8 xx(بYQHIFb*:J aZZ ++aPUŅdab|aWZC7v5&Ĉ*:|1ƍU\xԒ%$dD0qŕ߹|—LyE <{BΡ9i F ȟgf86:xp(Xe(hD!og fvN DgzwLL$^N3jOCi1"iqz}Z{ʊ>ībaG:< uROMuV_u,z IRd'ݞ-ZAxL0M;9$SUP/  6H+[kfaqR 3w:RD{v2H{ӌ; 8j:߰AaUIoxky<}OO=i\}_?N_,6fK < c.tƞ 3v,0w:0_F$Lc^`8e^i:\E0գqs2,LD OѹJ6,_HuN 'M<ӝB3c\@PzH qCrǩ] ARzv8)}+^ 1-VE WiΈ4 t8qqֺo[5ē@sf=*72p T(5: *q=RG D[@8 B`܍*G1C C -@ˋbcd1&0,+ csv%;phs@Tq%@68+ qi_ ΉN< LJs<߉=>T@ ;OВ@PzTv Z"1tf ^đ@yR+PS$/$UF*9ye-WC=msD0 %ixJ؀Ռog$*fJѬꀦS*GpP9Wbhż.W޺tpk\|_WwrɂaB`@FP0tU~B1j3AA)"z+EF`&ԔBq9r%df2v+e*sa5(eז`VƼ2*6_t.:&Y&0 x݃vl! €$: "dChm@-,`@ijZM9x؄=} cB qhW4݁xq:zxm o5_ nԁknew@ $x7,'Z>1*Bo%~w#5x.K""Tbc] P/|2)t~~7rcwD,Hv-WyB$'s4z!β:xVWt"Wj2W>&0@tvE(Vpg  |Y3ef|Wpl]sA) ;;hw6qD4D ;&r' c KgbŅh34ysGAZU4bDVXO׀[fwpc0s7]7V&*A 5 xqBR3 If(Z<'<9ς e+gYoLeSel8 s 끆bϸmXj TwtvuEua8V`` \P7D )2Hieς[\JŔMi6q1Y$H)0e)U[E3N]y7#iJ% NΘ)ytZ@Z1f0VnU7CV<ٓx1D V,xa\gi_YcRIa$;W& dI^y`׊dI1h jB,UǑPeDz")E̘텒lW04t0I@☙ihBo VGIjpI+UoU)Z5JP/"1 R9r! 9CcypZ$|UvT% cAvmmj|0V1t._UWk &D;2 xε- S:ZyjR4z Yba K(d2# j[@C]tt˖}C8jAfyIhfOZgfqNWFu@iBrUy I@YZ;B9&Dkf[ I1Y@*d|9\I)uZ~D0S!z}f|H鹣L{᱘texǩ8# I6E\ %*ZJSMY[D0ګ%#v3iz:vzf@9%p$}T; ӢP:WK( gi I!ꤥ H+0IB2 [iI 3Qҡ{bUi0E yG2o yzќv3"i؉.WrWŞf5_ܙ_40`;k`$8B2gnQP;A38.91Hiembٵb'aKJPf۟hk eɶmKp1_eIۦw*#CKG!+lWR{O^}a_ݐq{޹e)%Hf) 5ҝ a&r ƦtU07 IZKn.02 ~",Uⓥ(nQѴ'kuN,=#m'HO aiZ S'%"A#H<6Ap!L?]1VE'GEV&~ :Q<|S]r>㽲Һ+6,b~ a21`$ Ix mӐBa$a/I`z\tgUys24)ї>s$:IQbE} +v#]4]woy:!:]"Z@Oҕ]02;s]D@ǀZj/0a P&] 4\w٣.]CC]+ 宊=jmOk.@!OZPvmNgv-DvYI,aD6!Z!!kIUnYv!Y.N],:y0$N,FI+JJõI%v%D(ZZaI+JvؾD.(\\v׻+vU@( $  C""1BqBx|ȱzveu΁,RٶvȜ9Ms|ٲg @ eK]!&(]ʴR 7B chPgiqO>$Ԁ@)go|TSSѳT]UvXbeZ6N;6Dv<)ReFMEK ;-ePmaE ?IM('Ǹ˜`ԠHOܺՠ*i!^EiD*gLyzB"!3@Z|*tJt4 jGrzlVg\w͂ FI纋 TðVO,w^{@DZTQB, ǙԲ  N]IOLqnWhA 4ɜC[',L%)q:2Aj/ 1H{dk~>8tYkdG/WoW aF*e{6H˹ (TAqslw9:y8U\].sH&l!u%: 3O\G*؝jG.VkרLx#H0ī  H"HLhD4 #w upk:޶}u#7)y 8 |/:\ ~EanQ`>j%1?p=1aP(:oxb(7!!CIAd*WVPi| ]9BI0>۽D'tpj*1!BZh"zГ 'x 3 ( D# +'Jщ2c+-Ope `U©J!H&xN9smԓ`TX@%FIr #)X,g Bn'ϥvRʞ bѠ,N =s׾ `Kؿ6`{+QO|苍HIJ`s%1c"ӂ݋ͬSBSHV°5WWRꑨF005=N%f:Iv\5*W=eO*;?ժ.\І r$z xKWN@bk)Z5+lSro^x9Lul^gO؎wV_AR62M^N&`+[L"H-9i" Qy'V#\zБ_qաO}UiEB@1Pv `]`WjL"HN?)r  t0w-em/| (Al.L90 S)哧ujZ.@ K!Ad=<9c~/ Jhn'_퉁Y)=S[Hr%€:?DjUհ"<1NMh{8N8n- 1`9Ӗ\3otgj+Y fmP_[$"Ђ0Ђܒ8I FWϯMݡBZa:ӷ|djvHmohH"&Τ3N<=?f9g}qOr7WF&-{rr {^ev;-JN)<-!9 v*yDޡ[܍x EwW!,|`U\Lᤧ#i ޶".:H9q bu>! <ʃ87\G6 6Nn`P Ъ\\LwT6{pqt!=3(TLV>TH@ %3ynZ9n|=*lVw@}%-7|t]f9ye ,c`j&C*K 6Mr}tm&J9 $a2fi0d~*or}IY;q0+ׇ7/>X?X/Rpjin",!I$. @:DP]{JN_sk+zA{7%:h'<Si@xE 5ȐpD|Hx ^mr75$}{ٶW(@?hԦIj lXp2h3>h}xw8?Q.L?&5!pqd|6 ua$M"i# ( 609ry2.5#$CR9<.Kbm0Sq&^YNքV0V%CїmuS'xM89@R(oO9_B&xeowvqX&vp;1*XsmVd%Q  'k=r!Hrיq)kIYkFk7kȓ$EmϷf*Te1Lt3 _]&SPTiV FAT@FL%0nybf8}X;' ZP.Bvxɗ~ ^v\1NN@:c`8pyP z|Zʠz.7HK#ك6 6~yc ƹ|U%( i̩ԉ}U1bI0iItI+ FAAV38NiSn[RHy_-2eW|2Zf4@Ч~@y0{*ZJh`{Xw Rnl[*_҈9QR}7*:d=`L N*4Q I#V qF,q%D _6+`n\fZo0vCEo[y\pYk`׮Jʋzz  2B9J+{Uy9, @ _@h((>^:mmV&s>Zy$aPګ k CŪǪy0Vop5s#?Pp\۵]+yXۮ^{\ h`*I8U.*4K _Mg_\0 oDIT'\! +Gꕦ&a$5HsH4B2  4}x)upg O;c aOXUZ蘕@NyW~ }m  ]`N@I Q_k:s6{X&[)_# YGYZ{Ѡ?[C'k`Y(vйiUs/Gҷ;KPSwV@u[3yvU4ncssH Q `6) [++z JU,zF+L : IjOF_ StS4D2v5ìCZ(\0)$h;(lZ=\Ȯ!8,Y% ,>hůɾf`fP/uO]}}RJ`C0Qpf0r g)h]^=fpf}{'3IF^ o^0|}{ B]m&^^ L}|u0Ns}_m+njfpeګڥmOmX=l(mg~4҉, p;eEif,Mf(7 jk }ƺ2J f{ v9x z o3MaJ,n6`G}^n 5E]lϖƐ2qF#Hay7v4* \c\@jB:%: m p͖  7 *}(Ol 34]Y 0Gm|ټXQicKYjL< pS{5Cr*@Y ` #|UU6G wM_:6g˅B H 9a,=!ӭD>G K =&)+a¨v%Ҁ?6UB\B Db@-kWf^o@ PJ]un-gj>/r|.\<._7|IvNy^ ?iRr @c#s +bJ*V)8;'lr AcۣeD_Dk>?. =b;dx# Y2X)mԘ 1vbeÉQItgӄvd jkٹΓȶ$%7ԓC$P $D(Yuу=`+ 1fX G=0ʑB r ^`` a rE0)cl/,f} [2lujƪ*-/?<(/[GQ)Jj*/[mS 0쮴I *O|E_5VYgC r"Lr&<^F`nyfҎbSB883DhAY8P%|s0 lӄ{G Nq3;ZoHJbP@;¯\8@ %I AЯ \Ђ\Px3Sb}\ jf=aUAR * G2q t xu"2 @Bx!D"\Ћ0@|0"jď$G rB "hqNFbAR&Cd,#Y ( .@8z'K ?H.qIPt fd ) Tnm @4#,~%rD,&`L$CKLR`RӗɡJxjF S)r輨# fӢk$oQL?ɀ?9cB y5eʵ԰jT:E(u)"J37%lҊ@4iҙ0p/MǜTҔȫ^ 5\u!a=PFXf$lѝ JK 8mpQ k'`d4B )y \Ȟ@ʗz-,%X)ji D6 \/*u?vc-D*~ilgK[`jISq7L`fI(AKPhӝ*JlhqMkzS9M!A %,a 0|=5h~C%!WfPI5A r }s/اH|/~c$gt`ftCQԇM}! }mo GtuvK2vVv&8@Nd 4Q3V,Q?x^&[ Dr!oU^OFF?eelJ Hv!y)'},0PU(I`u &ppXsR'(W'(+ )ǘ(䨌sxs P%|e(AS0d+%@}aplRzFxIu~xa8SHXr8Ԉ  IM+`FJ% Id(RA k WrLxR0j Pf8prKPHB`XJIYP(B`X'T(WR hYI+SH${FO1AuK'qD!!rQ̥ ~`g_ xv3fh1o8a&A4vwP)pe!FuU& 8!4Z#=Ybh(y! `U(|p|+(n R@h ZBj C@0@B0h@7Gy) I)z_p+[ Aktȡʐ FG*h WdjRG5À@  dٚd֭7 P]_[<cKekg h/㊯/u3pQt*H !րR& ʲG`ኑ*FNa㷉9Ƴ} ʺ DFQ K`N Q+פokv@yegn +g{ٰJVu[ZJELY``Zq|)m]2%VE 1viR2Q45ZiULOuIEG+ BT4' aFW 1DfxyC !,#ܑЅ3̋~ FAc?v`")utnaȾ(GV$q A4@' Nc|VYF/fܜ|~|!$ ȁ,Ȁl{ڶ󗬸C.u0 4NCmEÂ< X4f}sۙ)Uĸʈ<3QXk4]ŵK L?AUk Vmihsuu jjy ,Ll<ͅ‹1L\7 A:aFK?t H<]Ӹ([ʓhD?hž/?+nab[b,4@p|'S QV5YE@%m')+- |`)A c_5W$ \ 6!O?A@`)l~_w5SŸn~amtMlȴy$^N9N],.P[|m'6;4C|mF~嶎PY0ec@I~ZEOWPb`cƾW__k Jh^@ж`>E N/wf;ZT~*| +! +pX.&1cOPb0Ð yP. bH8 $⫖V$R[Fޞ~NX.b]YpEѴWѽ[h*c_'kO^9S1{rDOb c_K _ @Vn\'oa7#1R DDz+JU@T0x$nćv%%%vD.Zт%}II aa.\((\av' H *TVXI&#[*pD8ceN+~AH(S̢ ?\)2F?h!ʜWRrq%'Ty2ʧ?  ',@+ ^{ݥPhEDvlp^*~P cצLXm~S5ŋtre-a#&,`ܜylA$%4gS4owvKֵo.dI,զ Ec[x2n1ȒPp1FLaI LZ`"a\YO c<xRQeVZqŘW`!P[kUR'Nqe…(4d^,М)d 6኷YXs,ؕcݒ.X#5MiߨFk Pmunə0Di4Hj43ړ@#TXInd%=]~)HfJjb+p^GŜ]; S`b !:,V8ea@ Si de*Z2E6JK.E>$t>HnD̗*%7-i}@ "WXzu gZ~,H0QPX6 $`lbC>mKe~Q35D5C#@ΙT5Ťٍo(f\M!M!Y,SD# ]ǗFqea{_'8$.(P+po׾d\g*6Y;[(RxL{?؈_]XqXZ PqBj[1Tm֤̭-O0>d&X &3N'n֘5*Y&Y1@/;jo#$3k4ާ8] y8!}%rb7vSGE'r?w~cʕ2Rl2.L:7UD;8WAyS6=7eZzFȲr H5MGyԁHi؈8/=fp38ip708%䃎9viGwb}!;rKR:P W00VZe a D2f84Y'@mXHdxy3K&:(Bf 7؎pqvJfg8qJ~Z+0(>IƉJ8)|j^\\*ӋD`oQ2XD(Xظtz؍%M2P討ؓ>"`gCIǏGZtihvHzg2:2TD #F`ory4B (0cioA$RRYXJ3e4tpV;\ mŁQw{k6X)..!!yB9|S vn" 7o2QC`|P yijȕL W$c) A wɖ C oɑ]Hvyw|M6Iv9 9۸\$ %4 y)c:ٙO:Q|0P!6kinno!!`]С,0nik%i0Bɐ br*S),W KԏMx#1IMiؙ~P)ɗg(ll^@HYwX++#9 9<'tǴaasy( (.hI@$Jt6AZ,`:0]py@a+[:Z$@`c.Y3Zw= h%B;>}H+C@gH )SOW Y?.Zf?: piKk" E T6^u7wAjb]D d:׆Kb{<`76в:iN+ O*ڱI]`&v5,\n0oDZPJx<;0kۣv4m\ P}DV鳳Ľg 囿ۭSppz y@Dvϐxk4dT^"c |+!nn60ʭ;+OIP:nknJn`i%:20@2PλRŇPY|> XI"c%iLkƂ, saBS:2QiqD$-ms HpYC u؏\=&0;^}=b\⼜ղڬ[^ͯNy~8NẝS wrqp)QEaz I@,ZlS:~F IA/&Q# Mmo`!#,M|PZ6 a۔L%Y2,D6̵J0 #v!;B[^}a 48#Tۭݫ8]SZ$-^rțM =>^STv>ϻ ~s^0Žj^gD D@x#D@r_EW#0"|_n`.y0X/̕YP,(T6L22v M<+I>@&'8~x#Zv+x`뚁1G$9s-?@십њP6@CDnNJn@!NZ 7K] "O0O"fϡҨҿL XTLT۬d2ta"ZPY:P('Jvs 'PD' ydx"N(2Cl ',@)&%oЅH u O^tbCJV9 :oIdR2{jbY6 h <c XF!Ξ1f̓$2Ae\^CQ%|]fq a۷O3Wn0~8QEaG$I:a 51%ieC fY:hV"a%xbZlir5]W`:%BA,I>$a+h8Rbh(k&z8`[AV*hq-Zq)'MsFWJB窬ږ O8ENOYӛ0.Tcz7 JcO/XsKvG6坓fl, C`'}W}1RrrTl jĂk vkkB{ y@]{s/+6D qg֘Ջ1ˑI)dZu$^tb;j\FĢH HlT ?L6/Tlq[2Q kc,nC)B*0x M}pTL:U!h$gA@ t'pH<'!B^2$8` .Lo=ۅ HcX$ ӳyDsѯb$ M|R!@kH ^Ʉ E;IK\_w  NAE A`18=2@=27@ BKQ= WDCJpZDv.]*_<Y8 IC9JХ&DpAP̑q!(A Vs>V)4A/_!AEڲpX$'YIV*,&ɕ%)aASCpJXN,K /0R),| B-} aA8nH#ȸ8WC<ԡ#xJIb,FHp@ @XPpOX7dA]ȂX"pv >.a+NY]KS`p?mW&x R@ ?6 IV&/ K[zl2l*@M{6 CwS1MD`ŴlteAJTo=z |-Κ疅mx c+dy7 "ڨ_ˡV`枮-S]؀| 0+`V l ΃>: nTVg`|z*d>{dr >!FD2zJ@- <٤@Ȉk[F϶H(L%..ymt熐`@vS5{߭B*ΙnW,BB[dŠB2J`>kr 1 IluN^L.|,Y8Ӡ<!;GǦj%H#Y(gFmq:8@,$6}.r20 3G9[tcUچ0#[C#f2II~_/ ["_G&-,jW{F߻ML1ݒNwY}o o~M V/suy!7;3BGlEtcZ'߶bsDR{o\~?lǯmڜ/ܷr%A_!}P}Dg[}$g#~g\@~BF{~Hin5w#X|ɧU,}'19p֢H€ާ5?I?ȁEhGc3u:"hCQ+&lB!|+,'҇U ptڂՀău(痄uhw؁BȄ!p@@|esr4GD(#0 +X$IVx[Nʰ v`z7tylX?9Z6x芯W4r|jP* *34P v0.yzJ9\cxdFʏxLjLꤵX:Cub33kYi k^B$ɍ1zC٧u-Brfq=Vm},>Ǚ%TIIzϚڕ}(dQYۈyݓuؓz5& I@g胬YƏx UO:y5v꬘,dJ >Rfqj[j(P +k0 +K-{J 5í'4IL:xiIv_&ʅdrF@;[ @aYӵ_{:8;;0ꭵ?BU$QxD`*p+N+r@V[E+,+Igl7MLRwx [Z3qěXk~;je,4=ʬ̕[W@s' f9HjiYylعqzй7?+\+K@ǿlpA뻠MU=\`KϬLaL~\Ϗ Z J Ai)irY)|irڝ+yؼ\#~cb)-\0E/{&) 1H`Xi&sjs_&`+`m!Ȗ0,[]-F_ a-cMemgikm_q]PXu],.X-{} ؁-؃M؅m؇tmwj˩+!,3}`/puz! ;کګڭگ ۱-۳M۵mۯpڷ۽ۿ -ܲLwR՜!4;-Mm׍٭] -mڽ MMmk{|ݰ7Y  .F.-nM~m %~@) |S1.3N5n+;-6A~<~#&&HKx[@Q.SNUnVcZ媬ELnP~kX>c>]= mǍRgl NnSp~w0݋6A|vNy@;n$k^n:硎nnlf^Ս^y{-/3.n^0^2ǀNk#,N2`Wݠ>w`>>.MYv]DS;1@ %511c @^m$ Yy;OO`9.Z ?QDp%"/]dw<-/ =U7o aNZ0s1SPt@aDLݴG*DD;SEMebOdohllrOEw?諞/N0P0`D_^9[`[`vPrl@b|ˀYn`*6 0_o8?^oo=,No6vO% x*8gVs-W~y|H! y!66| 0$N,Fkxl[[41S11t[SS4t[l0|n|֧i0yny$yn0Ҵx$񬶸ǽƹ1̜=6ڵlkuv r "d"F qȱGI@"eJɓ(M 0at)%Y椙K3"۩cѣVR5N +皝znbC5zG4i>]1dQʝNx *ݮ <ˮÇ%A`6ـV RO`8@.tхE\%EQt2TPc`]4vdށBIhD(qqYEpZ kY<"7j-HTViXf\v`)di&kdaęlp)kLQYqZ.x,х :,P$\P Eha]'o9Db꫰*무j뭸뮼믲rk&,pPueA!LjCDvaOZQBv8'DYk ]8]AOx,l' 7IDlgG,uYh6uhp$ yAͧ՜G,lrZ DmH'L7PG-TWm5y\w`m5v>F6HVҜp KmZqx߀ z]]d2 0ޑ 7>[ ^gw砇.B0騧gP.n%cx|]ЦIv60|l! K҅'ws-4I/o篿 H?v#:_0*H* ep,8 Ba\0@XrX0pv$T .pES_JZ.ޯ \GH2 DAI`@8i! u`'P"d`SZP,ZȀ'%Pd#*;<^ipA d Q9V:P p,Fرr\~jL 7jPтS]%0 P"%%(P.  0dOZ`2hxV@< .xB\ \Sn/JЂbAy18tT7qSHn Di@JpB Qv$$ h%YP`P*Q>08)h с² E(  UY*.RGkI/C6q!B%؀FJ}n>,PB,5t2p@vȀl0.ƩT,فJ55U3E+&Ǫѳ|x_Z ǙIL^ z \>RҢg].b"t%dE L"r[*F&Ēh.|bQ.|N%6} _2QcՅeZ&ke$|d4WB,5A_J0u! Z%@6""\]fEx`ڢhfd?2J=a ,hd 1whjfWVT< JI`AU QNpA (a > Y.CG:ړ, \3G7yuF&v:lH玓`$cZ{8 ʃ (kMu"†ʬ^Θ.(i[p>54u}Yz3OW;Wo+;_f_{oO^|7uσF6OOƥR'xz,^i+a}3ӿzW.|D|"wxyJ|7yz&~W3с "8$ c'?Җ~'{WWguv7vJwxNHgwdx v(28w7z"Á&\؅^`b8d(,ǂij?hTvhcts=8wׇ}pGyGǃ>8;vVXtX;Px؉1X1r׆)~0(sxwggYU(\١wãx`r08=؋ק8|a YXxؘڸܸɀX(؆׆oHH|81\p9\"Iu~g8) 0w}؈y(giȀ4@h(xnXuWX\gŽ8|8W,)珀(ɌhW+0lpHJLCiMLIlPSpmȆhXY8zWPfyhZLrɋix9sْ7)wHǓ CP/A3! DIC [ И /9OOIVPXrg?i!vPvycPk,r9 y9zpy}霁}yag40.E 010v@Y11PD/Y޹y9% )UWz金_ jf(x/+2`xxy JG `1P.U͔UY1YH6ztDL(@;*YKE<[>TO(cР:kYw*s#*H"IG)oI\ip=*ީvtУ\N: z! ?Hu h9äpÙtWqʫ} 9ɪyۦ?i.KPٚIک%`=N.vDɝ9ᨆSZ䈪շd`B8K :I If } sH{vJȬ)1@[@ yv v`v@* : IZ94;PzfRz" Kcz|[)I0k'&j) H t[@ɶk+ n[M+P>KQچ#`c:I+M봵ze ( G krWۯ^ץx]w*yZPu M1ʂ<`B+FSh^WStf k(zUx{;.O(͈%ʱ PBK[kj[]x.L[3 ;`Ћo0Feaj/z^a7޻KzK "<4^z w`)w*PId뼙0Le YL iqx, vlߛY( JV|XZ\p껾j~*#)a`l\8){wz`ʹRxzMb'vO:SȎ\`lmLVGf,vxZ@_%m|t9niWv :ukw&x~\{|`˕p)8g`Y^Yy/ .e9bzcЖ.];y>̜ë)D j˾hW0w=<œɾF+w˜Όm< lD9*y;~ B ?%\` ΁)ŒH~w\ϭ|L ኷Z}=LIvsJ}SDk+Xv | }5~w-bVQ~(-RG- gSR6lжɖ7.x 0f0phGH xqًr7D|դ;FG+ܾɼyh1bwkLPUwSoIk4wڳZèw ٽ,dazMrL(7<҄ k1h̥ml.gG_%ض,gY[h؈V^iYN^,ܻcm{֑ޣn{C_7W޵x m}@ om66C208wvk8;P7ݴhzX/!.,}`O`pd'zuk8Fxw8,eƙtGt/M{v۹d'S.HDȑ|R+}^}`nd浚xO Fov=ӥ:p!8eꪾ~~ ੺aՙUj>RiݓSڞ̻d'QJ>pl,MlzmK[>Pol,@eIY>wuyΗ>,mۍQI/Oll IngQ߬)i9 `o[^,JYC}Pq/KnYwLNPOd~ߟ-!٩% `1\09 _HB(HLbd%vJpqQC-t@C.:8b [ecGf%T5qJ)S|y va˚= 0bƐ={ wYi_DΝ x?/at( w.t0D1jH&QFF!9`A*<;رD("Ξ.dǰC#FY)ԨRZܻR;رdq7l`x'W9E?AXf A$geQG4RI'\Sd1ts4H ȍa[vĀANU&e5Wc>r[㙧1# ]ӟ^\pF񡣏VfAnXzsoʯDd^|0( ; 1fgԎu^ vbM!=i:g 79j3+sdM3Yo|Ъ4?- LSgp˜SS\W)ag+1E袏Nz馟z꩟69Z7ʷܢky 9߷8 cyx; 3 fҚG|~O~!ɴkqUoFPeux<*iy_6F=jsҜϩo?}% O bvC&e6xPEK(:mz9c j,"l֑L\($W)۹prΐݔ3 1X'W̕$dA#bSb֢@T"x C^jn&] /L4Qq¯p Z !@n@4''$Z0ZKԠ ormK1Y$ ]$O*Uq@Tю!&2^y=ʲc.y].;:ɣazX2ᵌ0nj&qi:Ve T&a$&8y=>֩CEoNs<Yc3,'@ jN*(y@RLD9QUDbF9+"H/>ȝJ!ɬ $JV!pJ r)Dci$R{EkԼrnOU4:#SI1V7Y{VZX"U=hm$1wV "PjzzE;KkE$k՜P*b-doZٴz"~Y~V!H8>4רh]WBԞ=jWV.&m7\U5㫐(FG,` Op-{ֺ֭gr=9`v` E< `NKۀuĈ3 v DzenXT+܏6l XI dfX(2<+fZ1~6 x X ݳ ]0t:8؁6X $pҐ,Z8 L[ "8!$; Zxl7 `tP0,, |,DXWpipZHkav(x|$(}eT)<+XoL7\2R'A6l1+a;B5,gT'vhGc!|KhLMȧ?0?+%g} [}UlEue5Ccr]fy`*: !pi \b)\؉gsixX`؉"`>`aQr gWRWkPDIU YCF>q*Y9V@~ W8(-΅0pg!hii#f|:ps]: tkOOfr,>O(t,8L0he.HU7}hIrIhmÇT8r,Ȏl|ap+v0"U@oYNV5YZU @mT}VpЖ\`@.9L{ $I &y*y$719P>cب %pafHfZ .y4(tO LY)OЕ6`'% uHfhLPv0) y9^p?0IP%K};]Aq1%a.n0 iŹ0tY(nIPqygIPyfNd$YNm]NPa`hn0F 4]4 ?#) MSE ȇ&Ş1 w+X.qhe>Rvev]v:fgg:lj(`nn%\`+Wn|`s:h %OgրG* hHBg@! IjjH<0OS >]Hc͸$_eMzYQ*}XɥF1i%1#x(v6k kPzkpb`(i`a 7Czb6qHTnPD6vŨVCCvvZj 3SwkEڬ3A~nhí " Z.êңʝ8ʪLĸv ڰGy+c*}0%hԱkz#%k')+QXK13Ji)4F"KJ*ɧK{6MZM3 ujp$.wCD$ٴU+GYdK胺A˺?X6 s y&Vࣿ$cAc[ v [@X_*RND G3 Au5dZ5ݣk::{ɯP3v ;ns5 [{ kBp`_5# *ew;xUm5ҪYye]/L1̀ 8)ɩJcE 60 \R$+*_pS!Z#k MJp*\l(!-gCv-x v: L6 jRP Ujb: RA p\c nuj@8a/₥IP"&]P]0[DjpAےtƤ[Ys+Q!ьҌ͖@ ĝiJvDlE`/Pu, CCp=)0mC/p_Ia@dZ$Z@J?@^}Im83OBش=l,E]}] Ju <p09Lcdc+cmf} k d>:,42gpi Wpu`|XC`Mr0rЕИm) J`=VIp&`)EҮ=?*<ч3^Pӯ^0ѧӋ=Vm<ա f\K=N}}Q=,]+Ab-D'@ zR,il}͘@͗@A#=ů!)++  65^ہV?NNCVhs !=h TU|,T:zr =ߺ.pS"pN#oOj]26ο"tQ xœڐ.*0, rƒAO 8xa/ hNpt04`4 #BvnoNSr`b`Ьx2r <L=r9> ~n+[q Cfq޽갅p@`Μt, 솯yo ]@/:w-<{}^ELR T潷/[ D8r 䔇0/IIY(U(\ZIJJ+z++J+YF$0Zkx*8gVs-W~y|H! y[t1iit66| 0$N,F<Ό @j d#JG!?3jȱ#*~P  M4\,WNԄ T^*t\@zTE˂vJT)"u4TK/K+=H"9Y޺`ךph1رd˚=FΛs׍0K]yyVZA8' ңS^ H&[dRKW2ҴK|jt(#Hfu3FBJzhV\`N,Lj[X!@1dʘ9&_i(`5<LpDCPf]6RI'E[miFJw'"A]arQS9t]utwdY'^Z!TVyE_^E )d)fhlf>dxE jbJ#j1dw"OӏUXGPszDdqXEIuZiM`%t䓶@Yk@P{xɷWyȞ&&q"ٟYa?#Y lH+)hM! ƻ! Ի]%ؘ .b j*B䍱gqƧ}Ċ#$lˢy, ;B 4ƄiP \+phY  HkP.z` HF-eyt7ƒͬC!a&hǡ0rS!LZT 3 },{hDB J\"Y1'>qKd7Kݒ9@uW0%\fJӚ8ͩNwZS[ qiπI}b{w$Hj~!f h#JE8OзUvEv)U)'PR"OJ׺>jrmRQTOU#Z UuXףJd%xb֔2 t+a*ӻ=>N毠u`н-HlTXMXd GU 5far~6P[IZwyum]<mj[òRl.f֒ggu3ItOK{x z }7QMcZm*6M/Uȭ@X7,$(_MdEtGRu @~p:4] |7C}w *&֓m{aWd(BNͩظlT^ %4Ybw *aw=)]{,o,dAxXr>kDz@ARL Ȍ|TÅ ]FᗧEso&YFtIx3 Pk9v0ggݰ0KM`"cp ix0?H o 7f $@K^#!K@0DHjO!1|Q74br9ֳ~ц}3T$]?-tp1elVφ6(w`-ɕK,H &/H Cp)  >@&s,X@n'|~xjvpf->X"jjD8QPAtv=<]ZAc O[kO\ yX0 Ah` x\!'0C\]H +A o3sr.+Pනvʈ6͋dwdE)7iwK` @ !HBjA`PG$  /W8{e{@vH_ugSi|v -fPV('4GaӇqxg}M}٧k+9a@59(CCwxIF 'mgs>s'pz T Ap [Ru | pzXx'[WArp>' p 7'#A|(}* tkve/ 'W6,aQrV.C~&`40AG8]`0kn@=  1PksJd:;]PXx؊T|OxȇHgP#}xT]тwT}p#49 P U*4V j6a7,cE~6EeExT xE[ W9ciBw 师HH:88bx@WJta%JLٔNPKHV&Igܸ.I1kZ%vᎊ EHHȏ@piV|ٗ~cXWy#YR0#w\va0dPNh6GgMj%,ىa"ZI5TɈةĝrjs=}VE W D,$Z&z(*,ڢ|w +bZ 9 :J(9Vq)w0%AX|S Dyyǂi )àc%ޙZTz͠Zvå0 @`e9qDiP{mf8f)먕H"ݘlwʉ^RfZFi2::2"<7Z aZPcjq`iRtXM̔Npr7iGj.h}ժF1䙄%)h"ICwDl#B*pk @Ϟ ĪlC7/~5U"56G>;|:bl͜J/QLȝ&xZ p0`,7Ο "Ѽo1fP麭M#)We#{w+Qqvǝ7T z`F0v@[! GKmdO:KˆI4g}9ߧ<^:O.NE%ܜ<,nZ # 칡oۋMeZ&a{Z2ȃB?0oB_1jʎvp`Z%@;\J!lVmdO驯qŊȮdx"FC4fCa`$hTTըɅ29YeI9U agzjZq1K;;q{k*<,la|l LD1D- qf5r">N~P‘ǑǧN^;/x:W! NXƍ;z2ȑ$K@ɕ,[tY $ JΝ< 4PRX+L8U”7T 2R.adMPjTTlWȉ׆pٽ,`-CLٲfϦMv-۶n==#0=~&͜;;8Ō/K>Ԭ[sf͛py947G.}4ꊩUzU'WG[+ٲ&€Em[V`ɢE.; 1C3L6mܼL oEaZF .Rl4لm&]8QZ RMT!a @*2œsυ%t(8`Zvouws-zg{ø_|}MIeI$h n%J*u kZvRJ\`HHW)&QU%^ŧ zRua X05 : > 风A-oi~y&K"DF2{!6b96~ kCFYYfKGcBHDfR|%1")Hz\PmWR]ymh&TBFTOS*1md*V_c uAHC<J+6hjA|W1a+iŞ|jr!{S$.Hp{m=kB9FbXh҉2 Yīl^'z m"o0&|G 8.@]i!|6D67=Z8p l<._#`9l2=sڕSɳjЮ1Yd%纐@m*zj66O8A\dD$!aCS?BvdG]It!@(a]D.Uy12 fܷEu. \ kq:$,r4d CN!)Y3Qϔ&t 8>!f`39'>h0\)QgR/g썣; 8أb?)BB 4!rwPD2mh5rp(6RU;l^*1wQpd(OP= l\Ȃ@Q @dof325$" o,|js@pX)4a|zÝyuBM"!N&UW>'@%B jҔEeĎqdT.Vr`쐇\8.'|l&hĢEԫw/tN5 ̴ uN]\BOq tĚ&`/=kݴa!jXSB0: ZiWDs2J@QP:}<].+MC=bÅ4y\ki@pm@0 p#@?7myq (.^Z#Xչ`dzdO{2_9͟?6Ҹ<\wcНkeOÝԙm n;q^r[߻߷Ty x 0_nwC]`{kFw?W{~ 76]q}^m˷'. ܀d>Uit@EAYЛɚXz& ۩ؕ ʰ ցoVZP2SJ\h)v3\pyp8vr ˱@Y뷓D-hʹ{lSʹkw gtWF>$(r;uC(yS K@ ū٬F\қɩ:S\`.pvpkబ r{˹plp]P,]`kSS R:0fxAs8 SEs|1 vPYΙc:* vQZ-kM/,2 i8v͉-A Cv71FYn@a`N0Qtٍ*sLu,Xkpic>ey`r lzlܥl0 l\p{1 @u5:|&Z(P-:iAL$rc6fY|DLGͽ+ĬȬ[\^5i,լ~#+ZDDH]`CPn !vLm\I!kV1 x{`j`\[0)=\) :\T˺wpwyke@]g0rkyg+nܫQɪ\ [l.୍PX[`ǺIY_c]Zp/.nROm,#m^D(X!01ٟٙٛٝ ڟkǡj HF\.=u{Ěk{) p`q6X 8P;k\U|ɡj9t0Kݓ iV͗ _ *-w,`D'Aimcd,˶tеU`Rh%Dt˭܌10vke{n \%{m[S`?ENBD>Hnw0mngʁZX aNqh;Yp@Uɦ-.I,t0.ySpK^{'n]~ր.+^E+rnn\wگ\m갎-y7(EjNsI.0ҮgK>tP)NF+pznn.rY mk`bPW!/#O_p+-/1/3O5o79;/?WA/COEoGp3MOQ/SOUoWU/Y]_a/cOeogikmdpPuowy{}tOo/O?g/Oo/Oo;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionParameterWindow.gif0000644000076500000240000007666510534177572024325 0ustar paulystaffGIF89aߧ}RRRRמ!!!ko33C dffffִ 8;:8cF @̙'l״ݻx˷_4` 6jիo*X\HhṼϠ(g!?]ͺװc$HƈfR7}`|RYj2L gHL55T0 Ý̽gylj"ɒ3/˟ORjH2x+1I|` V,ל[@GtfAfe`^)&wRGF *ƛ6`B ᪁ XĊrHB)ہOl1( bE j'wۅd#< WmXg)]PV$qatM7\<@g) XJ̃P$Q3 I!jQ+Ay/ l8OꬷK!gԎ^"d$$TvV!<߼X 0=O $=5E(kA걷/?8K*"<>rfo~L k%1D:.xЃ 2ρ(L WB #qUfMkp8VzJ؁D(B H"HL&:PH*ZXb ,` H2hL6pH:xda IBL"F:򑐌$'IJZ̤&7 m@=<ᔨL*WVt,gIZ|.w9K> 0IbL2f:% dC nz '8SyL:vq@̧>~p8IЂMBІ:D'JъZ4f" 6 (MJWҖ0e) J*8ͩNwӞn=yIԢHMRԦ2h XXͪVUN H JֲhMZjV$n\J׺xͫ^Z88 `KB:(y:d'+٩ QȬf7z mg0x IjWֺMͭnzĚr pkٰM;Ik:uh[N(+!ۭ*KZR]J .++^^wn{>a C~_W<@! @($ NasV^P-jSr.jG鶷-i(0W:=/z]<4CvPPTx]`8*yqvM0``R` 3>QP \  P`g-}0 $ J,'{<76 R`C#: >pf0(PQ HijHP5 Nf NN6\9|+i9 X\k`Y:.Q,X"4mh ~%eЄj_ F @" M8A("P9 ]"P Z؀vP!l?PpPu-t UFA|g`ށLfm&B`38`C%t0/DC0 h,\QT ^GX`f:h0;I;8Q@Lр;"nf/\an 9̺ GupZ ĮxA ?pZ-[e6`3l%a-0AtS & (%!Fγ% ^]a~>\`Ch $:D@<6 A ;@m?0TVp9P'4`@ssXWP ,0Rp@X&mxi'su C'@vX3uW0|j| >0$`SNRNuL8Suwc5WNp>ukwtXXW^@^pWp^lXwm W0uu0sP0txTyu ǁT](g8'PO`'nFBoDZpF;N h}WFPeF 'B~јpnKpFpZHѷcF(8p^npW crUXWu%uDW|Ws??H@ .htumpuBH (ń Ѕz7مH0 j~Gbdh%X8*pTHq\U@N|8lsG50wr5>@wU`SU~xzzs@xLJ؉WDppB ~l`W\V3Rom;73|nGFE&0F@yEB`qyVم}hf;08AfpWoQSm$HP'T$ "RXX֕Nm0uap`0IKȄ wDx$0 LHvw pH0M ziD-xJ Gv5p@yGuW&F[ٕJylcTec`D 3@R&Xfv{j?|4e`K`FWE@5|<F{#nKYV ;`rD0ofDP&@릎G%B7cWf`#w[)`FGwRgMX,``% Zh%hSaeijiuvH`vY)x*vNqah`H@{pZVǬZy'zqrzլrʬz&n0M5 ef꺮Q_oZzJjHygdUhDdHNkN@hA%{TtS&{.(4snh":<۳>˳Y`@1F{HJL۴NPP+Hfpu\۵^`b;d[f{c+q_;npr;2ssz|۷~;['@^yXи;[{;fP @[{TK9{ۻ;[{Ûf Gc9;[{؛ڻk۽;@0SXeJ1`#;[{ۿܿ1O 2 e]e 0L \ \$\&v#@m]"||2 8!l'%20[RYHJLNlf@DK&F`e Y<*`ي&0Nnpr`ڊF`ktɜWx9ǤvPC10˲<˴\˶|;E1` 22E l& )&B*i|͜T hMf]02Pe0EpX|̎RЍFh,Gr,nfe@E ˸\}ѲWls -3P%d<~C WI(}Фel Б ܊r=t]׶S\R?@m\*҈mXsc>l-rl@͍ p&@l]`H9sR- Ț[,ٝl-s-}ʬnD<m}ȍx]z3K̀r?礮-ۍPmɔmqɗŝ]Զ*!ը͚ssRPҵtM ]&pMӛ 2 K^uܼƀP] @>n?݉Эuuv-@pX<~ ls,~۝ޟ}ԯVu MPk_.=<׾ >ڍ.EM |V|mfnm njolro`^U|̀*e%n,n%-NλW(`ߧmͦncn]उ,W0JJ;hȮ rF^p\NJk8?-nF0n4MLy ~k_,'0RP&si)ZPgNpn~lZ ׶^V&--;0nVSMo,NzW &PN-rLs3m*u/mF@r Pr^CoEϾϾbZJW{9|P&3 oWp O'0f?΀F2 '-M--WM>fYlNp ̠/E ՛mC?R@X 5ƶ3pacWwu50@DageHDAad0P * )JQ4#h+TFDa 3#}B!gEUg҄qU4 #ՆQtuBF$e=f!3"#0 <0… ňD4b0@\K $DX҂C1%HP\1B "M~84:CN'E:@(£I?4sV+J(!W +v,ٲfϢI3Ѳf- !Ey"#(\Y!CѤK>È+^jǏ iNJiFa(+R 8YJՉjZ.I{-/&\%Nu 7*xs[jFUlRx~~ nKn枋nn niʢk/.2"GTFuxVŦA3l~%E/?0Bʔ1Rg(Wq E'6lBDCz%?YdAń> tBMtFtJ/t@ ?/? Q0@'Z ,]R;AޚsvUvTmЄ!Fl &|7 -qPx?yONy_yo^I?nCH-acqݮD/]TH6Y<՗ͺ'MB߅A 1ljs\ODo=w}sO柏埿~ OgԣoZޡWj\ױ)HC~`#PmEˌ;'ButR'"up FaI/[96&$+ '\O@ADaS{ L&B0kC {hiT0%Y|<<`t;KDǚzqVmh;h (2Xd1`vE "nCBiE`Q,TR  % y OA'̀hf 4R>`2)0+!\Ge20aa* %P@" |iЄc3{R0i%-O8m2 +yl|~C ч!hDOSsưRBd@RC p8`pR 8pȣLV<n}CUT(PH` TJj/D%jA (2K ;6T3Ȟޔ3N aMx8`lڡW'BpT2Yx\=|*J{&,*J #>7A4+ x@}@ \V$< ee)ufE0 T&LBЇ̀*Jbq+mHSt `M kd+3&֋Yc(0V `C@䱄R`@9WggT Bd廫LKhqʈ ,j=q?L(V1. eX{(% aux \2w_ y&){1M,=w!PU~ @0TG} !p!`BScIu;%%L'ҭ:yYT㼪/"u|8p{/P{m&͆ш%}tGvcQVԞ7Pe~-1L µ(|SHC wms\llg 9'.f Oa/eZXnsW4@z'=Y;f~ף70gOF?āV% ~-Ϗ+v@4` 7@C?  Ü[Gg%z0,w2eP qLj@Shp}p ;w%7,t`0J  C|"h.WSpWs< A 'T80&2X7=-~8-p>==P~#FN=R(>+Ƶ'f7fg Pwcfg@p< fw[~gqd%g% 6(1H Lp)|w%7 Ђv#&uS@`/ UOGs%]6v`w>iU+L"O?@vMNc~j`=eCClPWju=v``lE5mQ=PPgP~ (Q eQqw]Z?kdr8r$0w^Hu_! K sHN3^E,6A_}wuJHVT6NlM@&I';p!-I/9$NRݠ&L PYRci=mұJvMڤcLlPhՒM9l#Rt@> aZF$-4l@7>9Pŗ})gGyqxv`/1GfCG}uN:xMV@ 2(idI.|vD:}!9VfV-@K@7duFWeeM=f5dHLl@$Ԕ'-4DcŴ,od I&f DKd(`Ք3δWPZZ9Nmi5lG$>qJj*\1`氘 w^w6e؇{iZA\S9{IU}}0fk{%ND ?@LpBP TTBYUl>Eu%Yr+uP!QB^ZUEZTQF!iU{ P*O$C-0tU>eluE@@9=w ],2)sRU$3J)|W 3L>P Q vP-G 3Es,w6YVN+ ` NtK<džO} K,TWG2 >EKHvUc NE 1T0O}6T0+sђN`msa*{BQz1i?ځv|9j7vChG<jMDv淋Mij̈wHvTP_HWD=+O،F(% ;~ cWk*Iu2]/*82B#mK2 u@@Ry2 15RiMEij0&_Cò/tk tOGS.0n4Q7S㧍sD~.N9 q2`NenI C#dҴ " 6u0-p_2Wg з2 6?0 42p1^,ǃ 3[Bཀ;a<Â&>z@@Wcu`M|0taeKoA,CLB|EIKMfN,Hl~ @&ס; PTSW:9-$Z hk(q%o3Ar2AX)-x%"IR"ADG<v'&[O,c{ɡ,ʣLʥlʧ<ʫ,R\*e@$:yt<Q ^Wm`G!&##/I$c*0%-1R&K>:l>O[pa/3^,g '昂8m;i0SNoK\!#Lo<~"?&e?R&?MR3E33Kvv4q@4((9/q{O1#99Y9ſTvD;- mԺf91q(O(414&]KKEuuLwLAF&>0$D>C҅&(]AqS%u q!23S.;x=xp(1LCCO!F@cGE* )TX%jXy="ĄiYBcFYhuhRȒ̮e[qEGɈ(,-ϝ7Y 1 ;؈$EF;R Ⱦ&`gA[w3>8x"0#3 IǹBMKJիXٮmײӳk]\Zt='D^6lR\m2|LB0leQ'iđG z5!LG&SL9Qof(ATYuvTKT0DЂ.K - T,%&D N&zI1I`u$Z!O]Ħc\1@PC09yYh'хW'fy`=ЧK@ZH E r(>ŜTU5tD FT,QDX llp#P30K-,˜3 npBdDcZGjÍx$+eEו(.AЇ" zH84a75tP+REF4yik+hawO )N-sTEvT9ClDCl8䃕+q F8aR,aC1Q]GdЂj]QD ^,5&9]LN)|XaC@px)1a8p~#Ø^D` Ex# NF&<-PEPPΤ2N͙k誃|%CntB w+1sDiZ `'qƀo>@A&C`4A}p ].Emj 7bYF+x%g]! z(MW0`&*aqՍP+,NL`8lhCʘ׆T -hC-lk[V]N^›a2fx/0R! a¾82tK$#VI&D8(U_ X`T0Ib3BBua(([2an@g:6Lm="V TdrU87F\j'1Gq!"}!'0, iB.BhE~A,N Em3T4Q@^)@tSRE2!Zjʆ/IӚ8MӞ< @M*OT4 @(R-!Me>- PZ4pi7L*D9A%6JŬ:ih%&.PI<+k)S*%T#Ȃf7z h7њMj9fLn1FP:Z*QMMj01@[ZD&(prG@ֈyAa.BK4J3h\kRN2LN;<[vUb+%Lp sogw?](ۑ:B&Fv&"/@5_殈L*[Xβ.{jm}`%s"a#$3&v b+A(p:+JCP ;0ws!0 iai Qv,ΈMrcњ@UdXszfZAhc@j@`ox^}P8Vwo5{kw"~htU>0F#j/Syc:A(Q;`2mZf{fWk2NpFZ`#?ZpE-  aCp&Uؑ]%P-jx|c(bXuy(uB35mFa=g}p.4X- m;0UuO033FӃ@m@?#0q0|4`А'@v>jb90)jhqd#Kl)APQgvxTGGs2npuu8}Qvŕ'PU bs4m? +(?R>p8@UW@FO@l93@V8h8p` A10x/w%! 8-ԷqƇf]i_&=l Qs1 S%izW0{WxK@`E@fiu E E9btbmp32y0{ ;12vP1JIs0&$(}dG YZ\۵^`F%SQj7sp;Kwo/Qxrhsr:k17PI)XAsִxK%R:'*Sչ;դ\@kPA-Hj=P=p.-kbP)-)Qp&?ປ<;o2κiC ٚ9-jXVT@껾;[?Uj ة{h='8 ӻ7A of1dR)& &79sAS椿8:<9MFL$r6&<F٫yƠ30=Yɷytwhb;!]A[g*`dTn$~ǀl @\Ȇ|Ȉȉl#rg%BCCU,c]lvP#PƝ,R#E*[ r(||ˀ-HȌ D`},f,̰ӀDɣǜ d۬}nԜKv@FdY3z(0;-F Y=W#?`mSJM3gi&SC4@?`?W # 30|(@RSf3t``&fX]ft~~<=؄ׅM؈E] PjAQB\(t+MVg&'M1ߣ&HAl)"-; .;ڙ@5,+ O2(*Z;Fn]*i?ss?ųj>T|(Fm@5fEfct?bT_+S //ޫov4-˶4䙑Vgب* 륱 )O<1 W O e |,AqS) .S0 pSS+0gא4/EnڡУ|1b^CxFLANBh2j?P?X0i0. piN~/!>>GЧ'<]Cߟ[z$PfB۹)0N2!8HHB8rIYAp≦svãys& pp駡@aÛ$BC#\63STQa"Uǜݝ-=]T^ĶFA1"s 43omgpێ;n];Ј 9:"Ξ'1Fʑ% *Kl8D%%"+KlWX0YH"B49I#vO69tTP C7p :}KjDA; l@(PD:#3@;(D;F=aF`V>*$oL R&VEz@}iVi?ٍ?4Z^iĀ1ŅGZ.EX ;,  C*9GF+EFGEW0HxRC Ǣ8`3K `l4QMdt{l (F`p}M֢xo$/O6=Vb94'i^{V5̐q="CSA E2?# ܨr67)tOˆZ- pRJ4RdJ)QIUT<1RW(}FCKtQ-<1I7e>,U*TOQ(8`CX76W-s(H;ǔcFR1י`dԐae;]hB)Qys$JEzD  (DZQB 0Q6.h1&Cx0N#(fZFX 86p2]^ oF,,FDb"(@Q}ă=a_L+;TJ cD3nOqX7hhPg (\RF٥/ ` s,1}YHDrM\Т|a1Uf:A|GNq5qjB@8Sp7RhGK0g;0p t Є 4 mC шJT Z H'j AM{tT8*sS F, M;cFsdMT.ěYG'nӆ8+ ) R*UCU`W VCV Yϊִuiŀ!0t #94tO]p0&\< hΡ<5M $mBpǛi<QWLMc-J*0 cP-0<p-`vo w-,HU'ls>wFFG~R%0#hX& 0gJk~ d uO`N3\a:iÅ~3kNZ+h=(x{64 (=`;2 5^c*a d!۹B` S`mC}_{^>,Bp=|4˟0x ( }D?rm)u#>murAgOP&N0+4wY}5rB duFxQVGB^2rO^/?!<x~/Ozhw` /_hzH 'jѪsz׽n_! U;@ ~ 068anGgA/8mO o;ȁН0פnoy]0oSo{zw f#!Fn`-Wum0p4{Z@'2{;p|x~i& npPRpP6#M@qwH@q loZ#|~jVAFX OQGtrLJ灅Vifi <<؁S|XGi[`|>b@%OJRlpIcbFfȈ(C~fƇDENW(`P>rJhPQ1Ǧar[Hhh ȋZ f`Lj`aGwdD,1&GHFeQ#LEUH] ]xL(PC!ҀK%%;4BHtVA!EPREXHD o#(KG$4M<0'8aRQ7S0y+i/ipWpNpWp 鰑A* @WP##,.eup]Јf6Y41AB-`P`RWK`+>T4w'`B $4q"NJ1`9lX\z%R?TzeOIَU9rDaKm;+#M)RHDT%MR*sBGƂ^0YBtc*XrmU:D ?uM1_#)C' *碛$sw U~a5-%Y bT)r d66"-5հ$@Q6`7KQl[*R~83$UK;q"8_2WҠiZxXʠˠIN !]ˀTvb`PUg}-h!-;P꺮ЮdIT *URy1Y=)+˲#)=#Ѳ5k \6˳7-@ ?=˳#@KQٴ`9p2Yq 9H%'ʙ ߁#ڐimPVٯrFP1QZS~XkZ3S42)3`%AvF˸븏 +KkYKWhHk J&J Z&97Z9 "c eV$n;J ҴVy #};Y jV ˽ +kh+}JH53>>R0 -N0s&PG-AT Q dB'ʼnRu Ӈ1%(fQ6Ɓ e+X|K?H9;= DR Û"1,R+Mk/6RuTe7IcPR R\’ӷby ud< 0k1lȇȉ,jK5K0nbv\" O bpR'"Rpu01 IbˠR&ԡXi:}NpʓEʦ '9'٬{)rFy{!sv@'; Y.-W;4<>R C ]"UϳA8]j\ *RK01vX25y2}XF[ N G\* F 3P Z ے9](ѨZ_ `=Q v:W<&p#grr4c@ ]0i۠L]Ѳ92 7\d` ّb,IKӥ!\wݬc1M]\! C&-ٽd( 1!uQ:]{]@VuPP]l\7ǽ ؖ tb=B2n q*B0Gaݱw}f ҺRߌ4%qRD]mZѫ(,GtZВuTZ1-, NzڏY' ꘮d`31K ">B&>]y3n15r#{mB02!P>1 IMN$>a^AwwTIP].8N{vz0Me\J -M@eE(~_!p}LFES'UX[UCa!}pL -IM]N朮`2 f'w!~8CvNEH9^ώ~C`쭎ή :@ٻۮ_Iͣ3>L@ 㠩ǐjNa*̣4_*-O@ɰw\CUq@*8@C 7pSR3 &}ww 8PɀFQ7V،/eME쐮C_0:}_*mpSNAB wp?`Q]M}Y~2h={0o!Gp{ƾǎ391q(O(414R>vNM-C}L>>S\#2'F&-Ev}CL!!8ѥ}}AĶv4q@4((9/q{O1# #H(١D$']>1AU-pU]*Q0:uʔdJPO!"̓Uw)8Ssֵ{o^Xjʵ=yi?̘aĈ PȈ"@`W.^ h)dę>\C'!}\ dqISStع'ӨS^/<$6!E+W,H:. !B\|pMLa38{^И0q#C@(sTTK_eM\E8•;4+eI%SO W/P0U ^@F8Ld0EYʐ6 TYߌ4Ț~d% laמ vJ? Æ3u)T{w$w`u9I5ZU(t#T l4Yv8X_,%WaWv%btQOj"~ԅ M1V%(EelC ɀ u:N^vpAZZ;0!Xv&Hlٱ/)߳$\㝑RRۑ,XaL+5!F I]QFG4GBLu0/1M!D\MLЌ]>YKbN⢛h|2 &t=Xag5U Pn?F8mGWbdgZtK9 ~-z =0ŏR(ٯDҟUH0wc}o|V>x탥[z dA\1*nm 9.L:aq7H! ?D8QI ? F}WoҲ|( j H *!G 'Z\*H1Wx_AP\ D4ѣ",lAl &Ѵ,Ѧv+0)d00°Z7R"66eYh$D6 胸JXx6 ke@h¸eIh{2CB8$LPF8a",6K@bHN#ECz ddTVg95.a E(B|Zkx*Rp8+ $iAL )ϋ.!\GЀ9, 6C2Fsv~ L ՔB.WQx@+hrcn:v" 6ņ0 ̀} B6ڧM)BkEOPAO!n> B5$e #PWе|sJTjҖ<'Ii*wz`gIDa*^ lF}_RT&;0@,@kH!.KVPؒZ&K82Hf+ [NF=ၼVH0?k+P|,e 7q'8]˲ȊMfcx ,ZYl.{^-P}Ȕ*ܨȩu ~pw,מ0(CEk0?1׹En+^rZZ*`"fs4V ^C1~LpPToa>!@qTV*\{-4{ Xjٮ]2VSưa-Jih #sLflw \y{ ˸ zQ VDG?YcPXfܲo p,ff8fVD@BHvUs#ug:MN4WFh$0ߚh9aV7$j8K'377 ZW<9wKs}?&  m AZ0^G:Xyu+fݲC1zb&e$bb!!ր(gcK.xRp6[[w` #&\f}K\9}QVczRNN%|d +&0E&(Y4Hwv/8ADkh < \%[0D/#xvz3fm|N6t (*uD8~aPdXi &0Jxc$c\Aw~C!bօDXI ~X(DM"4q\%*)9mg/?5S;0 p4v_RDBp| ΘKl0}zxh&B#Q(/$%`hɈx ،iaS@GuzJEgYXE@  EՀ_rxP/`aT8exHB7Y18Ȑ%I8i*lMm`Ov/$kF]R#ِ 86RM03')(8Ix:cu(шi==&p` 20h~2EYR&P&Sqȗ}=R`@xQCR{ ebс{nQ9)t1>i_hXGUYx+)y?(97e9hp 8B[}ipG#M0{PX(&r隯1(@49K7`}0ٛ ]22qlj o2Es!fP՟(؉A{syv{0Tc'_͐yy_)$GS4QlEcC8MIA ϐ V0Z8j {1h)>`M1#@ @Qivw8Ve`)u3d]\j 3HmZXաGQ* A%^lzy˜Q=4+>ϙIMP$~uY=%F)zx1 FZjta٩/j}Z^ay')Apw["gڅSOj uZ9tvZKu Zgډ_Ԭ]dg:ZKF2L0ڏ3HʂY׮8J1dM"*{HŧJ_P߂? Jez_k#P ׮iAE/*!,;ɊXJmC\6:#{Rh}`}  0"Y{ W_3`R-O[ S kY¡ȯ& f a/5{Ghml{C9Qx{ʄx'I.pV:KH+⡭0 Hĉu1M g! -l+[&ӀWeK58Y@;(eWnໝP~gNqsyڏLH;;B8 v+ w!P3»\h;Z%ѾK]ZF Sԯ Tv ]xeMM9JkVwg ](l&(5+3`rOr,lEDBa>Mm+(f#Pڦ}ڨڪڬڮڰ۲=۴]۶-1s}PR-ka}cp  Έvę[ Pyԙ4hܡ0xݥ@=]}M-[+`r &VM z:u` ݬޘ -&~(*,.02>4^6.-m7 #Stw+": x;p)@{n XDL^`b>d^f~hjln(Nס`9#1B  0@V>-*R01|NgGb9,{P~阞难>^>%.碀cw<ǏH'?A7wt٭tH!l|>|hϺb,>^~؞ھm⪾n_ԈH.@ȏRJ ʈH.0JT[_ ֢=ߞ0,< m~_ ^ʮ0/c~ lP# PLZͥ _68rZ @_v9UZ{e)Ȅsn1\^`b?d_fhjl4LjEqDF X! ̿PR*_VX h=q?_ۘλht6J-_ݍYE2L?yc_rE.8Eaoп9oMPK#& ܔ5f{w2bMC& 0Q@&t2@"8HXhxHX!HYiDBC#ںTTQ1cWj+kײtLk;\T1"s6`3,TV֕M3C]G\,lԄNLDa "Ξ'1F|:P~:|1ĉ+:S;z8F$UT*rd:+]6V YZ3aÆzYÖm[Z-TgD]ڹS&P;˷߿ <ilv\AGӦ,kڽ\|Ozb l-gUsj+ؗ1<}zj)Q+ȋ9 0bǮbݱʕ]@U"ćk8~̕Ӊۉ#=;\x%Xk%&- Uӧ&39)7Ї^UP5 l8xmbVVo%Vr+1Q H!ǩ!&(.h܇$,<~-hs4uaB~$KXR`&0Yk$( R ~H K6j0Ll`EVnd$ x'@ 7 P \9Zipx( C'ޠ/ 0 Jkyf "gȢzsLɓ}B Bqb] q10SĶRDENFP0ZWh+.iuP-ܽӗEٙ”QvTػ%T$TP w\E400zpg  QNquA~ES\p i: si B#<0 >; {i,NWsm<蠀=mAjaߊ EF\ZK'n0Am_8+]Gq@ RhCR;x ;DpLHm Og[%2aXKϜ4PiáHy/GNQDQBNŮdSPv4|7h pBX H"А,GN][a'c|#cH@qd YD18#Eځ3MP"yHKjRd!HG2R8a0;BqE[AVpDcr,$G$<ΕHvD1Q˙)Uh|EMLz34񗾌4Lcao0:2xy s h S CZNpnB*բa]ٚOdlB[*WuVMEfQ;bT&g? Њv&T!AҪviqjGZ4@4C@00 VAA wNPFI0vP, ) 38AھZP`2N^=zU9Q?a#J+x n8x0O;Oق?0.:L& R3 H&HA(;p¼ګ;h?hflA6\a&@val@  P1{')`+vP@8#h44; I'f5yC5~ax(6Z6h- І3\5| HȂX0[boY N` Rp[k WsTS hK{ԮN}g9E81p&P # u'"'l03`FZh+@Wc9 ~/+~oyțaܧkU{|=!TAoyXUg󗯽o=Wo=ߓDU{ρbyKԯx3%þ@asũo>M'Hh|}(HhȁY`D}Y')+Ȃ-p 1`79;ȃ=?A(CHEhG(!8HOQ(SHB}pJ]_a(cHehgikȆmoq(sHi([Xy{ȇ}(wHȈlh(HhHȉ艟1(`3h(芯ȊH(HX8{HaD ׈H֘8 p (Ո舎KDЎx 8(xt3"_&[S(@ux yY c9")ɉ(pȓ"2BgL" Bgّ*A#60ғgE9#tKy.;G+"#Vyx TA1@Vi+p"V+]I@)!"r*k"kJ锒ؔyI.9G/0Mb>)i( `>_ǡs1RK(MM5N䖑"<0YFMet"RF<,Nė~<0~P0>0u$B`>r+PN9>N#Җ'C$Nv02㚯I#و)hyS $i>+Pe0 pҜI)$DupX#1$?)yIqu;yg`FSH/y*>`>r14 v,Zp1(麇PR )R`! V*i`ʮ~9Ld [ Tk ˱t!#K%k&۰_Kp-&K*˲/K.;8 賺H?+C<)H˴MQ+*SkWP[˵H~2a|@ekgik˶mo q+sKukwy{˷}u{r0<@c`˸븏 +Kk˹빟 {#r˺뺯 +Kk˻뻿 ۻkkNjɫ˼ +Kk׋٫˽뽁;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionPreprocessor01.gif0000644000076500000240000002341710534177572024027 0ustar paulystaffGIF89a**qn]?><C^w3{{M`D m"܅|,+*;330sg[糾ݿԲfffy jMfD̓sssFy$$ZZV!aSliYq#" ̵.Ǐ CIɓ(S\ɲ H &$Cx0GCSg P Q”韧gT:DV]|KVճhӪ] 3جCf@aM@A lH` 1=@$6DĊLR'?̚˪^:~/fCNIƜ{o_ W8Уp$+8Š%Q'޻Wf4|WCa0Y5sj~4hGM]Pfځ 4TNu`nF_K0matWX}0p 6~ k1CO00F |pEDD/;,#fQA/1F@k6Z fHc\6@ )C\H MA1C3@ Arl,n(703DpF!ADC@@3б@pC@ nA@ln0w$qu@Giada5$H5\lAwb=1PEnP+6( 1`@4_Ā ^RnnS.R lxH ;{Ѱ" ~ <չ HBІ:D'JъZͨF7х '`C.(MJWҖ0LgJӚ8ͩNS;@ԢHMRԦ:PTJժZEBOCȀ@XJֲhMZֶp\Jױn^k]׾ `կ MbXzmd'KJ6}j^-znŬf MjWֺlgKehJڴ pKMr:Ѝn3ȢոxȮvz xKMz@`Y0ͯ~7yLN_ ]n!V d @(x G(D5 W0/7jfja C؆L"@:#;PL*G+pqV%5LXC5p圆,CխpǼa3Y@t0?Јs\.5k@f?'PҠ;K)Q[ZΥ1SUWxAȖr: bfuolZ:6@m"/|}j[s!{vsuY!ghyڶ [yު-jGM4vmDw94>j3GdyGӋ|֕723V|C_ѓc>nO^Ԭ'_؟|~i}\}n c}vZdMuxlȀa倣F︍(c7GXkhhevjhHd-7Iq؏؀j{>n8C~$h؉ iyO/yh18ieA0i]v=gFHhJɉxih47V3yl6eǕsVm?'eFPhٕh)@@vne8BWx~Ǹ )gQgj(g>qS֌filg{xVfiei>h eS6aYSlUƚxuSh60pƛieH'BVG0 ٜ pUڹٝ)sYy虞깞i6bwĉiP)fZ;dٟhPo`zj:ZzpH7[]ȅ&*,ڢ.z&J2:4Z`x :6ڣ>@B:\ݘVuHJLڤGV ZCWpV&VWJV[h_*bXW3P"AWpUpZЦbW0Zn:;Ёa5y ad0V*&pJ"eXd:5<.G`a3@hddipWZoյ[WWv&kbu&p˭R॥;V3`&B W[ ZpuۭYc {;Xƛۼsқ;:k{ٻދ;}eDXuV$BWBpv* o0 b | d{՚y릊U˿Aɷ[:|q̚ ܻ|ȓeVodpoZokw<+PaP=&+a@ap` ?7,:KF@p<@[PAک@[Z;뾽yPhU@J5e3oj&5Jo`&5Uw uj_|+GZ,{<Ȱ\w+$[.{yP욨ʳC[[;[3`<`Zɗ,>+ 5ʦl̦+" djUp\z|F *(dH+dUЪUaW0U`PGMZz) qPѼa@)}mDq #iٙJ{xI=VxpGa&PJ@]])`AE3}aPZqW`&pyUf-k rMAzVp^ü]5Pֺ tսgV¸WmixzmbpZ"iݿV-Ջe=-% ŝ%]X;Apڽܭdܭh eP si@RoIe& +`^WםFu*\p;0TF%UuLMAXuW%T=@Dw< NTNU.TPSWad{|SMFT2X (pT;۵;7g >.~bTIW" }jo RJGP)@.UBNcGU|BuyF{^Z{Y5E\pBuT}U.^W| xwgC<#I@\vTaTU3T@X@>^W%Pc1z@ pSnS*^ O(poaUza :̯P&^6$(]5pL*ۮm !_3.%7>B_EFZMYjIYgk&)0yϪ-*S_YNiKroVGlZ;ЬqU[|sn,_yk"g kf=LV쨯Nyy@J󝵾M+x  `@30'i@d- zZ͠GW`ܮljJp?^|0Ĉ]Lz@;@[ןʷ{љ|Ni>xWdM}yUuȘu"C 4v1sR66qƓv43qAVSwQ5crVz{ -ȇc%sQGǓ-Vs8-Ii鼓AvQf < cOԅ7Wxʑ)Ep2HG3RhSX(cejrLE*]ʤi\3TiHA`E h pd;4Zc)#W+.<3˚}v lՔ3L.Mz 8=xۛPy dɐ+Q9͛)s :%>hȦ˛?O{[ (P0;v胤s GWhA UVʠ`>L4фA`ana bЖ, 0  +p yP&WK|рd sM7LbjZ-q 2!RHŅUn Ņh1!@'At%AF U&\Q;"y&h+qA@QCk%IaTĢ5H ]>xeYb8jY" F[젫:h`stEU9\,5 ]GiM@ӟI n.jn^U+yhq AhQ ;l 3+d0.xp; b)AJw D_@ú|Zʋo@E)`P ׺ xQQQÛiGoPkm]B*`&~*p lvJp B jp MNV)p$LNiS0qhkd"1&56R` zl ^T)yS#}ȉ<-G,Ptl_ Pp! x#hHZzqNFjuwl!W85-h!R@*̂3x <4T`@ +QB:dҜA.PcoBTv`a!@ d5j9[#@P Z# )D% h i E-ЅT X.S"w =UҔwQ<7~ xX:Ԁ ( dГ6^aBwN 6% 9-PGBC5U!RA*P`cW 5I#̢Y0" @(qi,ʦ "!QlK2p%JsC6֍r5(k>57Ypd`EŎAm|j[#Qt{Xq;(A;.G`[ȼ¨wm{3p"s$z*KųW ,(` A N Kx0ˠ\R? ߔ!i,n_  pY*9XAuPH,$+yWPOH[c8Ik@ *`]Sv0) w˝u6Ɍt~^8ea`%S }y M hn,;[iHDF4AbE3AO2%=` 9j :H* ЉN>N\c]tB#'NuqdfϏ82$^^/uȬ˚L]TzШ,X~Ƃ ;eL)OfxNyE ZA P ײV(C5VBiT̆Fh eHC =ڮ8bdŊl} R'<kVUCwW@We P ( hcE= UPȁD#(C"H'B&+8B*Ȃ/A.3@2H7hBs?A(C8CZ@IDXHQ46:@p[ȅ]__HZZi_dhhqCt㷃4}(H:@x|8Ȉh؈؈CDVi@:b 8hzȉ(艝X }y\4@(~ɸHXXXX@;߸40XjX(hDĨ:Ƙw+ @X@;긅x4p )XYZX Ta [ Yp(#x2x& ##Г9`b4YT i\;iSApb0'SYN)7QpYI* (SP*Bz 4a!dS0}8+ +~PPSb Tpm[fz9aƖ)`) 9هY0nYX90AtYŘ<9p#S+`90ɘ(At9w9)dni9HѸ+Ùpu)pⴃ'yTzpp0i nm0CSj9yT&y@f~]HhybPjݙ ɊPținI; FܾssϺuQS1{yqy։KR@GfffQWfŴȡŎⱰz}ͺ]9;h眰SYň[cz蔒JRŽmvvFBB55>"}huusڽ{zf33?Du]]>-WǕ!, o42+m++RpwJ2R 2H2_4fp$p$$:. ="2$p:_2_Y*n:⏸1>|"DuGB4#Ì0HP@>`0Ō6czQJQ_P)MU(g1txpVyHk3/,ׅpƍ(7xM"G Z>JAa hbUP:@|qJQ,$ @#PPo,yG"*q,!U_&cXSmngɟ:ËOӫ_>}mV8$e7H(MnHQ H!}4UG*S GDH IS4萢$B;aeTOch3DnJDk!c4,( RTaH,QERd\v`)d&pR|MZU}AAp ޘYJ}~ _'}g'@e,%F I0B稤j[c:Ĭ@`y*^ [&ly\Cd =ݵfڢ0Fx+k覫^`+koMQǿ,

y` pvVD3ա `; V8p.uV # Z;ZֶV<0 gЃS 0D(HA5 $D0*@Xb].ڀ{Ӑ,H΀hՀHCFe `4 (G6 srj7`xL6JֲZ*ȇHp\*W&8 _ fz2 *CDh@ 8 gʊXŪ"J4 +FJB[ 4&`6 M1Z{4 +Mg ᩂ!a +@7pAT- =!`Yb6.AZf=mjW,@|Y;c`A{%/ c@x~t~@wyB P6>Dw#(ux#z7J{{IpXnInx5[0W#XwWiǃW4,肨uGa7wGn?p`l`z@YDysWWNvf3 UR`QrVxTX׊XD{րgjl0ЁvȘȘ&w#]txJVwz]IGng`7dD(I׉ >d`j`Y(SNJve hjpX ʘ̸#N %}0FpJ٨v}`PpؘE7~hyg kh0PЈYDžu/(YǏ 88Dh(k0X"HX9S3WKҗ(x\X~7(??E?@)+E-tXC(Z^`FJ$8߳=hX7UxDyb(qdX- j`ؐ\^)O[@\}6Xn JHuw)yD{nB/DlhqCz]@ǥR$EZZzܩusHȔx&hpCؚ|7@Wowu@*Tj?pQz)ȉn]_eCh<'vmƊUu=0XQ H\Oi6EYHuq(xjY`]5?`IiTYu{CHxqy`` Pn!859ιy5] x)C7bÝu4*U:IZ\ϵ7X棭Xff YڋMgLM9^I/DzUgW*bpY6؍E)imK䗚wH+lJ;뎡ۛquz@e**qSU7zlUY9zPۼii)*bKAGAuaj7Kde 1$Ui[i 0 ;VGi0iAn3Y`?Ȕw0$AGsN>Ic{.4JfZ<Y%DɥJF<ٗz0UN]Гz"Pp0AFph=f0=IZtNP,DRՂAkukI<>C0ɊXuh\yiCa@],u|Ǟ4CzWi[ؘiXQ3,s?=x?X5=I0Yetf1ĔX8Djyi\7H zP ?B?uT`Pv$,CRm*tƛxeN4v*]؆}؈؊nr8 *n J'D˻\4Q]ur*0']Lk,L֩6 zd*ISur䷄M~GzMG( ؋Ν؍9[ UzE=D8W,53@ Zz/P jP5i\c> 0='=eJt=Y9^~<,bhϚT eJSf }ޖ߼*rn m6u,dPC j>vپCy0GA$lYG7,^.bĢFU?ut.?7z+KT]{u}?.IP ho>qMH;BsLMnMh[MH$+U|cۿ;ߊf]oA So"WI>(Hk?it8j_PqM5M8wiʣL.fOZk3P:UN[u]@UoWgnwXnvxGX e5F\8YqJ08huӔs#67I9743&:bzjjB6 f4;%vؘ0q3 Dq=-j뉻Ifrq{Sq +6?.O_11U€>0 ⑆GQ#KcPÇG>0H0F?$U&n:eAZ,SkLz535yƯˆCgDLD;Akشmqҩ;.,{蕃G6R} 8 &EРzb`W!DF4Xʬ3@YF& q*5uHEMV ְ*@ VFr+]`XБ 'D^z -],9h2k>f m47Xbǒ]f 0hDm|sXNZA]!rmŜV|b"Hb 8$`Z) cEA DM{PL5GV#8F GLPF'`&Z0fCRo`MUy0l)AL|grIgvD<5ދbP2: )TE{ُ08$~1GQMr YZEi.>bCf^H4ɹ\\L lKl~~ơf1{|1vE'Ijj~:ǕǩZނ  KI{C;+p"]3 yh^*&t-c3kϸd_jdCL $[8ap=/&mpJ3H#Q\@EHz`P#ؔy E"oB9&#iT}@|R3P@74WU[.0[kfqZaɄ:',xu8uCMK,Ao>NZacꪧ1 PPQzkܕDa<1Yַ}k;S&^pPA,y KTDW WHp070̺((dN: jľA.w;WJrIHP-oB`^ې =A,P.| ?;` 7! _<5~< EVM\#$BY*.sf]%|:A:ը!6u1 ͵K# ,`! 7= 1sCZF1\tbM|OM61SL@!bqhr_I JܘFV[%Xf£1% ΐ$ $i01Їay@2d(D`+/6OwJSA4[Wh*\1.V0? Ѐ tdBH0FVnT&Dd3QԌ@)ƤADq R̹CqC7gHM?[+H|0kt#w a QSMNtB (I L Y@ɟ2+!Չ.9RʄAT_4%snj/ut]W/lD2Ԣ8]zr pSP$.JQF%←h%]'VE<K8SF{k pv}mwvL*D=*Rqa5v&V&MXBVGI .LWAvmdTaj>ΖX+\!Ї5a/-nPa],.. =@ṗqn>Q5 nG튘 cgdTUq,v7k޶ &`D lF0B6 U@`C/TQAˍLr+U ⤈ HizQasmxӸƳ4r [ۖAe0 !]/ e0_/@aEpPy!$b]FS0wT7=C,Q(Q).h@rT5 8s JZ4|gVPq8W5W,78HJ@}(HhS4 Bh؈4`8~v^[V6Ps C?vC!tKmXnH(HX>tPrp`q@hLjɨȌϸJ@pH0(qT?Z" p S nq'la6v%.K6冰H`wxtXvM>rPx8ȄXi0 !)(:8.ZX1uR = <-陨5sT!Gpcm?}p_Wdc<0iEfqbYdxrx|U)1Xɜ{(ɝɩ9Ԩ pA rpTqӂ [Ar= waBfwiXiЍ}`\pY`\9I<Ye HjB*{! +ʢ-ʝ$$q@hf BI5` hoFe/_YG>Q:!A'.ʝ}ȝ@ȥa*cH),pm+^( B6o,P'0Jj犮骮ʮzvr]E 1o0*3F6~:Oʟ́SgpF0g ?`z|WЦgRygZ9tS-/ 1+3z* I DD. +Õ)¤ {S{R )SK٭DD5P`P1_ a+ajp C?r._c#53Zg 'p'eY`|w!t tr YDZ˵^;Kv*?>@jBF0%\mtZ 9S=}˰1|gWuFP%P }pQjP3]ǹ1{ZQN "hpbL3T&{~JB tc ۦi\Wv;x?w+\yի`gًvs\#L%lsܫZ0 Zб/ߗ:"jw 9 BȸQjp`kGG`B3hGid|E@P.&/76Tt,]`!YYЍȏ ݘM+ŶəLduG^,Gටʧʩʯ ˱,˲L„"0˽˿ ̌L{njY, G0˨lBL`ӬͨLnL^Z.PbYS;R9'u !ʤ,mBl|˃ \wLlei W " b {PϜġlB`k M5 J= j)`t0d"`CEIN&Ҕ[D+m-,mam8 > Ԯ 93 v`wx pGT iXмA b ٳL:֕k}NJ\b0, 9ʜ҈ռ٭ж ЖM['A}ɜϙLڅYܬkڮ emAW|lٯUlU ϴ۵bɭޓ-g])p$m <`Jt$-3 =hϣ!ý` m7propip<",~G ? '"۱-lnKIY Њ]Ypsvs}s1t4'yg \]*DF>,lww.y}~elSSJwҶ8)lnq.MppNn>9Kupnul5.L4} ]b-vP0Cp.> 8MP,M@V blIAaBB} b\!przV'i'@,~.G`єSAT/#O"ʞR~;!*>|I ;rAF{ L *\a` Qvix\cd]tEiԸv9 <@͛T4P)TϟѣDΧPJJ5j!N@(!FL`̕lj}8Y0v8!D 1T.6ĽN=௿ .1A6HF,W,KZKT!$ d,gAF0,4< B'(/ @]0 `HBL7 PG5QP~gJC`-aS Sql/Antx|w@**B! ]0 8ӋP;:-`-.袃N:䆆A .{מ"on{/L3SL7<C.u CȪVծzu~0V8N A˨&F5h|&SE UU䑦 ŽCMkP,VEPdžHc7(Z5jU7_t[aMe*"I[ P!UfؕR%6EPT಍U&#^M@h*Ԡ !`eP CWM 14j*;5t3MJ jn' ~p{k!H FFh%1Ae?3DaTf,@B5E\/嵝0@m ` (7zq75(D% pu3JdF 5?߄-,9l`a qFLd\3 ":43uaeWՀ\#e)+`2)!s@t@ +v YqJ 'dQ)ZkP3yೕ&I^ PjbmYPg gpPso5TbPL>gi`"fh;!@\C?PMreBs#]?`jBtA3z'0cOO(x +xַ[\4iE~*vz8Zu+uftZVj5[r0hruL ))a qђSkvp.pqI7ulP@uLN@?.[769`XQ[ p 8ۋS#p 6LtFZp4ofdKt*f4StS{oEe1s%Lx;e|mB}0?#_Mln`{c5WPY,7:]:3{gG3á0KYcY# Al6Etk@LTpn&@O`m$=Rl$iPS%e%_e\ơV>ƈxIfu=% nFki1 @1Fz#7:cCYpWM+Y/}*n6S|N\^`zE?`F8v#w|Csbh@zn"`k}p@4cfbQWa8PL\LS`P&QDzSiB &,Z&Gx.ꕝ,CH^5R_<-|lODs-k0b]U|Vl) TeQ@@eg!%B&=@67&|003-{^>V^aӦTxs pp}#,p} p3;5܏ݨx\lEc+=k8Z,;=.,s.y<+$544 qswƗ-v;>؍@=D_w/,ڢEN0N?"9S5ɞa@D (@B0pV@U]|Y^51 Us5rZtt0vzu"DCL=@mC۔yU"B76AEAxNCo0Ǟ `We<Q$~7@NHy.G@A& OҶ'|N3Nj `)֛xwL xGI}j<`b`a5+2bߚ^L=@bsBiD+n_r^۶}v~;K n1J(血 wNwep^?tv xw d@U `wԷ@lTZpBGq6=G+t*̪`x^M]Ԋe 5NYڽc@LPKE pNn~:,.0;6>//NF p, O9 T@u*T_0?t\E~;OnC_GonhMiOoQS/GTVXOZا}JϽE/L.@N'rbP7 fCLosuB::\UK.URe/?_sǡPb%HnytO._,pc, vUU `op-dʞPz@alpbWsXQq4x()6x8Dp*)yczz*CFvаU@ gt \2!ga!aRbWӔ.>NWUG'Rqpr`PP}W0@!$`u !R4F6"I`ҥCT4rrJL1JzyC̜a^ŚUV]| [i0(> ]Iڤ۷r\ŝK'/ر̧F 5`I$bgѶm  7p` hd%4@#JD%F9eѲK/V`%ٲaӾ5re+EJ/a2Bm[9ǡXfEDyکF]s=D]qe[pAO_`pUa*54 YLYLFtQB`Ov[Zn8vH;EjyϚXХf?lʈzy {=GjAdIi}*xN` VL郛raf8c:MpPưqzOJ+Rhqd$?a D1Eb!FAKQRJe2dZCf)~Yj(6ok+;+;!Kj:d"gP@@$;-i.(J:^z%fEg:i8qt=MD?1PCS1%B`GƮEL[}^W:ְve=T@ܩvKVk8 w p3g׼i3am ` JL @ԩlb x5ԭMmPzUji~NpPG *0 9▁ymNj~\X.d/#4`w= w ^UF *P*Ђ_ u]<OQ@K\$6ز$6"7h3'a lŮH#j4[ƜpBda~ .paΞ!aA`6 1p> zx$ ?hF; e>9钲f>; xDh)Rnz/լ6琈`A\l"@&L+aP0(aYFx䵟89&s㎲Y4ԫ. @Tpa _f gvkpC O/`p5!-/bM̍l ,kA݀nF~_rq{b2o>'@oDÜw' QsBTe.T_Yb-L5|c@2}e؀6٥ݽ{M{UQ(*yjqݠÚ:?yuˏެJJJþ΂pfoԥ[[[{ޤgppf"&wﹶ;rADu{OV=<<"}&4vtoTTS=fff`쀘fm $$:]G333T[˵!, aY3&f&6T {y{3YYss%%Z~MA{%%3{&3՘`FSG6v|E bth眅0 r<Wb"G?체+1$CD0CCʆ:񸬗͇k!Wo>vVKuéNUHuKhE[.t\c=c@ mN$Ùf03XC EI!a$^a[[tE9(i mK ')ȓ+_μУKwRڲt(&A @ tfۅCj2yL&M6:qC>Wq.]Ђ 0ZmSx䂂sFdzLV ]T|xnR0(4h8c YPQOr%g ;(RB'TZRIb3%̸ 5͠ӍXR dti1"(UKXAE bwm6ݡ(r.q)/:j饘f\T0o%!j'pꪬ:n*무j뭸뮻 *k[4edz(<+zDKb *l~{k.p뮸+]P{̫R/8CZ;;ԠEpt@?q gt3B;('!D0 B 0md3|dpDa D2)7r8D|pWP5]e N(ŸzQp-tmH*BlpdhAi(0]l#CUF_`^ `A`sqجppB! te6ϷOi@=Cw!t`swC5d ,Kh1=ܼ[. @@ΑsN1f(~: Z @fH!txsZPⱠkX׸5 fYP8dl@Ҁ{}P,b*L Z5?=$ ]42 B ;X?2h́+&X`A b8H|P -!y@"fXڀc8`2q(X@J>{ wD}5 H)ҊAзf b փ![&yslZҊp-h @ 0k,8|B]6iRytd9YY6\ =,oIЂo φ:hʰx, 6I^ (M94>А,(л<mѦӨ%MiZThl ^mTv80s;:ծz` W 0i5HT!^j pA4Hmf؁n6GWUa˫rӍ`āu&u_pk]՛Ԕi}||Kv־c (8Ѐ!Tn2ɸlO- Z{2hmP^P SRrgm$)Fz,x , #U{^!P4i vam 覇!ՃlQa0d؊DfSlnx uW6A?9i\CCp^]Z2ѐ$m߇ LMw{>=l+ulތF6 39zi2gc;jm@@^v jk^ϢҨ5>=m\ 2)f{q)āܦ 5?ws br6Y{wOYvU_eӁ14ئ l DA ^Sf q.4oft;G?u_xOƇ0ۀi1Oj؃;񐏼']J8e @ٚ]FY.RW˺'MB 8)c;b]15ôއ͎_ˠ!ۭ'M i,s|kޅ?GB{oBO yg0uSvgngWzI|G4p$$qxE{^{4WfyC%fr 6PO}}5V-/k0/90J0Z=t"h w^Hzp2W5i7]R{vvwX_pgygƁŷ~Z5"MWNCCZ3ׂEPj@}#hd=MՈL5Exy(8'3=qP~(s\S}f8CwCsHxS5A3m$l7i(w:Qy?h$e#{<VM=V-0YBqGh| MotqSpGH5+s#u\hleB]P `ٙ sCw}hAtjPʨZ9 XPRdه9@V@D); xLN#R$W'J)48;&gX4[T>9giR:XV0ciz2\\pM M)cgA#=<05pq\Rgs V~:7^c:ڬ=ŨGʂJ0ؚZsz6n~Z4w<|ٙP2B`S\R2IatcR$cʛdQo(u7o P꬧>OmJ޷~s";*;#u`b  urSH+]6izTZc!CwqC묠Һ;X8We,: :I+Z@=T8D2{ 哶1YKET$;O@C}N[y`۴i6+\4{Tc5^ ۢo}v\U9Zb04H-(dp/rH[t$dO۞ۙ˞97Y5`s;5W+8s4o@)0;ꙉ5BipbSi:Pu4QZ-{7+˞Q۸u0[u =poW:'}ٱzhCFxFsH 1+-x=gTsvۣ<ڸG3 8|7DIJ6$c 7cJQ{  Jd} i06ʷA-pZ lq@A2q`Ԗ:Bն 2"Cc\|qS}.*q{w2Z,v ">l7,*}TY)00T]V}X@5З=]~(=Z0uӅ0/F8SNS}k,^ g]C@-56\>1,M=zv]ٖ}٘ٙ՝8[<,sce2Y7:m?JJEPFOʽl{<Ģt)dI-;̧"M1=E4ɛźm3/Ԋ-Ikll0^R 3ʧ\NUx) }`oU>pv{*=7Ɋ۹/~m@Q57<,HFp@Zs50PW;DXs-`Bfo4iڨ:e7>)ݬ!Nr-ӄ3Is^+-PDS54U~57Os\hB֣~87SfH)dFһ:3Tuœ#} V\CuCM?:=?,7DYܬ ]bnEΉNs"~n F~aVWk`$`FcXӢ[ -+` @z{xW6t4oa:$]IY/¤-¦tȮv@cĩr.sBsSP{MC$ <ڭuc foDH>WscFX#/A?%p5^  /OOK ` @lԎ&IT9+\JbD|PI~(4@Y]?:k/}CW~ GxXI5w@RpL{=pP*g~;F-u~"x-5ݵ:<ȟ33ʡ  @.w-"O$5UJIOЪ]~z0p@Hi/O9PGV=PX28GTHV'V*szWQXeYk{ˈp[Hq'-=3C]}TUCd3\ȒIZ J⁋FBZFG'rIҎ6,Id-O XYӏ^@)0+[w+رd˚x 3̙4k'܎w>*':vpA'y|/c?0,JH(â)GXT"+RWӇB!=TV;<Ē 85 >8ņ )`!Dr eSR]jJe+#`ռ&#`Gsq(Hh9O~XAt=!NX1pqEa}\WmL= 9A 'QG`yJ,TBhV#0%R |U̵v/|C'[ 5 F` [Ia*FP P-,` ̅HkO.j߻c$CQ0pB~ ` s$iDlaf."b>!!p MHF& 1H P)ph^ ABf:{x9(0ae P8F0AP&H!L|4H NBOKIy V~ZS3g;PcpZ dNՠa%@Aa%+E 9ڰ4`[QA B+^7=/$G+4(7 +j /{wB<8VrjY~dlD,Y&8apZ՞ \<`,ԬKYc؀]۾fkLGXvqH[ڞ$"JƀJABx"xȡ&h Ѩx-ZX Mw x#V7qI0`g8XwMZA \ V!@ xeT^.1 gu՗:H+b#@"hoC %Яc@I)[8mfLp > ""#UZͫ J9[TIy g8=0X(S@s3ˀflV 'G [` +8, #5yެp2;Y=Uy ^5C_;6ֹn074!%m!"Tll .BW 0mai+-a|Js iۍP# 0%K}8%ݐ<iOKbh\x8 66 Ҡ1t#_XC n)yBڷN|/u&MV0)xk(yЫ 啵qcJvS2n. r 7J8J _d2IyZ69Jeh>5pI_9lq=^C~RM_*P0Z?=h l|vS W@\#:v4eSE=~wFafww=(-0I@a *40nZ@aTAxVQ0J@Q߰lQ(l[40}a6}:ӷ!4`i&^e=q@z_`"-aeUxep2z7335i38KoC`=0Z@cf(soXWHg0p#mPt8P9#=MuP'1'quȂBB`SBUti bS#r7cSe` ;0@O;B@fi`miwmЎm\#q@| z"a S0D0SQZfi-[|6:SGF-`3ZQ+\k*9aS,.Gf$zdP;fDH](@$LلxU$@[0l^GV,tRN'bvȉ(g:z1-DbmGЌ:ƍӠB_pzȅAqC^>يYgY9ɘiUI2؁]M2u8PXXR0uˑ)X rS\eBx2\@&.G{tBi]e3hH>Y]MX;XHH:SVI]\44YG0&&ld<}Pys8nc0n.B(H:SP Aa)bH[saY>%'ԆcOʥ3h3Ռq`SiLR m,<ɑ{釋cHD$ri74vpOi6` :'zDee]@JZB@B0PyS`S8eJL癶%1B(E-_a`R%ٙ~  a`b`mcH\@ c 0h]qPE1KNxg`yQy%|VL :P d kSqy|6t{f.i X])_0a=Ċ T:]RkPg Gw} Iagy_Ї* *aq`B"vOPbUGPdh\`7m:xBd!{*%aw`Aw^J⩉d a?`J}I =0 jjbD0C`)Z d8yg r)+0!#p]c0m Q0[d hjykP {/ 䏢i+J r)5PZJwdAEVB/Ua(>(Mq RFaj.aN+K5 LWi9l-P"-;MVsg~EIK^V˾﻾ɛC晐EOf@k 8H`Aב[ >]V<l#@u6p˛ā6P?`Aa40Ad)$T;t<tS@DD\-0tPLaSPC%!h6A5>&$#0A>" AQ."g9z`>"zPhv`}{##Ь6 4H>"Z ZPdHEGz#";@# YO@+c.R|sGi !pClЦkJH`H@HGJu~HMxذ9# <@t +@GN"f*PN>욞[^D0Ptpd."jp/K>Cg .$38vY+Ճd=@!#-."Ta !gB>Xk9=gX@^OQpUoW_.O/'_XOa^Äʊ0ZM ";Hwy{~}/ѕ؝f~QݩUwPoopOo_P<K'tShKYFssBr{mOoǏ'fN+JBΌ=ض/׌!ʦay$;Ϝ|}<\;@M:CCJlJ5];8Z;mww]5K5d*y/=\l5==5JÃ˘Њw!JZOH=ldZZdBqddHĄΛyЫˆn`qYF!i"iؐ་C&G>D-AR`- @Ђ&ws3voO! ev΋;R"FSz 65cCӢ֯0Xb-`ژm߲m=꽔ɑ6eʰ`D:p| m<@˘3w(|,2CΑX93É1 YV bEMo+M<Vrm? qHXS@˔ɁdqH(&{~l>Ö<&̏/Q",ı?]ZiEQcu]|At|@p#F <"rE.<1 N0h 6A,T{kjF  6><"#RQZ5\[V,DYl8^ Op#,X`CЙ Mta%L7,Ĺ)C"67y3.8wJvٚ,׼j+A Jj+f퉉{?A nAM2Â.T,]o|, _w^ O8f)v-@%[$E% -p(zlڀ?L@g)-Vw |+XiX >ԁ gv4iCpN7A d'G!ɸ~Pa#\>P*cGRG3 }UC&iԁ.>A 1R~%7$'pr)I XJOsPfd TU:_ldF1-PF4 Mb!3&! qQS&71K6څs|[kU8S?` q8A &t 7N>E &!O=T@ @R*VƔ2b,mi_2.?W2, uИgX=Ҳδ L^bAZ P6I`>Da פR4 " 0{J@o0Yxd J@w$.0ZҲzlB9'xMPp^";W2(Boˆ! H,J"$!&@$iiiiI6zIrX>\`|hVQb0p A"]`bVRXE\x;!MKX%X؇<Ő2 Mwv ;L4qvB#>YO:?7a+k=/f?H5s p\NJ=/BNEhvY6 :h/L&x`o]I~XV&M3 y66jM!<Nȹwpe`;PBaR.%RIC?e/<|B&&p>("y~t@+44A O^1`{CKM!{peh|m/^+}>&\%|*3azx'a{u@1m8ЏOmOuɌ.m^z̅,. LX~p@?P# | tCK?gEF 1 iTjVM hHt9qt~:a7idWyu`|gv PU$ Q=%ue VYˠYEhPfPw1%,/mPrMh%hZ&@wl÷@ pk).^(7BkMdTO@!='P%Gpv JFI1R`' UF ّp3phe@EPg+@nfp(1s!yTSi `P*0'RP؄ wJ7]P`W 1q;"0=)Pr PdgO$b TE4$E&.VQ@=`X<y\<]0 脜؉XFX@PtVp\$~ (s m G&G|)SA!1%Qb2} 5E0]I ]P]5qZpcȒv1%'ih .z&|q0%A70@CYM i1wk WILb84adT`B-Hi`H =qDB` ! q%)xqUВU8qIGE( FYtF.Au,^`|z]rcǂ%}DtIslGMiyMD'@B )&ىv9 zK*Ek]BFE amPLI|}=T@eS2e4!Rp Rl0Z`S\l  ʉyPt0q|Ȋ-/R>7uTa@?o :vwqt :j+@VY&8QNO OBm) ڠYh@|9zWdE7:$fEn:p rj1xk o{*q0YO|BT=JC *'*' FƗ|w`6[ s4 RvS 2bق}35 a p2%SP)8S[D9}ة|o@hВ|0PR73pcl|@Ķ@ B~چ6m / q%* !X\k#=\ˁ6қ1} s~#[]J %qcʪ d04Zb38P`|< o}0 q'kLV{"%b<.<3Jp02tS8#`ql; zyd_uzjhgG[᷀K3Qs07=4 }Y;aF剞M}D:pB*%a#Bp2- ȑ%!SQK qqy`7RJ&*h >_i tapaWji*{'lPY)1 t`Cj iЧ@VqX!u°Sr'OpsH49P5(ɁɡX<5; rhI.]Y8Tʐkln@s ,@Bpǣc Ob'@0!qDO(z 8[\ OhR̜vql Z6Pp8¼Ɯ ,ü PЌ>s T%l&5 #ҁXUL hq9Ih(^ CRnMː `7T>n5AD0ODPLޢx kRQnY[#ienޝ 'k }*!ӬfNvP0;D^;Si*NG=e4U;z.83x3VxSbˆm^аgEwJ`.=7qZ7 {.۷8'ALANY>-BH䣨6pF7k_CP.C.0A]A5;5\^&n `t`aD=qvxm؇}Yi; |O[p[8S;*:kp؃e<$p04+%Zfz3I4o;C@2ox@5jFР}m/]tݒhI]aCn3 u H=1XG5` SA80CR K>l_/ǤdQ<-Wͤ]H0}OPmP6ldVCXmˇF Hs`cSh3Exh5&ɩAYdzd3E21'ӕ+ U켉E@-E,%`b'8Uji1PƇ ~'ÚA [@Gg!Ӻ8U*U*qZ5֋.JZ| 3̙-@Ν9-@;BA4L'o!2$B | ؘD!#--+ 6O6(j5Ni$,YldX"Kl粦9֌B(Tάƣ7y, iaőa e,Cj-"~vIH,WĮtI#A s9f֝A3=:@%@_kaCZT6F#-Ѕ BB_Z S|9ș}'PFsX2ZY"+EErLX!gEhZ5BQƉXF\([,j bJ-OFp"`@jq |77fSZri%a~JTq!BdAVlGG]NZX5\l\2ChjG@bqD2#a Fc ZQRwؒ!Qc)!*JtᎨQ"Q@ 6άPHdhr&IG+ PZ'mNU?iX|5ʻj*FKPt5DM4#1'R@RBN5U"Z0hal\XP('4Uci[!0Ep_+a)3([Hm/Җ=PGzSsY`In H!a ն~E=8 xPw 傡ymdPQhcd@Ѓ&DC]?;gӌ"Oy\$AmHDmih=W{& Dho{ S) k  qBN >2@Ԇ t &#:5 RC 0\(a,hF ip*( 0#S:; 4t+~HG2LuR!])XaS0Po"3(Aoa ؍nʟH>hcXKv5m<jP5H\RC b47<ǜdqGh28ElAJ++SJZPBժK3 0D20 q#VĐBHr7g{<^hӖ4BFr=  S|8'f-pR>0IM P?4Հx</" c/.6` XBY(7RZpB8G`SeUT{\|(VV~5m@.eXV €S&0$QYLHhH4iCk. 0Fzy/Ax@έ]k:IaXpaxZ` GDb!jCZ<ԥ@‚4t \IUu*^1ww2(Oep ‚'X%à7 '_-_t$]2jW%vgf8t'X ' eV^)(z'd BF+!dP:b+{[YIGxYV$sGSA2I Ms@uLqz;mV5fu`#7jGn2a tz|u"ц >v@q8|48^󚃼%'{ē1`C@ Hp+4|}?zB1:=.#V?d"(47]z}sw+CiO{;x+p;nD]PWj w9POPp{OHs;0#0y"0x0@qqgx5qWqzpa8o8pɈ;(5^X GKPhHChWjW<{x{gw{V KSwV7]Ha82H"200*r*#-*qQ"itidG3R2"qGa303DaX+̨*q0 9؂Ӹirp$qsB=P ɚw;y&yEsE# <(guV g@ PQ"ĹИx߹:6z8qzwOgiȀKKEP@qc8sHJ tȠUxNjq zS8 GL//`p*,h=008ڢ? A*CJEjGdHʤMO Fz/'@UjWYZ[_ a*cJejgiT*mo q*sJujwy{ʧ} ;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionROISelection01.gif0000644000076500000240000003546310534177572023644 0ustar paulystaffGIF89aJJU((-ӶSSxSSlEEĝRRgߌ PXXRRR:::yYY럽YYAHH33̭ʯSSJtrj||ff)))RCCظ¼BBB`RQx333˽r{{B@;llAܿEE}}֬{{10+RRfffzmmJJRƺa`YőКzEEfffZ66ss[[vv!!!̽.Ѿؼ}}nn||ߺ[lkmmy˻nBBHHXƽll}soQQlXX̶噙f33VVG;;ŵ{{**ɼ{qddž¥;90ޮ{{;BA+{{{IIaF jjfff JDDsssöuu+;;IQQffɯYYTޮƘssbbr捍س޺cc5**kppyYY̙ѻ||+!!f33JJJffffoKKISSzzatrJJquu;ZXJssZZZ͙qqKYX``}䥥ScdΫ쭭ЪD11! $#+v<nsU˸1YK`wܩ0]ʠU|- Ή}s<"Yq\>e=ڢkؾ=+)]?~x)P uRȟTgߟ;ܾ qh e"q .U(w@Bdd!V xna]h(#.l3Ha&@>i$CjXLv$=aTfKji\b嘓d)xo֙RکYx_ b J-i6jh*#hJJ$yij$i eyjn & @@ 6:=bͯ"k,VP`C-6m;@ԆMv,ڪ b;.. =*6`,PPթpL0HAA&LI3bRK-QB/%gBAǼp1QH3h4!M#bD!ӉMq4μr3Pr1|abLuJ3rZs/h34/QK*Ő#(6p2:FÒ ?uD8GPPK (20.cEKX  K\24=c 3 [ CH07ct-"-;DC!'@2{{2(#[ }!+{N# `OS؋rq o{_ ;/xLD? x{hBdx#^*j@xлE*[ (@ *H( ZЂV 8[NUsbUV>ݢ+;A*Z~.zыʠuF-nj\#!XQȢ >B0BvBA Mъͨ &CZ6 *E6㓀@) p1$vI+(VZycx;9~1_wٲmt#pcؒsb,߸JVF!HE&L#RH&;9N~0QMN\!XVO,r|c@Zz H5@k#'ڿ^yD/Y¢EȄF1$/ ⥰@'I&ʝVZ zRўx4G2H@}CBy "UE؉5P&EJ+p%G3B H6hNL%J֍;c#w܁-d\]bh$j i.6X\srgr1KȒĠ\) vw?,M!h.t -aH6;Jpb$h͸*T:fNԐ45@z΂$! ȄMԓ=hϫsDX\nh鞱t}' qPnZprfŦ}CW && cgT:MPbFw^pL`slOڠx+.֋=lEҖ`gc#ؔV'b ]l{>}NC6' B;/,G+^gzN@ʵwNyu&;WxǪ׿nv .wc]tWLY%nfְC%W}w}wlgG&sjGVhupsE>ytT%{a%_EURUdjRxurtH!M|uy y]u kgGw@hݠagj #s wzp<0 m]AbTPGUU@jT 8IЅGWprwHp[_v%SPgR;Jouq;L0 u* TWGꠁH։G Lfh}avnvI明n ݗlՀQڃ\DFK  h֓MJHJv_W]؈ h`rp*Q~Mh։")hw"b)U,ZQ TPaXtwHd%m]ta5YMRAGYR3 ' H#p %8#D*C騒-9IHg]dGwC 0]yEErUw_"DM@5LZt5_V؅jk&OXEfhV&hWm4tn,=pkɖQ 4uɣdG C} kZDI ]H'&^YE0H4H0%Hyj o?=G@hJ&@VwdiYgXwVvdyHQtPsHyTiO^+B ɣGNKxZјӘD n I x $Ȓ{D3;늦ٛ Y|X КǪlB`dhbiZմ{؃BRiAcTdxJ6OUdMZn0fjN;kivVY_M$j "ʩ>g~y=^K(ۺ7S{L j`~XKĻ(bm=RvH75Yw uJ%C jAIH[p[x5W. xN#Qj X p;zu PGwp d\=+|pP):۳;}WAƑtu^ǴM9SVV4F=ApK0(  89ʣa^`^0 0- 60˱N` 0 Pa } Ɋ(80o ? ool NP  + \,}Ɍ %? S} P>Ѽ <0s o p S ۸| +`fHp p^0%a8pC &0 հ`Pp 1xH02 ` a P +pp?0f H o]L€mp 0X .,ِ  ٸրP]} pPpPH f~o - =հ p ] fڷ 02U3 V @ ڀ  Wh|p -eu' [#ݠ % )И     |Ȉ'H )p^5n@ J C0!>!>J @* G4U^R ~ |PH:.H㑠PEG]V4Ari5d.JZŗR7uyXp{aBtP P@FSQ0wz젦-@?BU VyڷCGwt cWt  `dܛؕ ʈj4j> Ua wؘRTDTVOHΪ&4j)U$.VUO~\40 5@sm[lڱ@kRGMc~/}?U?y$k}K_C?AY4ڕYص m 2ZWeN<k~G-uuVS[%_@{8f`^(4yV _z pmշyCKndV wmElV|6HǪF=_7\O>)-y&VD{Xu)% }T^G@B#E=+NmogTI~@-B6pg?PH5fR=QBP#B  +tST'V4D#n RDaS)K@J$̮ TKre  #;eGI[@<}TOHTuL|(Q&v`M$cSu[HF %@KmPp tCXbv@$B(:Ќ2S̸QX*"M2B L4m ;o/2vH-Uѝ; ='ST !r62Vm!С[:wh׮`-TQaÌ(b" ̔2 Xȉb#& '$gNCͳX dn % N/rh!B9eX3ϭ{纰JХNi?&[I(\/*3r.ɒ0seģ3rl#(hp4F AK1Ĩ$VYz4t6l#17~"QQşNtE"tUS/HuDTk SzB(5(RW`ٹ:+ 50TfM9?q*2^ =T'E+(cƄ0"R|$,M% ?+SOCX[ xf,`9;/)3Vy<=dN&Dx^Id`=$< B,   v»*!K[Mꭅ O>x&e>WDb;qt>)U$v{j$7R"a‘UP`X(3(XJB!Gx;h-FXiA`+LB "OWHt@Ѝ;-LEJҌ !\H,u!0IIܢ/"D%‰NC8>wp"T23 zDa&(tFqBɱ S`h*"yRsD\V2!lHUoD`a&[xC@XPFû;3;R2Ɏ M6c+1n )iAvxڒDM IW=4ZN„Z[mnO=}%2f 10uaL`E@Dۃ(KLMDn#4+HQkGonBe(l(xD:Q.!%t^"U&Ha3pN7Hˬ(LiaB L?}KNQN6Win-h# .XH0}Gvvl5 ph Gqu{ Hv\1LjzT#3'8jR.be iSIp?-w5''E]t i71/ٺ)a H1o0FvfltKS 1OLR(&놊Owx. KU. ^ZtQ $3 tMpE VȌa5vzs:!Da଱ L0 I|t&Җ<WM6I1V uUOz2tf`3=T\luz8Og}dǼxZsfHuho&L BL>P%/^RaC*Tтɭ;(#;wjԨPf"dέNJP.A Ln)BTbT\(4k$FZzKgX"H!c,]UK6,)rBk]z{E&:u0Re | DFN[THU&;(t"3С?<4l$ȁhT;0<2B @$PNwJ,l.U*B]'T5+!BKYغ- $~˗~|& k5|+b|Ј0HTR ӌp6g9Z'=` PV [od"c&6aBI#wD'wءC5EPNU] -\PXeZm@(~g}q)|(VHpٕHHV#0q30SxRmQ279Dd IhݢԦKaJV;5-2vN7gʠ@A#n@_NP7z] חzEJ`l\0TvhbGRPHLP eQXN瞘zG!Q$('imb{i $@]mkͧDtȩ;@3L8F-r tqFᏯR`B,-EJ PdKv"Y4c|ٹAKBs -TD5ӓtaa=; &`VkRO8?@LVX3G։0CU[QNIz%C2.E7CKSU"9j>t1Ƒ5b)!(Y{z7DƷ&L@J`8F`9um)3y{V[8UّY'@Th2>kaѠ7&ze4&3Q#RFHafI:c$!NaNLJ3Fhlp$^Xѐ/(2D&o.ޱrHz(g&AaZ3 Ii -#YX뜄.IC,gS f t*uMog T}>dr/(TLH:Dz4a)Az+)G5FU($crDm@(!L[#՜+D+ K\*$r(X]A ?,,$LeRX4_K l .'"0T%H y#4B|Ћn! ЋfN0=D!P \Ќ@ JТ؆IAvh;@(G AEaL-IFD*С eW E ?~PXA@ "ݤw᭽P?ozV@x+Z?X X -C 4@C? Ђ2pZZ(D7\ G?Сoh!X?Zp,ԣ6h"`e" E" -4!S D! -0A 3Jъf{+ }XO ZȡA`(=x4@`W:<3<#/S~.[{S>魯s>xN?Ho?ӟ???  & 6 v^EJ ^] Vv 2  "  r 4 Ҡh*聇|_ )DC88 z+ )( ;>p_ *B  *+tt8$D6a!"jta ~[.!"99x;b*'84a"-% "//"00#11#2&2.#3#<#4>HZ;!6f6n#7v7~#88#99#:#6.;#<<#=֣=#>>?#@@>>VA&B.$C6$=6DN$EVE^$Ej':B"$G~$H$GjI$JJ$J>$dAz$L$M;B9N$OO$OF*tAl@ @PA@ 5\ebY=C<W>FB*B h@ pCYh&x%N,5@t@>@zn׉++ƫ+֫$;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionROISelection02.gif0000644000076500000240000001211710534177572023634 0ustar paulystaffGIF89afff333̵;;;֥ZZZ{{{JJJ彽fffBBBSSS)))sss!!!!,@pH,Ȥrl:ШtJZجvzxL.zn|N~i"U!BZMS"BZISY" B!CӍ!E ۲ȍj" HO^?Ye8iBs`@q ) 8 DhU:2X]8`?"r0/L:OvLCt) əNDV|LbaхLDPtHӔm%Swj C$!qʐ]ډB3SuU*`gI:+[` y`+:H\J iB#jٳptIUQ}oE3(=@N0W`%J\N"\# L`EF/tc%P!b84phl0G(V\ bXu-uš Rt>CaH\H^ǖY`(pӈVlVy-ۭ/޺䯣ro@'/o<+Oq@`g0/X/p;=ϻ}ǯ~/>o=I@*9O{]@-8Aݱ}ދ*5Ђɟ?0yk(e!8COKqwAfP?p14aQ0 <6WtC v/v0 D*ЍT,^8,yw^XC-сbzjPRH#)qDx$А@Rc^o@?0d1h,GyD^Hz(%{IbT%+fҖsTI pjJ 1=2 1J-3L''RC֬&6nS8KrT:G3~3e=rqܧ?)V ԙcyj̈́zr 0?QFAm@/zG;*lj!eHXRqJ%K Оqʾ;ͩTS.UC$(wkSƨN ڿ.Q)S1WB@P$ZzOzot+5+S|/m֠RU4jKw}c*l1[3 Ԥ)]q Zǖ-lby+FiMrmē|"fZբVlcl9j/I63zK~OnVp%y _ZS ;ŵEYKvr❌5f'!O̤p 2ܰW a;Z ۏ8vY Cϫqo"W  ;]D5*[ZXβ*$һ eKT6QH@_jP̻rԼf79p1j.ؕwf>Cϐ~v_X@thq6U3iN{.D iR; /WzijmprI>ZUNle#ЎMi#MƎtMrKv{nXSlmž2~ Vo^znyXv=oX60&.n|ƝpK;m>kU?\*w_N<̮vى2%$+ Z 4&l@Gc^퓩t&^սiwXցuf{Jh"g>uzWr֋u+wx7'tJGs"|p6÷/wq?_+y>ăT􌧻yԛ{v$E1kWo{F%q??^1MCI+߽nYS Ox܍?ү_@pEysSw{z}'S X@ x ȀoE7Wm ԁ; X!o#H/g('[ {xz4w6zyvUi;ЄN(kB,C8E(s_mGMOU6SX}U8mWmYc]acet%QŅLphr8WDP6zUa}8k(W(|=m舏(xUXw AbK;1h%F}81*jfLZ$BxZ t/('VF6(B~khQhHGgU6MEȆ#Xp@ݸX>ᨉ2RhrT툋H{;׸؏8QLV՗<"(r&y(i,ْ.0=)Cx="9$By1YF 3i#5I7ى9Ǔn?$CGGKjO9RTW(ɕnR_ITLi8Hgi %ɖ*$KNMx$Mؗ!I)Eޣ_lYX-9I\y=)iɒٞFөeV֊l깞1ْٞ(Cq7Y >:,YJ {\֟:ʖ df) *j"l$Z9&CPТ9nɘj5i7l9(ʣB VʣAE`Zt9KhţIj] aJtcJ KgzZk =㦪Rfrb vz {ڨIT?[zh騞YJoʥeJjJLJN ?zX ک էf/hFF?) P>:xVҬ@ɜtrhe{ʺ" JNu׊٪ܪA zڭ*[z[{s𙻪e 0$YzJnnر "K#klW,f.0;4kYr~ֳN@B[zh£/ʱAۨ4[S{U@샵J۲[]P aKC46je쳧a۷~;iW(PMvx/z;{G{냏+<|;[;[;TSxU ij[y;fɸ hhNW̻Λjs\D 㑼3b+BY`;IOdZe뫕{KΣ;Plfۖl,B{Fj\ie|PpFkC<̿ࣾþj(,\/ 35 7,KUiʃQ@؞R {t-R{W9TsǤIa,ɨwŗ[lrt vL\Ac9xD࠶h |ԐdAk} i<9A+z1B:Yh8CifetnD es5WQ2ۑjJKĆj4;,-/>=~KpkI" F.;4Z:\l iMb9t%e}F6*V;.^Pa$N! Ș Q J`ܚq zyt~}ĕjwy f'b !(L| ~ndOc0X̠-n !pF} z#-E@u-=ATPE V*aAH7#`K4pT rvE'-ڒWT05Jx@V"XH 82W˦ךk` PŸ"P,8mU)HQGeH@ᑭ ?x A22fM$F С*T0N ar оH+W\'õb4f-$$oίs̀fFC¡!8)NLD1u+(Ĥ5Nt"pÁ8eXw<1,Ҁ`CI!ʰOr>P`2098uP 0iO8IHaJ v|:@:(ݗ ` "aH\J׺:d?[QK@Ы8A6-9D &lNцVԲ`M+v z;j <`),XOHBۤ"AR *PU `#g[sTq %kJͲre QE(ΝdFҚ~p*X\qD+{~֐;Hj2LL+ ZrՆ\0P}rF0ؽ'L [A42ZX@|‘8; ATG 'nGZk܍N1s\c,aǎycoAqWxiՆ E\nlQ̙U1+Y*lcgyJL^r_?>K]"b'4x#Fo!D@hb?XAs9#&i7} pW+5=t[%q:gMknDRjȵs?!`r[ p EG7:sj[v7m vqX !q;&<˦vMzη~4NFpҤ8"He[7{ Gq(OW0 8Ϲs^5_ЇNHOҗta 6BӧN[XϺַ@oNd'AvUсpNxϻN 8OO;񐏼ow(@ '{_˯:x-ʷ{ߣ <Ͻw ~ @ O|Џ[lu@{OOׯk:t] 0x zL Pw/Wv7{BF ؀-{?Ёȁ8"h$hp'%C.7`/x%?P:XcЂ5k`@Af )XL8؁-0R? XhB`y^xy-pyZXfxhjl؆nȅ_hyZvvqG%btwL8HP腓zx@8@C@hy8wdYy2(@f`Dhf]0y rPrĸ,J'ĨBK2 vlK6 X8`ŸQ"pLp'舎訋숎]Ow'b} 89XpLjpywo`F o}0bkHw 0b hkCnb %@n00f0Ps6o0 %)H{HHO`1Њ`fЊR ^`X"wЕw K@b,[yLЕ"dXKt2XIiɕgї|]yqd@y@]2  IWI wz@`npFPFopw}bP)kp/`w-? c?% OwD,Ў8XQ)KIvv]`iI'pLLHLpI^pDXP8숟ڟy i٘Ɋ㹠i]h9Ey*.|+mX @` يbyCuXwcoУ X`w@1n 9 i%0CBХT9?幊陞R])I9ɟyJ)d*xJʁd0Ƞ@ "` c`_c j=@Zf(*ʚאpnɤnFs6F}@16P@` CvocL/ b`naY8Luw::hДO z6L ʘ:ozXZ۱ 8Iʐzxn`bfTwCQbF 0FiYy'}Cp:y8 K [ PPي1jQ rLpP:jzh  c0c@d`+, tи'ʏ;@-J#qL(o`t}0 @H뉖8x%Dtp[wb*iTɔMljZVp)py`f`-hnЊ(Lf xʎI|b~ˡO@\kJ+\ܚy5r/{Kkus7J͛_k\+L@k٫`?늟j `{ l@1 IٷVۊl0,Yayd  h**dfL@Ǹ;t\v|xzopy OʡK˦mʞj |,KDk `uq yLN)jmp@% Z˾g<̺˿7Jz7<\|,\Sz-ȋljx68]`I۟J3J 4|ӫϦIÉF*ʘ[uL}k=,9PW"=$]&}(*%miY8XUnZvFO`i0tq@[],@|,PUͶ}ڰ]`f늄ݙp l};zz zB ٔThLlp{@ԈdJJT@pԞQП+@ l]-׶mtv z}ǟOU^^{;Wm%@?tJ8IZطǿT  FpY jId1ַr۽~y7mX*Uۧ}a^=LP"'X{?w(j`߱=۴-mݪ]腙lߢXF^w_5 ]`yŴAm逸SB=#60?ع׽*39ngp@jA0*wgnt\@j t} ^ܺlYl UkP X?xPLqnO] ( V Flm:掹zG_aBwdp pZp.fgP g$PgN6URjm6%0Uܓc犯L-N'ԫ M.9Vn wpk gP kww\+PpW'p)`W\po]jCkJkKK &_`0 M,^m~8Lܨ>o1d~EG/J?gpp\pP]N~\N? wxG1̛Ȅ/_<Z*o>N1Oա[, a{].mϏjO 8HXhx)9YhUմ35 CrZs崢pS{vrfsJ]v'cDdQnfec BvRdOf1I0… 2ŏct 2` =2$10yG7F4i$ö<{ 4hК67ujTjLQ9 S .8XchQ00 2mPB̝c,ư{c2cVV̓ ;7'ߘ'_~ 0>*v 9l0lJ#s Eq;ċm'PH" ԧjѹp :ݷK==*ڀ`3۷ƍKFL8A uCL-m=)HճF-vNn!w9e\.!(fR-c>dBIwE{,_f_J d$!>ل*O -X@f !CO0EBFg= #mÏlC{hԊhh529bЁni~ j** (^e8IXjD]NbQf &,Y w2wI(F $cP~ n"E))tԁn oK/0(p}Nv;f?@a@ `Cq +LnGLDO=yyEC]R*n2;rI.:sϠb ش77N VJlIWI0le\°C<1CVV -L۹lkI+)2P*:srXJkz WB}[.MFNt:IN  0F7?@:rfLt"a%$k½ ܺ~"˽{3x{B|wZ#gi+7l`ۀ ;M/LgFRc)DO@ة+n!I'ևn.8AIԭ\C jp`P4^G(Z/hc W'PWN5@&:԰B C!/8h56P8hvK0 ޒ'\all8* /k(Gw8 b!6iCݣ5[?CDq K9YjgCtDٴ`t k"q .ixL@9X l1Q@5)MjV ̴&G n,K0o;AbIa@DVF4JӘKy(ׂPIcd<).ƕ*dEu Iˌ>´N8` ##6_R>4( 70~-dD1,jdwI⃞0 a*0%>a]*ć>`r`C6E ]\Ku4:)bm JzǓvCP9un a5`h:Nh8O"o?)9KB 4Ra  g&.\BʎE qn5^1xt=YYg ^^;U%``^hdH`00 ZxNaHb #Le|mw]~f,(n6}1Ll ]dsh5=r6nnS88(KYTC5 0h!T6H6zs !F Ѐ 31`u1HiNOazHIzq$Edc&dҏZ:ni% ,f)BW]'4G+% :8V8Ȉ]&U}ww3@]!5&sx07X#pX=m9~Vw=1XdpHA|4Y1DcIDpreDF+NQ4T ;0"l-c/k5>5U-yI 2O(Vr0h('򚋓~0]k:eO&@WaYwץ\Xl0gx\f9W?`c#փlw]G%h4Yۘ!:Z#1Z-0zdӡqJ@K 8m`狾sF˾+;B[Kۿ+Q0` 3J+JL*f>xŠm&ƭF) M "$ܴkS*x Y`IǛzt !@G&$EjV)ǚ<*ш\ʦ xc1`u4c4GI#B69`@PĮ!BX͎@N˷Yuy0+1xN  `~,Ѐ 8J/VaCIZNRHl0TYQwxmyס}s$l iʇٶ4JZ;dpnLI7 k(=%39xs]-ެCKMKQ.ҳá6zK0% T<`4y0B|JiKH C3°S~[b_x"B_%ZC\f& 5e^6~I-A,(2u<)0W~4U=*xFk8<}|&Ĝ6eVb"Wp>#$`НLv>QB5-^≾?C:D$`}'``}'iN4~eXIJ ??bF]fqv3 m㨾[HÐfB%~Ξn>{'p:6V4BշM +PS?0a U݀hcc8P]"+!& re4f2դsMդ*?LHy@%os^>q'acZq/`h9 {"19>N:STqU@hauLC":udB-RTߩb'o5,c+J*19 aZpuq1fc?,]16d))qqLL""'LL'6y]@ff]]=Zu Z8F=KKdɟΟ1@`lc,Oc-,cCf@y2r0˚L@Cp*PUjcưXB0fb/_MvL !t`% ,0%,0 */Z JѣG1QF!lIZEi(RXg' R &UJ@Ɔ -`TՅ%%dbǏ'`1r)]|4` OyaM芪=lجbǺ}"F0c fM T~bnܷk 6;K8щ V(T`, )z1iX}"ŊUc.u@$iʡ/a AsϠH(B)ŔSPyvقX*l`Ԋ`603mц3fad]WJ%3%S É'Cr DfgK02Y-PmUSDUZW!&6yMxaB`"c@J g1Msyؠ4J@ 9dNgFb桗!TI)$T|X 8 c8eJM=d `@cX4!#̎ > JbIi] П+ GH9`DG6:Y:vrBhd0$QcDi 0>8ƽU%zGȗ`Θ~ צ%IĖ0ֵ7Rϝ 3:QBgi{'0}|ŠZ4qXFr QR2mcdqlI'X^'is+=4aw2E?:g!饂`A 6J饒*hE5ō!DRN_Kpxqnv:[tS42-{.5_0 *~ĘQA !eRbEŨq{5ܲeFE_1$>y`p /^ ~ B׌!HR=/P{s;Ȣ6aG UX`/I4I+hn՚ך.,/|Q {*?R#GR,j;/&; PTh5r,l%ꎑ1Sj| "jV4ƔỀ<^S:DMـh&=n-Đm@ qfo%?0da)Aӎ&sE# ~9"N<ֻBӈVbc^u5ތyL,q,BָѨZХE˃V Ʈa eeTiKVmOvHƷnx]ܿ@g}52;cU??eC^+U ;oA`> ׉oO]M]聼C{N7-3Г\z5"uCN ~FAMoӭ {1\Ĉ q|Ih!@ D rr1 CP-7AF~g~( ~X' va#isg 1}0Cc%(8WJX1yBy$'T<+7sE#sգz<䁣dTsPmꔅR^Ւ'+e 3HZ5Hm7:.PXZ`}E ?JjAn ~&S(wj*'1 ~4,fxqzril,ւxm-<1׆A LDEc-7W0X8 hdVsaO Ǘ],~|b :؉J-i/HԆK]|x?H+e$Fwg-0h6~ƈ`1̘q1Qy1 t.ff7 xq`6eS 87S$Ot;DA tP怪&%VNb( \VRC0l"@"tTSfjG8gRŗ^?rQY$9L}G(.RdPx-*PC*G_A/~^@G>pjS)"?y'Xy2SThXq!TȄjy,1ٛ҈h@dn{ ` i0`f{hwl#|xyZ89I$__Fn/O&IASU |"YLX(m FWp-(SYՐp'`, 2 xc"uCDC`Nx-.y":$Z&zIdTPf-dWtڵ~rSu6d זŧG7YSP r dX%8 QYRHoʈ^q^x{“ii:m d(82@0X:YdF]3 5i󥍪915n`b_0qV ٧V)r|Z(:Z( },Jsd+d;w{t7 FpUqu{I`#pGҮחL"[Y'8]X.+~*Y|`7K8y|@*'>apA0tD|O# p)I mPLP |50APqq5PNqaNpB]H{|wgᷲf~,Xk *0k:/18 R7u۹+Hꈫ'96NXx|-VXxFprb x(b!m% D 4$ Ñfik g60u @5ۼLLpJuIRY  :g&H+[һ[vq'P7Rqۿ5 )b%d{h6`8@' 0[_ "<$\&|()^wksKEzB`v~N " HjLpTBl[y1СbO'Z'y]Q]6jf>y^4-kC;)#eL;{uVȆ|ȈȊȌ,u.zO>-@8]]mW :!{;ĜFgKp|P 8.YNWq8[(Y8Ev )0NeiRq:`{śvx̮7\|ΑkO-q B 1u|䝹d:'Vy:QʑmBL,6R!e8ʹX1D6~B6L+!dod ipB-՘ۈsц((So ~:|HJ]̏.,@ @{rĺ+H0%8eYWl6 iNQr r m }vK*[b' 1FeKfƤaq,6m4E@ń2FԠڢ 3?p.vqU}-W݌w^bx֢S$ʽר3vy}]N}lƃ-#]b_`}!`,t?yv1kѻbo`֡쮻@0-[l`鯃\A5ppZ:K~L  U?)6J=!f0Cp%SxHGe{ll|{s5|M1W1-CpeqW olMf33KT&J'qGh@+xodI*gopZdutFR@"9IYYv'c *:*d7 {”w+' [;L̗”tBlVf|2K|=+R Zpa-";X& &bcqCZ C2d/Jz% 36DW>ÙF!Ǡ8?(_ITO=K'=P"ܞЧqPT\REtKEmӨIN 6{A:A &砃uG m=2o yKQ1X>\zռ8SLO'I tqHA ԋ 6H= emT{r+ %)6z+ ҂^p4 oC kʸy-#;lѯ^. hH1&O|"Űa;hqZ`*ZQHD9RA#AmxR^׀yM5Xr7q (IEFv7t> (t;B7 C>5[•^ق!v*.t x4ox6>P$%E{1ؐ!Ub zW'Wlyo_%F_yh5渾t7Pq>^LJzdbtCh8E =2)Tl198mЀ @Aa=T<7YHTzլn_ X 8 + ȟ^jz^>k1*&5cI y)iq=7qs&i$zn2!)0 f=Ř9>) $_f|X..fk|vq-ogv-ҌA X*G?`\>kiRj',U.jAigf^M!0H?ÄK2)4qLAIB> !zKRԦnu <9vB/^㵾utCntO(~V=& 6pÉ*/`e`? W% Px w hl0eHkwyE|p%'Z]lA57y/4S{Gސg4_|y@_[Bh#QwDygY?e:S O_sZ1(/p~WD#aq4qwV-B 0wQPx>eUP5 0yh5lPN`Wl}x.A J Ae+5?`)Y2W@݅Ƅ=H]tBH[b5%4ЍyR?zYW(dV/񔻵4'+]cr`5jmp |Qqw+V7-vV2lh5i i?`YlPc%ْ6%i7j9f?P9fP (u1/ MPVh+0+ Gav55f9hI|=`ivQ%(O]Tᵃy!L}Ys ?s+0G x&J`xqA&]YhJ֙jeP&ȉelpex0Yc&8c#i%Л0xvN Ii)rЉ74S\c|8)csN W6EA6i!D$B Gc"i^5cCp3]*|( :L2?'rpJh[%QHDX_345+{7sj 5Pv0Wlpep3xjwUhy]ځD)6D%5A i@+*$19H/$J[f0 QdZR@q>N[%jL|yIYC$@9l"% ke6CA gǪt(j>z'dKc;>jp۶89y,)04A uV1pb+;*#X%E)TaHapAqUf}pB6 e `i(;6$Zz|>@pѦ@O ! zju9DЖ >1-`7eO 뎭R S-SwBН%p' (dvkt,4շR@fb+co@Wf]@,(.1DqJ$ b3%PQ38Z曺FtҺv`*PDM]s{mz'my1T[o6]`&(>8W`4׹ {%-(A%NaYP5Z[Hv K-ƢcYVulDzL; @PC`u}hd`ClbZmkgUAU%(!ܝ&uayz*&w 7G' ` jßP[{{Jiry*)ɛ|+`` mGLny Lܗ㐝FqgJ6s|*b6[R ;G5 2PBP p260 z\AȃbA')RRڻ˂c0c/4@z UFW%0:]s%*A# \;ђ: H!|W] 68@_Y.MP83,]5ggï +  1 w ϡ\ o\ƚFumwMJ :)\砞c,0Κ`0+1ѝyЂJ&e@ķ, ӷ!@Ә19 ŧmQ0B&o)egYg}Luu`A&9^eĿ&A 5X<%^|W0-SeץO('Kyg*8]6F>3C0{6%e%I#p s4Cڶ4M-˰Xabfm a  +C0@,{J2=:71 4$зD7]2n)qJ2% ÈU2SaN1u(\Q䫷ϠADtc `Jc^6 \1d:fȽF0!F֩x1ƎWjνɖ/c|)=q.8RX>$\Y5Zu% 6qeeg{@1^O'Bbއ%4Xa$l2 ddd=ap?af,D c1g94Gj ZOG',@mr&J !P 6QK6"@([s""NDFqH"L&hz=c[!e;|_9V iV%Z *h`A!\!sx(^qRXX@+خ믡&-OaY~sN dpL7M&}\sBC(h>`^@=kg&Кl:!F2uvfxzS B>]iql*2~ Y- 6`o ^ 0_э8+ypy/':`BqxM fl((ն%9HA-jRfn ιؠ.lȱq%Or'ɝfv,yݝ 7x.9{A Ng r5\5ȣ3Cc0۰S 6SH!bEd%! B@Sh zJt6q6uCBhg{Ga+a%ye01'\Lqթviaڝ1%`9|pMej@k bݢN*np2t[ 9@Q<c,t(jB bc*푌Hk 1i;"[cp(7kq OИMҊ/5!$` 802΁'CFY2<9= KaMZ+P 9]1$\Ȑ.ܰ!ҡvS U P0W:%*S^jla2ӞR7 VZ[b=J`. *X9MCl6 fqǂa^,&hx.ߨG?X8B"Pqc`"ʒ1L0(,RAPFэ*N> /NiL< .͘'hC)cSTq4%(R%p!8CRB!7ACmʰ! /2 KrQNɮ6D@% 6R֛qo> 0Y%q[ C0FPEa&>qM=C?삎}*dpąAb4y9 # ,k/@ HmJWg5|$/@-*Mxnt IQmȮpKG Opô -8 fU,jB%/{ʰ>Fl=ۜ$M\r]rH" L]f4YQUa{o pi^ܚی7#c$P2ԓڙQ/yѝ+w?6:Ju ͨ-@[2Z.UA@Qy3)pYZ:95y@H VȐ׵qކ-2:hoÇX]U]זUm6{ۆsژA=Oq-:|H3x.Zl7crɢSn6>W/4yk yYib稚uhk\̙PyWq&Ppgn$.|$b(gj[Mgӡ.pP-t Qvz\h} iO4=lܳofx_/\1 o (*_p"pepUryP<@yv.A2v^ 1 @.h*h#>JfvAQr `q P tt||6Q2!@gAC>v܀9U0re_vq8(]$w\2PRu2oudPi4ovH`6q? %ue CY=%e7 o1d*cwze7 c U@CT^6?e by8x^[R e|L'EeJ*05ր$BV* ]؅e ~'d~uߴw=`U%@`^6'Lpe?+gd ,<}VH2b!^.r@QnD?")Y"d@kW-iq7He,ْ.029-_EnnqUPx3]0&G1?v*iuX8uxZ[+m`rMCo]P>55 %uV$sI(S0T4TB q~seac)S >ax@(m#Fٙ9/Ḩ(RiNٓۀ$41xrZRjXЕYɕʹ\)joSrGUv1`EvoC17ɨe}M#7W28{($+YHgx:ZzY7Qr9Zp]PsVrb`WIx9GAx@ qbL011#ˀ-y?XLuInx!$=b!@}6b.?{=,%Y_j|!!x0etZvzxz|Z( :gًqga}cHHQ[T(MuW~ZyעZ3ZypI=Q^B rFاHS:^8bhBV r J4 W' Q6ʘ#!u&:]dIVrc[3ro#ѬzPBr3MJ׭))L(0L۴M{R˴WPPW0sPZ۵MˮHtAWzNXuJ@!~kJa5/\e2kxpexzP6ȘBF-h( S5Բva&"M@4s2PGSM B 馊VP`ZX+ q:#p˫ KTtkׅrQ !!x'Zi! " {K3ztKWAB4:y2cʹv1 UJa*N%WP 50# 7HG?hIVpl&&`X&0?-0/\x`2Wľk¹ %pH3y :nދ#yyh'@ ]{wf,`iB,1Ps6Ge$*!)[n)!2%x6P b#62_ d&s|G \#QF f-4 R N`r &Q"3 y8_hH SLáF+ p$ 3zx6+NML"ll(&,p2lxlcc0c ClX`U*9d{rlOfz̛Яdd͕;>ܼOz6PH\aNH)`-i,5 +vj_c(kyu@0֪F1dFjS 9٪>L,(,ixES?H)mIϳˤw s ZX # ½#@bf(LǪ{}%t]մ@]f,KzhT;E*h2+Մ]heM 慌ukD23z",$<}\)+G^Ų :鎜,0X<J3~b~-VqLtǍ-1%)lPAm4dDǵqܺE2'&8]p MSde܈&0F6r -a ao] ˰-3q(ԬF. @fэ܄tRUSVjWPP2<vS5窤sϬ@DII?'0 T՚|`oN;Fj0q J:QEJQZ'XJW׎y}|DL2-CĤF n\L rO`CA!&h.d5,hHi^ό ?Bj|NKF=aviXwԂbvS/u6Rfe $(dGq qXIkꚻvE\ZBGP\}w] 6hj2LR4,s(5l!Ô_acX6hG^#tKxƅQ='|߉6YCڃI8hvNV6Vn&%XҫX٤tl^mb4yq1ccNt3GT0{R\+ 7O!MA FgLQZ@ñ^iyӲa16] PzԤ 06Um7 cP,u'̐0O0FG%`z@F*EVF.D\:(洵$3.m ^x-˲u d9ۍ*e#z0&/fݥrO;iKۍ/ஸ/.q;sKhusw,[~0'd/2ǀKԯok7neг{ ;2ti NJ9; <~v30!y@cWG=PGa$b]P|p|ɧ|(Hhy@"W`>"~q\b5QPY~zor:f0Xv'5} w''Qg(105WU z?Dv@ ^UFW ,=tqBpyx-@eɇwlH:6p' A\AP5\)Y5ygA`Pw0Ytyy t+/J)@pP2 4?YQ[aǓ? {ňǘWv 'ƖKɐV *|[iII8@5I.)3Ztp!ٖ$*PUP:ppAhA  )N+*p_E`LXz]%6NE Ry)pZq'Y a54`e~hC0Y3+iw):j 0:k?F@D@ꪰJ 0ZppPjpa0 @pj@Z~p*  zg*Nlꦾ :pgppЭ|ZPHJO&ٌKzaTk,6"^^@a:I$;4PYx 00y)CC8 +$n}twd0*pjA0*z7kZLzAYj0 pJ {JhRv52p %o!Ư㕏Xvp-wPdTf& 5:rs*20`Bʲ|'3GtY j`L@zJ0L۹jPںyp*^j`{˦pp)pٚ6`;o99/(L , A KHghl|Mg0pwP` wpwlxL,z\ylȂLh }D1pl7`ɑ|ɖ- plʧʩʫʭʮ/l˷˹˹˿ ,Llnjɬʼ̺%V1ό٬ ,θLIZp ,LlIyp -Mm  -MmGH7 Ҡ6Xh'MX+-)̈́V3M07ӿYq? A-CM?m/;I \HMO QJ-2MWR]Y=Gemgy$] `Wq=Vw]o-{}.tbW!-؃-jGׇ 0%X0Tе/IHwyv؛=T[ٗm٘ݫ,Mحn&J-۹2|..2ڇ8Fp$ b#ٳ *m׼-. ׾@]ܑ0ܘ'}ڎxޯ j󽄳T /)d2BW?W 83pxnPkt<wi67$?Uh-...)DNNא @q6ΗPO``bPPL onq$Fp@ M]رiN/2c`10r_.{SHr=3@foB*C}f'`bBϭnnrNvz>|w3IFZ`k萮劎b}D~1 @-;af@r16` F]5vz1PPy>10H~~ln2X`Cx(P-7nWSX., U1W0P02)BCk}PC~nl(M  d}`%@/C`뙭%D@>06`?+ `l Y3/',E- ]:/R`D6FC."}3@12&0Swx3}C0%q_`@lzb@/ 1rtoy}??X :Oœ?n`okD/fz:O$@lO_@ `_Gudfo&ߞ=/.0cI0v?}4e./bF0 $8/ $}b8/Z /x3ssX3#s#ses#(T }$/ 8b} ǣZM;S9{tu$fP%6cly?@@ %'ClPfCq3L1bQBA]H0f n (Q}t1_(a͍Et,SfMun\s)QN`"f1r01!߾N_Rc޸qW|˘KQϠ?H!6'ML^yаcw]uđ3 Nxp 03< ]´MzM*xkνLjWGmyQؿ_^I7J0Fqxo#Ěo( (x|i8Ё 91pâ6裐F*餔Vj饘f馜v ) z*ꨤjꩨv*[yRB(a)x :)}6 (0h*Fy$*Vkfv+ײ@覫{ab1X0faC@1qB k/-బ#Wlgw ,$T,0HB+]tB @Q`a|}?OGBXg\cIyWw-d'x(|j)t%pdC B !ӀH]Ld0)7G.Wngw9C⟗n騧'_*67f Fq in|lB8WogwR#laF6 U%ݳ,jW=nlgMmEyֵ-SW\&e-p Y ?CA6?K.6M/T^N Xd0C 38 ~7Mib6Ig/0=/}Ƃ x0Nϭ:$46@{ b`Z_6` V3>tqd{cu!du( % 0<0d%Dv0nB]nVqD1Օ!2FFCf)ptc}?Ɔ&t3ZDk8_kGMRk! -K;h`4@[ָε4DU`1K+lzvD)RP#E)rvMm=+Sqtb< u@1)@n ]nS8 taW  8'mwu@۽x`Զ 0B4b`dfǜhd Iq$laQ8 {"X7D,V"C Q %P6 #14 @jL5鐳$Ojv,& Woʋv2j\5v|Ҹ#VtC:(@pCd>o@@02{,ǯ]4o)륿`߅@!}?@otQlǕN|li{KμG=}ڑSơnL/ՂIAoDgtWy\V_POk6Gt] P' O#^mv0cԁg~8$R&^((M*v쇀:O3&OY.{,؃ ,PR8TXVxXHUJHT>`A4Dvcu1dh=yt8ђ6`wu']6p'-@(671-1PdH4]0t(-=#-zhoH-]`d(.ie!3qW'aXFcp`6XhL@ĸhXLpd p)Θ̸ViflpBp;xhqGFHyfۘQ2}ǃ苦x ًocps(;Xyy@0H(()Y7;(DÏy[U} ِx(JӨɑud8I*9-/)h4yA68)u"to# ip(-xx,ɕ|ih1L31nGɈ}ٕH)dAg)/ٙ]})?(ؐDixˆ6)yX蔲u(WtF9Yyie\lY0hDs9zYɔYwLv쩔ɇr9X_  ڠZ<\>Y乚^`K(w@w "0+F 0*K`:=:j`r"K0&*rФr`h1w(zh:?dZfZQ՝E=y]աJ4 # z -ʧrprxK2xyK( ڤ' Dpzrmyå襖Y6k'AjHYٜ(D;wD`J*"pwwȚ*p9تF3% `êƊJm0DzD"ЗȥyL(PTK p sp  ۰PDXHcY4z)=H^yP'ɟ.-xyG|Im*Ut(C)+ (``TpO;àXp0U; JJ;ɦ 𣱽8'h$l)@٘ǛxɎ{I"zLG:\Gۯ{?Hy&0x0#`Ok& &({ +Ek:X/!K3Ki-lO[ĘǸ=[ɒdi;e H+OPk-x&-O?`l``KjyHxc*{ӂK{-`_`U;¼Ɍ&l$l8k˸)ٽ k;l(0P`,eU+ W|:^nz{ c軣yڗc`ʫOHlmˆ[\9\Jۏ{qQ6 dc?cOGlH-L OLak9DŽ dlg+r1nlL~\2>4ġ sW A|x00 ͯS|~Dÿ-' {I\q@|ƌyxֻja) {\[;<>a[?L=> $-P M.rx^^(-~˛DZ|^?5^rnc[ > ұ|M.>n*b(?PP鰎γp~y b@3ԇ~3zuNNl\=S~`.0 r >ZɞV$XE^% `z퐙Ր ^ݵbPZ8U{ F۾ݮ[ncPpg)M7wžkۿGks@ I_6gygSy`;A(LF`CP``>?FjDF7B#m(,P0oZϥ,^3OF l`f,Pr`d֋X1{#7-.}eC0Rp^$siك m.-tgᒞ(O]_=3x]f0$V%VT"`fX)66F6fc3f"`TD0a{!v'c@\l| M]m}l@%! sbCfn^jޞW~2/'2?τOo'`*ܬ[ 2cÉ1?s'xLB9=J=93̙2hic 9@S.R `Qj0%pcT0cب+t!Pt5Ę" @#ttJ |udɸL4m'P&S ׉\LS^9t[ĜP0L8] b۵0pW&ѯ?[ed>'@Eтiƚ%&[o6fH&'fR}WM9OAB ZdB C9$ /#ANY.ȷ~Z7[~YXAD&e"ltRIFlN$ц@f -XQ:(HǙC)cp3h܍Ȝ-/ !@Jw0Qjjx -ފ+bʘcvA`8وGptREu1gk?=ϡ*a (;vap2Dc"rA{bY/ם(o J($ qZ,땴.kHq/qƌ΁oˢ4tFZ -HԐBVn9]p"Hr!GO]Jܼ8*-b@8Rchvnt8v]'~ 2]L8zHD"l- ?A~GLtPtH('ԆQ]bi=?L`%@`w/7t/}wwi}yx:1 {/>Bbd eC]LQt:g4{$dB u])]SZۆ kXGBm{ &=e){ = |h @P. "N @P e>81 C E(f1Z*Ň`dl(gu4,,$@0a0X5V(貗 0yd.͌a Nb6&̉/`yLq㣬d$ϣ69PAҒXJ$,@+˵sόTzcNt,gj^s:/-`BЂDԴW2q lZuNRK @!P FǙ .4m[Օ&[Ԝ2|{ \u?lZ<d\H0^{Ks H,gXg]lbs0:ҙCInFoj*Gh1+}LoӟC=zu]"Lb4 HDu} s /d{tHOGʖ:&ymjC b.>{zό P(3iDk?ЋTŮޗhAz evhNxs٭O2H Y,1o.Pm`ʺ z9woٟ6>.Fs@cmo{%GO` p-"2 A#4CxFmfb&f-hW}Bo6F@ MmcsfgjW5h767g]'cq7O.,&PS">9v|\Xi4WhW:얅%`g~E8hwB:@kЇ1-,SH!ZϢ!cCZMx OÅ@#Vnxg1Pc,cs3gWx{ȇ ׃W{' H]zqh ߃l爠T-%a=An}f8tH| H 2~Hp1@DG.@ANl8V|͸79Xw;f_ww6)7Ȳ d0 ׈8-i80k_7k'@g6 ɈO4Hy6pY@ڸw@HZ1 Qn8mCW.#x;,iW00 pYWvm 9YHiT4YX `}Tivw߷H9\(A)`t575̥6XvW-ɖ{x9 _' GvWvhπAɗ%ÁHf16?ySq()/C44]|ɗA=@_jp*ppgP\NpZ W$W\* NtW z9F( i ǂ☥` Im" FxhYF5ѝbU`I@7\ pP`\#gZjPgNApjjTUP0%b]Yt. ,4+҉Сa!"brf2f(-"Z9Q6#Ʌ]P`t:)p 6~ GW V+py0uCuiuBxQ צnz p ;i.R{ʧ"F~'%(,^,Pr.~t )B $Jjp5pg1ЯNtЩjp7G@8vsZWjr#{ ҭWrZ~'SY}0!w''>3+g/˻˽j P̐[ `Esw>g9 Qᖍp#DlA Pü @ ;\ -ZJ s+hZHǐj& ʑsj:q UO:|{ε܎ˁ05/%m'}0'&}0l ! 0{]X]` `<(8el&ňjqqks"B@l+ˎ4Yξe )pcMemc moRͱpml-׹00i7 O`;\yCmh`n=N NdXJYV&U`6(`̠[ )گ ۮMx0}۱-۴mۺ(0Vf| 2 bۖ͛9\`5%yP PR@\HR3``BL\ꭢ>|c#(-8ڸW0N>W@Ex.se ^1MW&ح'Nmxזr}vP|-v:&kbL )4B+)4Yp4&ml;#Xl`> 'shc0ihe~oN3 X_k =' ^':qx^`Ga*6Z1G"-ߜys(rd bCļgyQ^>XcmNpNL͎%x>=4<#X~mU\.7^`>K`KRfroKQ v ?KmK-S PpNP TɊnQ`N( >(`W-4/Wls z%߾ O%0@Wf1-3+S7^cOeoeD 2Dq/00wc 0q2s/[k?,ե(e`7nώs`sW~;g=#`&xPsL~>L Hâ]S؝P^^`DK@2`!DPvn /rr ֟ ap?M@D/D_cru ZM;S9{tu(XXe?O%%(e3T//L(33x(1#W#W((sWX=ll(/b o7yB8FtZO@`]1y@``ff@b7GtP X BQAG%],œO~,B6c 0g$ÄILxѥ~Hx̀AP2H#G$ud ,0`:*DܷxQ$0-L kꖩe͞EsǴ$LtQ\-^?Pvٗ^.`4|QeJfL 4T|'xD/= `'K#48qS` Ao>c1څM5pF4B-jHJe(`3@Q K*&@\`\ׂuE0?Q[$3H|(x K||ODz嘣K%qJ@dc!uQ(`%,?%X`H@J&=Dcmq'L@L4Ʒ_,;yq(qC kQ xg`ؐ½B]tA uzLhU퇕\ H 8 h(k50ρ`=A{ %P W=,k6lkTȚYI&ms3qw5D qmqQd@'c ݑ}; ו xZΩnu,P (d]Ę&3v@ 5w) B,{аѯ@A1X B ns.dYc&$&鑲N2`| H!eTSQ@(%$iρOCc/! 2J8Eue>t8ЃxXD̠Z&hE?8*j 18fw w. (_2-Hu&VPSJp!>ڶ4 N!QA=@1 7:`rWۈ1@C=&%A"8\8D5l(0Z SX/,xt`C+B[pIvn4씣QEcNx^R p\Lr'A !$YǼ)Z^dUi58JHPwŠ;][Hh XZgjii$H8rѦu3\ RS^>)[;Gx) JKucE DJ̺zKWf2&ޜ!3? PX':š#t 'R/13UX`oiNJK_ g!aIF /QgT!'8F-hVUYRKGЕJȴ8'S^Z%8{e~ѷol:=b"`MԜK ,|eڴ0zp&72āJE'OyG3t6XOz˸2c8yt[OCCf^y@T2 ߃5ff{6`}~#)cxrOBO~ꥷ >Ai\5gpM8n1F^1`aA׀&gdKv)0%|1\gjB<0,mBV[3\b}0(1RuW6~Sutzg_&kס57C1seHKf H|V|Sb86*.,mfh }'?Q})nZ^ppI7XvI(<Os)X%XVq+|FBEE6X|CL9=,DSheh'᠆]t3i EF;w~:臾;>XHWg#`477)# f|8|rTKQ#`x96Ai10놋xz0\pXV6C45ag=(neF|3+0AըP#74l(ց' \0 n4"t6pe=n/Ru1%=%3E -w97!I4$qƈ=Cް*n3Iy&p!4xȋzuL!cX7!YH7NGc}amudKIWFCH јsNpf'bk:@P(` ui{i}؛pX#4!ɍ%5t$1iMYaIu=1ns禚C7*v2Q2&O5w*Ař#X֐A%`*ISst/𒙽;XGA3$% NQ&z~105 )eWYPp<4DER6>cY-O|J,`64wRb9&CZlFȚI Pqw x19j;0zAZrTq:5ȔEr}Ap 6r%#1 $# IO1C?c- 4j2spsPA٧vQq|"<š#6@kyr}S*|TVX`ӳ>JqQhN^Weex Jڮ:ve0)WcI4ɧ } p5\eO4"aksϤlVWaZ!qHXf ( ~q@PIگ8dyI, vɢGt:_} ʺu cp~\~0ʽ 0<\ǜ<˼ͪh<\|h^Ϭ|qp/ςE8@n w$ݛ}=ǯ8@P7$]&}Ҙj=ʡя 6}8:<>0-/=,DLNPR=M-F=E} T`bV;f \^=np]с{t^km|P]wYآD~}؈MsMإg׌=+ؒ׀ثؖyѓq]ٙd0{٨=ՠMچ5ڬ]0ڲիڶN}ڳAع-σ=ܹK]ƽB=r z]Q}'ԝ̭Xƻݞ^ނ=޸m ~ߒ=p}mzZ'߿b` . 澘.]>p]YBtP.ꇍB}P*ZW_pB.^" ^~Ȟʾ>Ԏ젞^ھ~տ^" >^~ꙑ?_>Ѱ|M_^ַ3c0 *,.0L1_6371`@:D/!AO%S PRTЮN?XqXzZPL^`/-eKO=xiXvOs@(+t~ysz?t_'S 3h (_n?4v/^/3VO@t 0pM@0 Qs./ ?#nc`P T?ConICivAnԀ`@64C`0bdd@Iմ35  +Kv'4˫we"`b2b`c†r5R2la0wB;wb3"2?^>&(1(L8010E^;zssF8B^6 8&1b#đB̛!6`(Ag7F(? aXF:*رj"H<&JcfR0fl^>pֆ8qdPh" ra Xqasl1)G3-JSƍik۾,`3fJ`mo6%&s'tZkF$ }U)ST~,.Y%J ?D 0^x5Gkr3CVE O<@4HdB P'aqa&CM!b `6#XxlGdX#JxC! `()hL` `C `L ' DEaF {ai|x1`!bspN 3CA0(HXPA3C9x1 fHNQBΡ1P2䞾ziR*#0 e0Z!@- 6@a&d /{tX@x`x(/ kZ;iv c&ҩ붂Wܮ-!]lqROM4ZǼuWgu`5F26Z`6?YC t(w~ x]"zxފ?>xBvw5y}KgZ.ݠ{zꪯκMz첳Ǿɣ>Kq9|/|?@ _}̷}~'/~Oꗟ< CT}OίOzF{qp,*p ,;p`/ jp?p$,!p,l _C6ACp< $*qLl(JqnT4dS 0qd,ψ4 jl8qtuŦї_|ꁔvI7ނ lw~qᄸ9XXFUyE|V'n!Hah}XߋQA'uqhU>H| z {yFWxQeleRjɥ[v9_R^2!0嗥TtD#Jk֩'Ma矀*蠄j衈&袅ZTD*餔Vj饘f馜vi# jꩨꪬƺP4St@®+kKDf0F+Vkfv"4k覫.b %p l' 7 K@lg1O,$l(,2t,/l8ls̵o ,D}2L74H<1OWmQ7u_/-%4ܵ!MM7Cg4o<x߄wg ^838ǭx9=ֆG> S8{ءyM5פwꏫz;Ag.;g_/?z󪋎yn=묹dz_|+^=oy޳ִm9Ovɟ9⃻^uj_UV-0{`x@ Z$ :z GH(L! O`2 L gHÈ%sa`h@ H"H Zܰ{!H*ZVqys<1/o^.3WֻgO,0vOf};ЏOS Yv<OOՏYOϿ'2 Px ؀.~ x2"8$X&x(*+؁P!} "X$88;x*؃4} JL؄NPS(QXQY\؄ @ dH؅MTh8L8j!oOdX_}z*3uxjȆh(VXiH؇dx' hk臔!t\Ȉy}+Ç}((8XVȉ᥇)#uHsehx8XHmXx8x2؈x2ַ؎u}xh20Yy ِ x4Pyّ)sk29(x }?W3w294Y6y8:<-[2,DYFyHJLٔNg47ESV\/\4XQb4aYhY5Ǖ;9:t7-67#F4U?8v2sY6XaCa _zIc?;ib#<\jia=,=:I6i?`~*&c<#?sY;u_`Sɚ)4w^!&I?Yi=_ƛ/ =I`9@S=R]}7f՝dZ!c(SٞcDd7xqf7'j'pE0D'ja+3MuG* z,%Ρ*R!Ym!ҡzL ! !JJ' ڢ$)w%),=z5$" >3*A7JFjK OMzO#*9)6.ѥIg>a8&T'0vzIj s|ڧ~r9wrc:j- ] +ک ;Zz Ъ:ZzJ ;0:ZzȚʺڬ2c@:ZR+Э:Zz蚮:٪z:Zzگ(f';3{KS {';5a{3;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionToolbar.gif0000644000076500000240000001031610534177572022574 0ustar paulystaffGIF89avz|RZc޴ih!!)Ż۾@;9BBk担! ǾóмJJZss)11❖!!BRRvx[W{̻,(į1BB96^V}rmJRZZkkȵOI߬cs{*(s̋Ɣ=?厈))1)19τ&$9JJGFA@b^KHheݻ1/腁̙ᣜQN䔎!!!c{{wrǥRcc!))~yri333sȩJZZKE!,vŽɻ""ҽҔ؝Р""̘Jwkl5-ܺ2o[Bm14n^xS^3 kÑrr'ǃAEy@.i=x +5ԉ#~x|'w)I}b$\:yF*餔V* 8ʀv )z**: '};`$@ëJ jkJꪭ*+I밽rnIz³F˂ VkZb ܲJklz*`kګ/~i֧꒬2P {p`8zkvG`-$n ,aC0Ǭq4Lw䠆[qg<1.@+l S./# OK}^WE,L5@ [ Or,= F \7|>&X)da&z'x&* AذFcy3î:ŚîYqG Ov\m`Ԁ !|貾,ʯn=LYpv9w} 67}_|ۂP8 ,*p v@@ r[]4vmp_׾إYH#Xr$‰9l.[60@_ط!15,Ax8v Z<`ʟcuI[8!#|j5p;@A2Ȥ)K"8/vH5( u@]FA(8 Adb^ط%a 7G!/%IJpi.X@ 6qI~dlu.Hũ_Ѓ:.cRsjp찀- _"j0d\ɇ%d=Ѩ;l&d;pR`W@*nn{vP&;t߮i.xBDgT`L( tzC Kd2ֳ=Pl9J<@U' F>u $?A5p x+̣\E8J~X,Eg{첳Lf5H 5&g09΍E84 |1۫7ӠGMj[.WmjTz\#eIεw^{RUM_{KN#/PvI[Զ˕foObMrvvf׻nE.~RN}(EA o@'N AWʸ7q*I8LR|-(ׂ6|aD ^wÛ$ e@S1H:|І1 8=^\ @B{}_hC@r-@°Lpx01 _P 2ay(B1hB\Lʃ}P`;ЄZ OzC^IdB,g/{&`8(CJO>`:ozj@a~考8ha{!>p?/z<h.yEvFp5PlkD|_ f< k@GpW-G|mP]pHl59PLK`Fwց&8BzZ,}7eB0k)[Ai6wkAs0G؂MP33k@1p[R(f(_0wzP-E^.`dօJ{&8kx; g`yk]aPap؉CKr[`8xfWp@6GXuP#$J(ȁf< 9KT>dȌ艻pntfG戎IogO= 0n2˸dsΨRǑԈkw(YpnvSvohS'*u)pup Kp(9k]HIgFuHKea~EvhЕ^Ɏ(Re AO$kA(/JU0jöDD{PR0]fl`by:(W6T;CpgpKis/hoz`k5 %e@+0TZŗ4)|;dgu(2qYs?g}ef piOpL-rP&Evqy@S ց@Mv5;p@P@rPf*_Fw@'kTr/x[Mrj'+|I]& p; _b 8q:$o?0} [\@f>wQjzU frwIl 1V2IIyXfqki`p~ rJ"vG}27)$8Zuv0}z|80#p`]z mWYtU|XjZl0rEtZ0Jqc7d` ڮ =: cgȡ_@Я:m`@EqzA! oFMpq ]Q "- = !psf.krZ!04[6%;!<۳>@B@qB@AL۴NPMңR{XZ\˳p7ȥb;dKadl۶npr;t[v{xri g;[{+۸[ 1{;۹ ;[;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionVentricleResult.gif0000644000076500000240000001650310534177572024330 0ustar paulystaffGIF89a '7#$)$9*"- 21(= 1/.+(0/+33651GWg wwlt~$ Y8 V(}(y=j5d6|;GFGUG 8fxc"j4TD\\[JPVGgEx\xaNhTs\ehiv{cwy@MMHWW^]VQa_Xii_rsfzy{zq  %-!4;.(9"<  """'6#&+-06,%.$2 9&=3"=@%G-P2M5X=aIDX\B"B#F,idi(I!Dhk}ktyz ?'4  / N Sjqgyo*V%|1h  ;9?  %& ; .)70>D!I,U!A2d(C1D7F@O@P )"#!(6!& "(-+5#&.3')5+17.3;.28>AHR@VDKVWet¶ƽĶǸʾŮ̳׹Ⱥ,H*\ȰÇ#JH1Ή3jȱǏ CIɓ(Sn该˗0cʜI͛8sɳϟ1YAѣH*]ʴӧPJJUAlWׯ`Ê 1V]˶۷]nKݻbU߿Lᵂ^̸Ď#K y+c٭ΠC{,醤O^=05ץ]ÞY6ۓmX7߄}W8p#_`*N:W+ͣO/~={g/:g?` H o[ `=!kNZam!h~Xx"vbbn$H.:cHr3hX:g#r"]pŐ?$Aw+YBpIYlEY&WYM(Uvea$b"w!%mFGzڜ[y'suAN\eA1j}qAꢚ)ĞwkAqyA@9[BpxrI(E\p@ d ip@EDDKYhnK(AV!B *x/ .o2pa̦kN  +%AΛp G. kEYxQ*,BdpAB\LDVcYP&/-{̰/ZQ;kQ_/{КIwV0;xǼb{3_n_]wzyV`4Smy-?ڌX~>hNadAu9ns_n Oݮcx̫fEr`Sqrk»L曑ͨ?ϼǏFadpg#G{ńpbyS@9ЁsٖȮ00簄\KзfKPǼouH9 KN  < url6x WwD! ~d_ޖ *D& fxkv湹 nt\b`0С05%^vN!6>u 9A `D$"@5d.K| 8<0nnB2Qpy{_ YHU"sPCX?nQjBu,@^B 5- X[2xk7BCƼ 6ЈP"2^ʀs,(0PW6򟴑ܺ3eS CĘ6X3m+^P7538z mxʸЉs iE6"Zu[`*>a? ZvѪT& `GB§T'_SX+"S؞.m؅:[B0 b,c^F1r bԠ@5h]; Ew][ES8`  {{q {P vwsMvC0lg,xAD0SMf @~` P X 8P` @ vn6s(nZ0 ڗ>jTДe3FPa Zu\ v P @lhlS<38 6D.034@5@)v Wy e0p 7xsUHtT-p7 Xu` hqAKP>5B61o#bc"3@ @.J i e@ݠ Ըh0tMcp8討, wR2oNu>SIR)B(F@4=s"648 Ű Ā5i9nP ͐DQc3+^l^woɒ |&DuoS^Qs<4_!9uNCQ@ 5J Y6X[9 9p5> ] l@ .%p-2d8FIEcI(;YR,8 wnްY)nPݰ fh@Ai Q_* , 0dv+oń:*/@*gbDqc7Ãc7ȔPY?Xe U7xО﹒ 09 %]&fG`Y&bD@Sø )y>n)7ngs Є7G  ` ح~HC6sQ63/J=+ .f:ӫݧgpHY `ys 0\~q @ 0;l:r`WUZI8='P?JuQD`JpL40P y@ Љ%{ʎ)@Uu1. m8ZsjxX`|AD ZUC]PJ4gS& @` 9 >Ǭy0 QpG V  -c^W'JpY4SaPu6TKa8 4 yn`) @ Q-#K4ay8$x}x.:aBFaQo7)i`RpZ ,0` 7ਞ6 ` 9  )l8\8]QͲ;Y@Rhp?D?fPn800⠷ \)  V;fQFH#+A][}\rSi8S2 V~հ:n+p  %PÍ}?eJplsfe:ل@a@a[ =W PBk,P4;Wr\`E (xq}1 ͪZț 0,odz<TiEW{AD  ^̙吕̙sΐ 3@ΜLLOtAl 0i̝e ˠ 0 +o2TdZh3WtQ&QVl Xϔy"] ǐ pҿ.1LHdds^玡'.P9:~1TzbtRCp1` `Poq5Cx8buWKA-СX !XE4gZCr4c*8Bo.p% a5s:?T[AaP4QD=4>L4gfn4zQd.h`. jr+(TEOA[~ E E|(׫^DYCi`g2pbbPw,3`?ACi@ǡEd00h=SDxR'cbw`q)1EdNY@d;mznN,? +V_.,]SE3uAu̖jRQwbPP."NJ>CU4A{A!I.Lwb0id Vtv`?N4 &X0Q7JOn{A%b'?Ȣ,'`13Na1#u9U@|!jBN9Cxo5CgoUżS0>hZuR YoU $XA < B%N@(nX0%ZD!i9rTز%!.eΌhF9@H/_HA9G I HԩL2>}zBH/aʜC էyQ*N:Xp3d2]N͛g uI`0{"fܘ`!;U{ن_6yMW2{×_/}?)*4*Tt?'/0C C?@G DC1cqFkFsqG{G FTrI&tI(rJ*J,r%1K0sL2K3s}tM8s6\6S7t<|;S@Eԧ$Ts,QRL3tSN;4%SRKQ%TV;ՇII)UJUV\suW^{u| e_5y@gaud uVjǝzv[n[mPl5o%]vUWiwYw晇{wuٗBX_M_|wu$Ɨa{;X_B RySNRzI'uz!dCHG ghsdig6ujg~ZjYu6$zrN.eNǝZ\iQVykABfzzP !Aq!!١D~0~Pz~.qz! ١ 9qsGQt8sAu)s1BaܙTォBI#' &Gz&(!gp ygCOty4Wdz}|} .p6:>(D=nՌܛ` Aٕ'޼X-C`;Pa=0\QCX!#+{G$o\zz$"a=~CtxQaaCt$y++q$V;QvG=ы"@&㻮FY=QRIJvN񂤙cS*YVc?QDXR,QK|Bܥ,$RLG3LhF3f5$fVS5xT$g< /)S"x"$ ;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionWizard02.gif0000644000076500000240000001504710534177572022602 0ustar paulystaffGIF89a~ޫъatsġҲ̶5@?ç̻޶}pgTLȵʞƿϺۨ|HVUɱνzdZ̺ïį|L>8Vfe潪rfְztͩƬWhhŻ6AA!,~¾ĘǡÓʝ ڞg,ۻo;.!A0.8r}Fb?l8n"G0XF"?|2s&Z`Ox#ݑGH &Z4(і(9}4Ԧ8Gtfԗ4"Ugq Dۖ[:ߐ0᪳+x-( VIeIˢqEyaKy*gFosuz&G¹WmibXiA&?Y񡧇jݭkf킽|aֳϛi1}=׺IקEo+֏z'x~R/ƁR_ 5&Thfv ("7 ,0(4hcZU"%"*)DiH&L6d`#xP$Pp@ Q)dihlPUVBK[v—m矀&ri%%wjɥ` 裐FqY'融F駠9iڙzĪ꫰*무j뭷j顬gH k&6쳔zihedv+[:/rlC+k/nD ' 7G,W ' t ,$l(ťb@4l8BT4 @ 5qpr;✓9w \\0!ӌy<7!ώs >3-S 8̮ nN N\9mk-p̣OCՂ͹! 0?3ۚ*pp2̖C BoB|#DG.mH\hƃ ^ V1\XX3 !R<n{ ɳ.&$F̐}D#&I:[^hNz2h"2)WiJLod,J#rr$d-_\R̤ۧ38R92y-S~KY3F̼̚fϰ3nmkdENr<.[qK/khe%P1b9ݓlg&Wfr g!wp/:G Йow7=hѶǝ⴨if:ԣNu0=]Mcb}] ZZͭ5o],i:~OL3y؝7R~v||58U6lc 3?61=0s~w=blx7lgt9b.'^ލ3f?68&3; lx(w8q>΅8q/e{=atյ tZ -:.j3}_f%@urϹaL鋧9.rq-6>q\zn?|kƫ؈og?.3/뜏C/vO-Yֻg4},~Q,8OO~|;M(Ͼ{t}Ǿ/M}cEOϿ~1_8~N. ؀ ax h*P-`"8$X&x(%HQ*0$ȁl~Ep2<9؃B4llvw-L؄NPQ(V,J؄p^^XuXE\UHlLwiȄkv(bid ~PH(EЇ888~؈~x艐(HhXhhZ犫X舭(xRՋŨ8x(ȉh(85xx؍cbȌ(θhܘ@ĘؐxX)yɈH|b9x$yje(ɑق#xm4Y1ɇ;I6i8Y3ٓ"iGswz>E cxjO)@(:8^VX^)ؖlو`ɕؕs _ɔjYYxiZ9o9 m q9))Xx햓ryI~2)yb(I- 蛽yX8xțٜXy,y)ɛɝɜ(@ٛݹ((W'(Yyy''GyZxi(7}jW~:hY0$Z&z(*Z"02:-)u.<ڣ>@Bڣ(::HJEjIy2R: h/P&!JZV~)u2ib:dZfzhczfjpbʦPZ/z|ڧ~ibZztZMY}wy٨{'XI\שzZfՉ&%JwaBhb:}zJZZ꫙WuĊ:̪ǪcϊؚzȺŪj͊׭* ҪӺǮ ٺyzZꭈ7(NI*X ۰+[{˰yY}   &{(K ;02;4 $[2[8۳>@*t.F+L۴E{P6۱D˳R7K\˵O{`C_2>]eknck k@+A=]۶oqɷ4˳tIl[}۷*2kxz뵕۸f t4{5˹vK g{{ز;KF{;[+TKכͫ۹+K+,' [ Gk N <\| \: "<$\ lC zl̨+w˿*6x{9<-~=v8ky*&ۺ&GL&ģuj&K&SL|MשQܬʬĩzc\֊djnzǹuIl*mj\4|պᚭjʼns<LJܯZȌ:zĚǏv,Ȥ,ǝ\ǍlU *Nh |eĿpliqZKg˾jC̿L{Ȝʼ{(< +;L(\ؼ ֜P2|[n;\<,u<=\ {  +m -=!#=%}|Ҧ,-ά*1ݸ.79;3=5@ MLAK =\VXMOm[]_\mmPb`-׆ mKzc}-xс}|]ت{؈mЋ}ԏ֓חm-қy-ڦ-oڬMد ۝mҳٷ޻۸]C۲۶-=]ȝ3 ϭu}=M խ}֭=lZ&<^~P*LjA J@Lh ,LlDA=96 2,/L+l㹺vfw>w^6mE^^~u qCjrGmL_*SwnZb6nZp3[s6l@چvyfnL&ngp&mBv fo@&pFo^g`d4r^jwnx>g)7qvx'm`|Mq+no6or^xNk eGc>nCiNsasoਇNN9snG&ihiv;g^o0>v(N*mwmoVuz{qr葉Vmf>^v]Ndɾn%Nn!#.숞k!{*&,N]}6g&5<|&OӿU~:ޗwDINT8[Z`?NCx*!loWIr?s/oOIux~uOW8jW(O؅vOrȅPWHYu'@(Z?'~|y?3 3 88gH8oؐ7p/D_xp`y1@XAqPaSd!`yiy )iir:YYJ@ :`8cIZ: jyppPR0"Hh)Ii-*ꩤ)p[D={<\|<<^7Q?qN`@@Tbv԰CA1c:?hZlx /i9fMx".i$7L4IҔ(HJ*CA~ъ^y6ڵ1jܹ=Ӻ )ld=ª|o >8$`ɔ 7і M:xW&ؾ;ݼ{`:Zmċ _;eYԫ[=(O=ӻ+Xٟ}|U\_]Y@`` .X }`NH ~b"Hb>X|&b^͆b6ވc:ce_s)mFdIǜz3dRN$Mr6sPe^>dzXE_af$i gkʘl܉gzg~ h 0!ٗ_&-h> iNJiVâUA~ jJjj`!CJkފkk視Kl,.lV*NK-Vmڎm>kSKn枋nˮ!;itksnap/ProgramData/HTMLHelp/Artwork/ttRegionWizard03.gif0000644000076500000240000001625410534177572022604 0ustar paulystaffGIF89a~њܯȵ5@?ΤءzrZzlƫHVUǞ̻ߧҲʸg_LpįܢLG8ͿWhhfٜĶVfe֪ŻatstŖ!,~¾ĘǡÓʝڞg,ۻo;.!A0.8r}Fb?l8n" 0XF"?|2s&Z`Ox#ݑGH &Z4(і(9}4Ԧ8Gtfԗ4"Ugq Dۖ[:ߐ0᪳+x-( VIeIˢqEyaKy*gFosuz&G¹WmibXiA&?Y񡧇jݭkf킽|aֳϛi1}=׺IקEo+֏z'x~R/ƁR_ 5Thfv ("7 ,0(4hcZUb | )DiH&L6@4P$AQ)dihlI0UVBK[vm矀&ri%%wjɥ` 裐FqY'融F駠9iڙz~ ê꫰*무j뭷j顬g2 k&6쳔zihe'dv+[:/r ?+k/n+' 7G,W 'w lgeP,0,4l7pqjn8˼ :PA@.71L2=1XA!GW HUgMhu]G`m*m6z݃"ihAzOA|%7Mwma>[怢TpyyNuyqVS<=On~7}>W=z]t =Lu/1h6첼%u~uI-w3m};O+,/Y/񐏼'O[ϼ7y]ڦ]FOқOWֻ; &SϽw{Sa ЏO[Ͼ/}Ki3@O?w'Ͽ8~&~B؀x| p؁ "8xgo|B@2؁/86h' p>>@(@8EHAXJL؄?NQ8kA=P`O_D؅dX[8fh hRTHjXȄpz[m~Hemx~0wW̗ZȆ\Ȇ`XoGk8(ȈX 7xJȇZ؋X`8,'<Ǹh8UvWȌ ~p~`~ WP~H鸎H娎8(~ E{(9Yy~H؍w9wgvȌIّ9G,p(*,ْ.029)IYn(<ٓ>@B9DYFyHpY%yPRI7%̇\ٕ^`b9dYfyhٕVyVaR*r9tYvyxz|ٗqym~Y7U)X$ǘ(nI)15Ǚ(:+y+ZtW9Қ-撛ٛ.XQVYƙ/XEu)T̹4GSTTUuؙsUyiumEVxY\%虞V݉jIY,swi֟)~2sJU.Fdz[P'~W ws/\x:'Fq Gff aptpkբu`kWfOna\#:o%m k6ov|So&u;z72*dZ=J][mr7kZoyUw jh ` wm)ekw9נvXx}b|v(Z{:G6jr:&ku (:JzJf'kjp. _b֩*kť*J&KveަgItGo'Ejtꤟʩjfikm2J_O!ʖ器Aj6W]\mzgzQfY֮]:&~j sڞ ǯZb{&֤y wzkftfvOa`תţht:?*efڭhvb$>=fb,,! ')1 :3 58F~U078^~Y(Ga>Ӕ@ebghLnzQ.u.6~op 硽h?@Ӗ`d^闀cMj^/z>`b⃠>>ޒ^N䶾~N>P~ aî~0^~~퐀+~Z%h2럾K )Ii [9$_&*,2?4-ٽ:ȉ%@5ԙK?F23֩LɝlVgGnt:jp{?/YJcag߲"o~vXfn:L^j/ {'@BefDmʴfj]4 X@8Pp(8XhhxHxxə Z@@@`!zaBAYI鹙Kl< Ȭ :Zz;[{lk Y?fZgVBB .`>!ub ^a GbIe`$Hb&b*8%!c2xUz&B>dBI$-cJ.5\yfM^Q(Y~|!U&fjfn gGevމ'b&vZst(Dyi[(Do&ڦx sf | ꧊*j œ*Ȫ묟j_8~H:򫪢:,J*J "p@ T @L+F ,V, ory*Hp;pľ#ierj햺n2Z/Z860m*˿19 Rq'Lfڬq/;Bp1HKu%0DrDHS۵|vڨF+8s3VӜ+N)v* rb%X8'&P(x^8R$L8 ea芝ψ⍧zꟋ|~5׎{`Y.|)?.bB_}o$L}O~9NAOSPs㏾LIi}Rkc@l`lvB/ jp8yp$,A Oeq ^p4 ap0_ʌIs͚8osϞ@ JtѢHgשӧPJJիXjʵׯ`[ZH`hUv۶p};7.ݻv݋w z Lo6ȸLAZ̹4E [ 8Q &k >`i.amඡB~&~)b% .Hcg $xcA/#e=QP9PF:$F"$CO*SX%E($[}MjfDfogF4A U٦@ \s2TAx飜mZ .A?FX? D`)Zl: .Pni|poFMa7 Xi6jpqab<: ) ,P g q ?sؚFW b FNj І*r즑1[ҌUJ" RH#`SY 7q2L[ۥFv(B! Z@@ed8'\m^P_"Ԩ: ]Ra]VڻtpRJWjk]? 6&*"ϘD`k9BH턞/yZ\@t%<Ч/mY# P,,pp@?c?wtEzc3>g9654"Wf(\qx0*hj >vG5^cA7t?Ńj6s2dfb6bg}d"pG !CnHeNj|F { P `K~xw~Әoȑ<ב؍36YSK^?jy݈?nW(0Cq!`?/v|rT8k爌h<ƃ?IwOCT1:)">dT c0P_2Heey/ 5P'm4@g>_`k$esI-, 1i.-t3CSt0)y[n|IyITLZOjS !]Q&r/gC0 bs( nDK<==Wj4LŹ,2%&(=Ȋʓv  >sU?H{~˼Ck:l`Q ,dC%NXbM +T,\̰CǏ#SL% /mdiM4mtE5:te8qlL&M\V6T,Lz2E+.L(c .eۆl(*d#VlJa])ױdsM ﯭC" &`򉋗"͖ɲ* PjɺB ƌ=谍Tb +L4E SqE&,P`LTKC1ZGu\XᾳO~Fһ ت L+ ( `((A XP(SHqJp-a t'0 KCΰ7t! 02aC'wg\aѱOAT F>xDR< ?nY.H3[D$ # WBҰ$IX F D 0p v ̘AlV+lO`p+?G8q%&6A r ]Vgu"_dIb $E6P@U%h@ 4'la j6a j3#b 'GJJ7qe>" zY.p,J%D1A)s2\#= @>O% &%$( a`m W Vq9e8l.R* -iX<4@DR #8Ak6ɦl|kl?Q D`)[ZՊD` efU$+dIHH#] JjKfP hj:%w,_kIr1Q*˰ @d@0P!{dEP#aL@ n6%^6`.%~$qzu/5\1؍$\] B36 @4@o @x1rYQA G'0Q) &R1~$"!*5 ^K邂NAfHc RDgX@?h8 W8ʩ4E `@G6PHݴƙ.Y  l&ڕ(g+[7)m0,'SGkZ P?h ‚&bk M~"( LS i,f!o ͩg@)Q4$xi~*nIMv",,f lV{?)RWy&Hph"a(Yc肔!Z.d؀u`ƀg}[U$/JbVyD Pyk?(?Y)=1n@> %'Ak, -o۾z+y>*x /sӾ0)a\.I 36·/qca xA+,l ]'*)$* PK\ Y[AK P>@Ckٺ (Z&Ђ8?lD3<Æ  Mh22ňP x ȟ`с( ʼn@I&گY^長s7G,:Fd X(r̫AjRŶ +b/$+p,q:ŵ ȟ_0s2 G]FH2o)* JB^an3z4EP܈޺“'\da*•۠0 (8;h@ds+ 9^P*C<;H MRJ\ÞB ȷ8AӕLK]?ȦlK(H(AA?1kJC3'i^Dм$E,LXl p&,#ʸ `;$Jرx`, HEsKJMd7˴ܷP÷0`: 8yc L r["0%#@ؤht!Њ$+%r9OPׄ p=kJs*d*eJlɷXE_qa Hm \]H| *"$ 4Nd DQlYwpE%EhOzL3t˴L 6Ի RSaJإ5ص G;0R=rE'ұ`+ hHT_[T_35" y^;Qe< //Fbm [ʹe`:Vkf4!lumųV0F%W~VUuc=_Wpeז>{yBAW~5QSS X=΂sMHׅum00~XkV]m?ӐWBؓ3| x=7xmY},jYH` ZZ-Z=ZMZ= x}ZZZMOZZZ [[-[=[M[][}}p[[[[[[[[[\-\=\M\…}\Շȝ\ɭ\ʽ\\\ͥ ]]-]]{x\=]m]Mu]e]ح\[ݺ]ݸ]]ݺ\H^]^m^}{x^u^[^[5^- ^M_]_e_]{؅w____v(|pu-5`և=`F`nsX}pu[U]܍\ \`sPvXana~aay[tPaa_Mb_]\5_%#suH(b)NG\Є*b.b+bv[tb/b3.c)b4ba2>c.b/~86v% `[>$d^?V1VMXFnLXuX^hMXAdY`InFuXFRƄA(eI'VQvQfXduȅHX[[΅EYXN~X0ևE~eMЄKdL^EKGnendea. @Bf>Dc o]rV]t1^A`BX LLvGL8LHAXvh CDpCGACd_}8thd^GfCЄXXDGC ^uhOnACpDXGigY6LЄAXNAHFPhni\5d@6B`^\qthB`AiYpG^GhhGiGXu YxGȄuxE n^vGM ^GȅkvafkGˎfhkBfaɎ[ǽt^mgqrm׆j&a^aCXv(nE0iv^aw^Xw0ECLhnh~awnXXwEoCGXXnvbnƄEvnLHvof;~[nמ۩mu߬Mdmwpw n wp7qkcVa ovq&pp7q r!Oqmj@n%G}m GߨVض\1Vr.`w b&`.`1rw(.1r6ps%[Crtt@(&cA@?CW_tFt@>GCtJtG_FtHIWtKDuKgRwtOG_%t*sWm @>s(^u_u`va/taaOveOcd_vh/gb`#f9r&?+oAroݯ%_wsW[rOwjvWwo$Y ;itksnap/ProgramData/HTMLHelp/Artwork/ttSprayPaintTool.gif0000644000076500000240000001566310534177572022770 0ustar paulystaffGIF89ax!!!!!!)))11)19)991991BB9BB9BJ9JJBJJBRRJRRJRZJZZRccRckRkkZkkZsscsscs{c{{k{{ksss{{{sZJcR{ƥƵƽƥΥέ)!RJcZkcsk{s{{ƽƭƭέ֭έֵ))1)99JJRRZRkckksk{{Ƶֵ޽!!!)!))1119999BBBJJZZccck{{νֽ޽޽!!)!))1)119199B9JJkkƽƭ91BBJBJJRJRRZRZZcZccsk{s΄{ƭεν֌ֵֽ֥֥֭֔֔֜ƽ,x爙#` DP #B"C/jQ"F"#ldI&S\yJ,_ʌI͙7kܩs%@y9УDQJB4OUj5+/sJٱ&][ٶ7.ܭj:M7ɾ}U/_e{4LY2d3[fFC{SgFxV:fbYl0ϸ}XD&]z$ʷG6:usӏ#}\B}?~3OV=Qf='u\QGrW5ǝL% J$ԝ眂kRQ|r-'o+jH.݃9__YHc>ad&/Ehki$Nl8_Z(䠈-[.ga#y$m札fK۷>{> ^I^<C0/,D 2K_S@ Z` AC !9Be,hf ; gH0,a  ?!y(QG4"CObD(0Cޓ7 ` HFa^ x@9n#(HqH(>qv#73pe3XH|ፁ$  :0 IL$IO2$x> a8,UZ2#r<0 <2)d:д#GI 9ě$H! #8 FpS$]-$~S'?9쀄xz! ]BЈv (DK%ԡ(C/Qn bI+:2Н~B`@~BҌVTE-R[꽡&uJ(D  (4{JP8XA9V $ڈIj_Ph\ꅸ"uu^זB|ū^;W1"`Arp4' Vvҟ1{Sfw]bRA.5FCgH d@3ЂD`6O\(rea@Jijw>Fʝdxw-`㙠Q#I q Z::s[Y;sdHFh=P*nJGmKt tvZwwuA}{:|꧄ڥ<H$r7zWzZzEک ʩz33ƘgwZBZ==z{]?:zXҚCjkz`SH#࣭JBJ>×ʬK*: P E>*A*ABJ=zWzx設S v`k 뺫iDH⣯%kkB)[1z\î$#'K0+ PP@> Dk0 o*X##::N4KugCA(@c@A[x0{0vpgkL@,Z身3J=wʭy[.s>ijO'73kf@`Q0B@E@W Qf y`prI 9$˴F'g>Aߗ7S(>)PjB GIpB v J$-#Tz[BS{`E*~'B@D JdJA;>}{}$3۴-}S=NDJbMqжup j`\{۲Re]cBzʛ[R UT"RIPD $ǪӪWiİf [@T$%U&.`6vO%[U >R[>ۻ(ܽ$Z{8ۺvHr|%W 0h\\H [  -\x˫yK"=4Zrj::zU_@ `02D055$+>j/5V̵gl"|_@P JD𳫀L|y 2뽱Z+>\v0]a + _ 0 PJ0u \S{B;\|P 9$<` P2g%@`_80HN ܫ̌Յ](Ag7uM@T;K@ZJ1'ggxM Pޫy^ͭFCxЧLĕ%@3k^*P& @ Ymzr̟>\ˌ3]7 m 6@ܬKepaPX@J rN}x,o2&Ұ=Odg\`bi_'-p#"w`qmj\ ~˲ݚ RSj*u -^Յ3[ P%z _ @[ J)M>}ڴ_x[  c#0)H v L$P\Q';۬U0ns@P`-4$H#ϣ ҔR盥 @WNZ^@.`|%[E @k .-ާݠ̺+̮z0 1^N03>^&0; ¹ 幚WV[YN fa*^桸k֊MT­Myy @{ >@5č@mC|`ڃ)kMN ڋ S^飃? O*>Wj]@[ `ү}?ZQՉkh^UuPf}^x0b&.f{ޝϽNηnlގkc mkW M69"pM1s>՝Ljx7{\ksP DK ]J%.KOYi @]E!SGr>gʇo,崾 MU<mPGRڟ۠t@OիkP*lݜ|Rj jL x./j# mugFPد C8,A 6ThaD>eG?&X_f!CeKb*KN:EOAYg#'j$ rҥ :i1S"OBqjBZH!#6%1d*iAT*u2G]wFNJjx$qVn7ndkѡՍn 5HK%K0e>KUQJzСFkװ_ eG#Vr)GGB1tNbqQ:#]TJErNHLD'sZ%1uSATUUV8dLfdO\T{MVYeuU` vcmXamYc}T7 ,ttWn#pm˕q\gM]CC:a!{^~շ_W` _ 8v{yژf_3xc;cCyd?QIXFe_ec9fsQe杰}9hg9gaF,ezjjzk::b [6lV{mvm^,nnoֻoo/|pGnqxߓ;itksnap/ProgramData/HTMLHelp/Artwork/ttTipsAdvanced.gif0000644000076500000240000015265410534177572022407 0ustar paulystaffGIF89a !! )19B!!!!)!,&"=)9)6<)3:5B967H>BBB']9R!L2-LRPH\JJZZzoQG4*_[[[l_amgZn~vhfuxsmlifulsxo{s{~ʈŘ/6JTBJRVObekN^Ch2lu"}g痰୪Ҩ踦б˳ЯжƧƱʽƽ̶ֵֽ޽ƽν  ))))!!11--99S)==JBNNJJXXp[oooovӃΔ֘Ȫ޽ɽƹޝ,HM(\%aC#BD/jǍ;)ɓ(S\ɲ˗0cIfI8k꼹3gƒ &1 8N(QӧPJJիXjʵׯ`ÊKٳXDYx)H$dKޅ/޿~ ˆ+68qŎ#]Jyʘ/kyΠ?MZtʧ^JזaM{mյq:$5B|!BI\%n!֏ޱ|xŧG~ݳ7O}g'`h`` *X\vwJ_]Xaja Z"rIljW,N1R(O 2);'8 9đsw]5I'PRieX^e\ne`~)fdifhf`6qo rtig|駞o>q3J0j(6D9t%fI$a ANdqF몮j+֊뭺민N尭+l&F ֹlb-NAh"Ģ2ģLH:&^i$')A L2|qoS\uԭ*Eb'㨸ª&0Cq+\1ǪOqq Lɯh25F92oD=sLqB^AzJ1v=u|ww'.3+9R>yķ2ψ <\4';?n=WHLuFo~EOO3z[oIh;{ AIlMtu%߾O{>C^f.0c ,'( `|\B +dJ=:8 v@ hW#wG:CQyA 쨎&l1*\|)C Y#RJG(wИ6эktcو4q(rȞ>*u>RLd!A$d$!9HDV$&7Nr򓚛B xCH7)JdŦZ:kQa s'C8(=eW 1.=N[?Y)c , `) $|<3zӞ'=|'>OӠu'>*O*(C6*F-4nԣhH?*R(U%$䔡kԣV+DSG'vBAUwrQCAp`Ey ! p;$`Q 6)=LRȁ'pH HHg~$(!: ` 5: Nb+A ,d +NVylf'j,h=ZvughSZѪV-ae;Z֖-nw;ַ.m()Ũ %%:^y3L@zІN4}F+ь~\DE C1BhLce;~k6[׮6ng wH`sb\H8B@ ηV aKp;n'N[ϸ7.k-Z}K\PP(~Bsp;k1Bw<׹σNtBO:ёt3N:΁N[Xֳ{`سhOpG; úAB7޵'g{/-@xP!h;񐏼'O[ϼ7{;(XCWzիo}g/=w>/O>3w-; spOOO?7?A0 Px xv8XxHm `0!8"X$x&(*؂00Ph@0(4(7;85:B8X<8I=hMhOHNȄ@(VGWH؅[HXZ8Q\XYXdȆibȃІcȅp O0 ` Xx `vUXnph8U`X `  Xxh8ȋP XxȘʸ،8XXp NP 8Xx @ 8XnU P g 0 q0p yI)  9I P x$Y&y(*x G:  h6Y:97>ɓ? 0 xULMON9PIRYKiǰ ^`_ ba9`hfɖ^ym.p@ Ȓxz|ٗXyW2IyXPKU@i9陕 KP  ɕ0芴y\ٛX@ ~Yyș8YɘIHؙ۩ɝ靗IPЛy ɞٞY쉟X @ #,9 -h@Ո 6-e3  60 9i` Z0H*0tp$@!Zٸ0I3i?p 8i8<٣?PGJ]20Y !! `.:`a6` aH.X"i(,5lڶ(9 * yKT + 20(С(+h3 K:5P0i**i` [,`زkٲ:޳6` Pa I ` aڤpP䤰 }L.aUN Ĥ )\aϿ-͈h(ЈmU8Š z舞腞~荮.8^b:P uN ^ L I b0볞^.~N.c@ǘ ~칮ž̾>nϞخ~.>nf@ih lhNm^nk@i  _ oop ^IXZ!?X"?)+&+/_. @ kpiP hh@/igN~??iiGRO:So:_:D?VoPRbjQ?g\RfViQ_eU?Xo|ol |؍0~ P_p/_/ sp0/ 0?i ? ` P i8 c o? /Ѡ 8`AФda4%2L8"f!2[䃐'M&Ki^ʌ93M9]䉳ϞAsQ4eQN4I6}ZgPNͰ׵+1tҔ![Yiծe۴JΥ[׮]cDcă'*YI&͚2T0lj79rɆ%kf?{=Φ1.ytf؅E~yԴC~1el ^qɕ/ged΀^uߔќԼҫFKũ}թ쭾?Ƿ} ~+ #?cZP(C(` p 78nE (DQ ( Z2 6@*pR *rBBLcp-jXr (D4Gp B="3QN5qqD}RH"CI%UhI2dz %H60CSM3唍9<uQM/5S=UQc%uNa5Xo=USVsL/aKu]eTX\u5V, Ԇkٹh #,D=A\ "`Q0@GST`M)(l%=?qR vm6 =cѵqpC]24}k4_ %M2XcF 0LRfiSbXP9LjaF뭳^٨CMjOMEUm[~[T]zXdh jl2GLGMĠ]]4kG=GR$7q1 F`'#20ZĭUhq6|O)rz=t|ŀO_ 0 .ȘR -f'Z9;JųTSZ2nS4Z姣ِe~fL0 G~j޷]` hA֏s_8A 20T`n 0AP5S ,8@ ipҐC;.\MQp.E EQ `.Qf!(GMfT0" I7 ? >;z[I ('<=1QL$9zuS")N({jd H8Rhl8H$SL`n"+yU*mUb*&7W-nk[/KTD&V̸k=ђ 1wFG︀LQO{]3Pb&z}΋kQ0УJ^2$2nS3∘-nA <#y.Dd4YO"M@0N F?tfD#?OEahE^rd}4QEAR| fWPuz~dLO,5@5-Z m*T#(~u\6!Ƒ E S (6~o cƃ*x. ZdLYQ !th?q4p|HXy_.CϼU2wY/kU#l5vC[̝I8я$@<},ıш>$M@(.'KWi cːY][eG`xfm;9HxDUEN0l#^\h\e(3UatyT9>h_g>iK];yͥ_sZT?e`,-gX@┄dx*CvjG TR uA"5T+#!2\K{(P <ɫakwIj?<$9$%3i=c1"$9$q; `wr,dva26B79 דۀ3<(YTZ-`6(9(83 C 4;:K82<:KC9Z@9kK`9_zeB:H俦KRڿM)d5+6sD,*Ct+;=BLs1Y, -)nӒ9(:m;; Y'A(BV'.1ذ%i91E^ %)SZ2LL9c4Jc*4ЬT8.``C`9:DCk۸ 3/!a!+V"K%Tk[/2rV!XIIO;d EqXf4R<Aw4)mEp?$qj+s 9`d-ͪo[@3\'ӗAڨAYܶ#!E_T`lw)=r`ځ¯P!bTXC&K PQ5\?⥾A&KͤjDc?:Q->@>RLa; iK]0 \]'(L2TA*A99"4" N2 E4NF€)'霞CRN % I0UQ Qa1+ 0D#vѱFRO^$̉r4€c"Q#p2?l;v )M-M;)r/ 'E4ZhqP{mANxAEHFpIH`P)L`NR-Dmp:=EEO#e^҆#+lr>0dJh7EvD8H8 >7BxI`v$pWCG̤9gb'fJ/yv>ցg6A8ܹ5=my+/Oނ[yl z+{fPz8qFl 1H5P|g؋O"\5qM?Hxm@A@DLSi<YiC`FXL`/tϮq#<'3e̵ @S.זAALFpi?x;9mA`BpW{o(|fʷo닇Cyo.[q~wg>tqPoeoi? q-x+ "Ohe?x++ it\+h „iС1,MM1t6C6Fd6&؉&;(BЬF8phQ`0`8e&Fı A A.[iE "SO6Yĉ9sؠ *rG 3mH*e3kdQF=?GwSRZ}'qe7F c@!I8XZm6 % bH!8Brjp rc<0AuLwudE E(GET EcԦ Ҁ =SdEtS eA 1e4_-\A8Q[z\8?g_90(*ΎS*1(5&MTEv!i2!yEqKLF?}jX',"U?UZa<-(Kq\`2#HpH#9Xn4îC3y ilWRI"nz }LqAc"L+|oe0Bpu֑\mgxP#ˁya@cOq$H?R>ZC zCAeΆ0Ĥ AˎH|@R[kP,ER=65!Dk;ۊT0 !5xG|)a0CwC ! 3Irȉ vq b0Q\,xA#* F8T0"Y Ҕ8HTza 2Cd`"`^ p{xt 8 >ȝ*d1N02(ɔ7Zʠ x!JU=dpBz*c롌T:Cg<  C}|`DCHc !4j rh1VA?Bzڐj֡9C>4hvG \,=ra aTpS]dN'?m`vh6 emt#3 l.ٝȆ90M$P& )4 P``D&zqOZsA3f$:r(`P<Ј/8QMIC?&W!lE l/}AqhԂ $d_qlp@)qAe(`1x 8i`s1?/,a s@% E\'sw!#):kA -YdwP aFZAktc7Ppx4݋;?!\|@ qN4`dAV7J(Z .'0!(q9 NOjPDFs:!xP0hs' ,l$` N@脞pX. ! |q ^%:A8B'x*OPA( G D9"!$aQl1ÿvurVNxVZy'^eњ52L]K< K|RAcFAblv88|_(Mα(4 4` Wd_!M`ƆFRz'✖\} %J6Ha lۄʅ^Ŧ6BQ~ǁ54T4p(jgxZi/fkKi^)Wk~zyY OpqU#4 1@0eɁo =, ȜEڅpJ@`lQ)o3VJJe/­(/2P1!Xh/A!*-/r P /y]20cM}n?Ie,ܳ3XKh!=lD#{TLڵJC{C*^:q"޴1i谡C3=C:d7Mmȓ?J<sYYeq-SmiՠSgfݸ5ӯWFP.Lgg6M:R2B9EˉA`ÆTvTX †))REBl82$~?N\ٲ(:fPnjF蠁:@ /{8QJAĚϬZ$x?`k cg'< @Âcs>k5D{-3)CcFI* 0sLd̘0pd8aT*8'D:J -x(设ģb!عKÀ @D?T98@ʺs'=C:@A& UCIkK3kNc>D BI&~ 20"}T{jS61cK5B/WLvˌMR1 L04\USb-r!,P#H 6d qI(@>maPkubjY'«e 0-1s|^%} _2]*ǀZzC̰ 3s)P(P*n}$>Fdz92e X Z?c掄 tg_et-'AK8"^ɆB.h%wC1" t @.:3$!0GJhR/xrRd8Źk6`V3 8x 个a-IאD3rE@ #3E#e ºhx%$*@S)@)WOYDjO4glG9ZOP_Y@c YhHV].;Vqx$@8[&y:WJ rX(ÒoIu3 rkz%FJ;A!24&? oo:Xb' %!〆OV6 \Ɣuh!'$@ @@@=V"DrPg?RA%o L:.5(W$30y Z 4ns"`=##(L`B/(¶OXѨiH>ÑѺI$=\dnA}]/vmp퓭q_4pRQ$$0'rQgB5 bm`ct86BYHA6pb`8DblGpJ QZl@ޅf Hj`{)h0p7 !p8;\W,5*Eh!!^J3lHI+LTo܍A#n#ɡ.-Gʔ,4Z&L!Mq(C)uϢ]sK\eP\4 E- ;"ȸJN{([* VѤNe⯈z Ą!p۪Lo#`Ѡ, NxXOh@ Kj#9~l&J4PF{*[%TM?#M `DXBA>E̮ #<BLV r"0 DP> r ,!*A8a`h%"B0G> NBO>TL fE`%hPS"@PjzP@Z[ vlDo$ڈ I`J6X|kT*"_tހ*| KZJК. 1 :ppcB,6YfpEk U`gHNCZ$q>Jiފ@0]m ψF!, _QWtpxj\knb |%lt,np2d s .a;Tk' 1Sn" bN0@ Tl; !aB7`0 @.gp/eA'BB(n Bh",k#3)<2P$AJ!ʠ m%M'lt p2P16jJJ&fx.0qgjAm(0L&װC%% Y#kP ,pA+!""l6,0F`% U*~;x }Rt1M$E%x' 27q>N"%4gKИ W55;,P, ӱP2.pK"hN..Ebݎ7/abSylxLR2 ؇{2522ZQ4HK Ff6 m"J4 |RZ P0+U4.@I$6BVaJU& |n^s&gZĂG YhLf*^ByXm%ӲZ !Jj$p#PME"+a*"TV2J,}ngG*sBC[&2U;AJ3-6Ziw7VK *Mp] km(l"7BPԖm!8wGnǂJajmoW45uwqW>wl5+&7<4s3{{ >{ t! ^֫ u,Vu˲ـdH~CYHI4wKDܞ=ɳYQ\ҐPh$ R} *b(7*c#D((}2qcTSA6*5<oX~*I53g`Em,Bnŋ)P1@e=:FRunB o| oKi3[XIVؓ9LM53 u T l%6'4 @LP$0ou ;⨚JB '2wPJ( `(k@2U&LXxsA_U o4uSh(6 v`(" gNb(LVS!gub.Ȯ6MKuZ[iw1ʈw4 -`j8'ު)"*Bx"ުg 9az^ a(P)䀘ш 03jPmob7_$C+(DB(z'YaT$R3;V((H(`Qqj3,̥.gt/}7:Oxd>Ej*0@ԙ,$a`-<`DHL!HA !!-`Zƚ/I̷02P @f e*u P! B)CDa!<᷑[!ۿ%a7`՚C|B( )dsX;0\ZC9xYU8.xxM:N{TU; "0 ( U,'vL!:`rX$%(0)[ ;a_&%@ v0>wC~X!ȍwHa3bͳguK#bLaa H! $jl!`]P 1B$T2P9 %&$`4j`% @TL PރHf>!ۃ[a$ Aflš )չ boApԑcJ\mjKcl: 0s>oH{7fx>Ëd~Gs>~\~~j4[1o)qT2 aᡏ&A|!!! `@4,AԢVcAQk#=CЙD+vFQ|:w*@ %dM<|\9+ýH>>*R}Cˢ&ztB TƉ@ `-Tęd"IjĉNpmgh2&;h\ha@@@!G2슲``'Xp,66$gE,A -eBk H<t v,zԨ{tN␉(LEil@ϥ"P 4Qv Lf):iвWB7mL:lКS{ M4q}>n7pƃ+ON8ϡK>swnqw?<混fiػ hS_ T@sp QC $%,$.IQH +C2E"Lx";혨ED OKaB !* SN L[U‹`?r C[@PBI!lf"n(c2BehQ{ WE@Vi,ZaJM! h 8M T @"Pt 'H^ZЃ!,EhW~~s@2A5PA`D'1K</NƘ|]>۹F= !lH8C2C%ny͋C<D"<>=qdb<N@Փ n~MVGE rM=.4>|[C=50%( (J42 )?YJX,QKUҕe-_] |#f.i9LVӔˌ-CI9PSfHCa@W?vzH E$\?a Kb7+67L| ?4X5|H" lA 5 8A8؀^= H@xB @*4.&]E !ś_I h L![4 d&*l2WGͪ|TfjLn7Z&0jUU5Y޵6kɺV_ )6mTq%M0?QqTa@-(JPQ9xA#|OLçEW, 4-+$p?*@*ړ pOk“E`v6W!BXַ5@Ap!t/l2O# /iӄV9,-Ӽ4:V:VTҠ:Xةn GviW<`g8R-|دV^wuMk7,@9yPJ0.h!+&P69P&TB D%n@ jW3FqZP(C$u#K2 &ɻLa**4Jgda?KbtqRzP-aߥY|:Mmj:y Z6޵TW3.+R %kYIj6W \v3h $n-UT`cq,rbwcە6qC E0 MɄ -q$ieҸn;ќ錧tTQleyvE~0pkĔ}{2JTުTpokS^uC-` ?y|Ks$}¹4 tUSXVzC ^asg)ë́GN#gu+#qu ).eo*0z`Uog`Z9a74"xa8=d6%IUi<B}@ +}4V>yIۓq=Xxjܼ~EsسYuMjG ncOuYz9_V[MYS5/܏r8ބũ ȢsoEqy ݶPsP%{g.+B(u(:,`zfT!HC ,fU(egEy2P! /a Uff0 x]Lр *SDi)߇i 6-0,@!@T/|eb;<06J(g TbaKHeePei2 Z20BQ(T N,]a}a5bWQGss`&bTr"fVc\5@4 P"'hW% Pw^by d 8X:6>Q1U3$ph&4>G2#6wr3'e 3P9(],z")FCzjF i {{i0(!@ Y@U(p<30xeVxx I\ x-ukeXUVF}~z7wi!yl)֒,jkfisOt'V6GYTkf98i` ce/M,y[uVR_R_B9%BA0d9$^T23V3ZpsPxhȔu6>',P0BA >Z@[X;5`79pI?!itju&{TH(†YЛuhK#8„gۺx? jV1M`c ,(RР [ $_$X]ejm\oHtu{~Y;ֵ_+^bkk [BIV$(@T&d@t#l$A-A+2Ld'9<tӆՖMAePAX :^ W < r!Ic( c0\@Z(e$Uq9@PG\Z%$HWa S:UW\ah^ܠ3xq5A_R쀿BTrPVҖfgəɞTLʦʟ\ʟ,ʜ ʜʣʲʳ ˢ˨ʱʬ˯ɷe,<̶ˮ\˝l+,U+FP(HU$ez\"[#[ 쫸JBg}5Qe"q+3u$`5[?G Loe d86^e'7zEu|t505~0] 0M57 ;1M<7?-BM@]ImGA=Q-O=L9YP-\Z}TEmԵדF C 5K/]Q U~7 dM(Ee2/j' UBx"$͹Qzcc C 2s@|};dY)؁R- 8ba،}u|d,o_ z3i;U#o&˭-pܝ-o}ӭ֍ս ]޹BFVN  ^m5zeHojyoe/T<g!Pe r $("5|mMp~@q\hBf. e xt9e^qp>ș@k)Wс QQ1P^NO]>嵥LZP]dbґ`.P^spna֚ԩ؞;Ɩg79(f)>~#^,ud'Xą\Ro)yjY a9an+_ݙ" lbR*Ƒ**߾>)y*|b˩y -{9 䉰z.<&F4"K"Th37Q8B 4¬@))2n(j2/B Q2PC,!)*r*Z"*B! jkޚK,||G +hƈ. ̞Ӎ94ڋ*hS ʈi8ذA*la7'2SDF =Ʈt:'QZ7=i)-.lThQ  lȂG]d!-4à3QTÆf4pK8èua"<'ț<Ǹ4Hi!QaiP! 5JH ,TĦJI#( +J!q]p-D lਨ m#8)c*a!2,;K 8Ù , -Rz E2 RThAf\lNp}w#]գWR4 ׳#ZbGph$saB';iζxHF#cױ(ρy!6O;&GH|Ԣ爯d9%F>P04fJ4|KkDޤኈ+cGmƑoiP)ԟʳQTv|=Ķhٚ* 1(ʖa]4&4OSΞ~fNds֋UZЂ`udfz:ԧ6Te!ղuf0Jb~nmle;[ֶmnuKۜvnp \6`FdϭltTf흳{֕Q7'|Ի^׽o|;_e0Lz_׿]Z1"^S<}5a thYʆֳjvAaQ%Y! Z "p`4@H"t X0@( ;e W: @1ޙ$[fJTqA~$2Ŗ= wNIP᳷;̣@>ѳI!N469ý#/|9't<#2*1B7B0D1T=FAс?O[[!0,=Q`P/ 4 !8Ђي 5TX: :?8648?AFSؙcg1#9P?iYj1".|jq4P13Ã{4 1&(T# #S>3 <9C=s؛=:=|:̽2=\H).LėH HkA0Cđ1>Oh j2b 0sпH h-:ZD]T `Œ@E blacН[mX 0 Y89Gc:@LAX06K3~ d`MX“s> > 2+k2ϛԱDһDG ?x{O 5+c5xq *xjDPmXQ `1pPy5|) pшy@; ͝yi(7 6, p"bZI`Qk*}T&$ sNλ<9-IEO44IBıx Ȁq9['e96;qOL NL Z"ez8 "'J;T'U c,h08굵@mЩ"ڮH&3<|{xFCڀ)HXO 3(MyBM.3*j 2lL4"q@t^fhiil荦i靾kecI+~< w819^-a{BS^bHVBMlj4ȴ{⠩׋r4H  8^|}e"tj:œ3)I j5l@{2;8Cc Hik{k4Ç| O&25-,LV is'4h`hhnhZ6,0nF.nin`sTYi徆hhj0i^ǧ]"e1kjXAޚ BCcOۧ43^kΆP12qpV?ΌC.dI9.ʾ3lU<Ϧߎ{NԒŦD^Y멧XEK\全c&t)h2N6inN2~iiifVnn~ifih+BVjg0 A{=2Oт9=5̓;.9&=@{ 4=Ø0Zr)C4ujutUJDJ69 O< L[Ҕ^68L[b!D5XGhfK+FO-H 1ձEYO5׃<}¨=88..64_7`;gL=gZ  y q1_RS0+zسAPCmO,խm7Y䊭 9pIt I1Nw@n-Pn(k845|hn7n897<"{PV8؛hڇyX2CN/*X3iBv- ;v _u/82ȴ#0~O~YBh~]}z1E  H8@B2la 6 !6h14?qPBb CǏt،e2&8Voژ47Сfh>uSU^jV\@bCB5Բ"lT6m6D⢡EhjmaQ+׮1C<#2fP,ka45C̍sghYc1if_ jӫ[{KulSC0eBT)Tsư\936"86T=ÈuG00@!~K]9-PlP!TE 2l x@E~!V5 B 1%e44HGt5v(xCuT ;r^|g_Erd (@ *58-;\ؔFN;)B6Pa'0! G$0 HT;&a z$9PWp%8C$2zHD9)duɢЧs'*x$v!MXD!x;B0" qB1c!@Ɓ;CBe%,MXHŗ.5*\24kf3 oX3ڼ5qM{ps5mR#ܼf8ubSۜ'<6@R-%QjЂHDs!9F d:B` !Cbp> %2aqA{;1*C!X r=4|*Je8 6b%D PPB$!PhO XHAQ'dBЄB4. (cp&25ih_24mLEZ,cB6 aKFlrD{W.ЃfH3!D,m5ҥ$6?ApE/B#ˁI!J|u~6p?2&ѾǴ4 K˩"b[$`nu mvE*BVLo˟yn.$מ$(C)ڗcքa'5u[p * 1KLW8*aSc[ZrmA}Z a4宍p}$?x(R{Zag:\.! 7! Vk{ٶq3.7~L2 p!:Q9,W:X[żtJ͆ 4aRViS `4a85gmk:u`rkX1^ Hc 32G`HܣhD$#-Ϸv+tʅ0(:IQ@9ؓ $,fXiOO9O]M'E&'!2fr($`[:,wu(3d]+H ת;R]%J,@<ԩ>u_YֱngaNg] wP! ~UpZr=mTG#q,(KH qCFAA7\.X";)̟j<dt,$AÎCO%)Sls=t ռn:9?Y@xG` . fA; f!KsYTMhAi9HEL} i#TB+PAxGy$D p@` 6$&Ď䔁tXDDA-Ý) V@D`GA4L$=C&0!dؠGԇ[pAI@YNN` ` sHD@-8 >MāE|__) )%A8fk4Pb؍jpC,j--bjԢ."/,ڢɯ%Rly \` K8 } pUh n< .}dBDP$D@"@BHMGQI? 8SA3nDܒpͰ[Ja@a!d%dB 2D@Y#5  Ȁ!$BģQu=L{\UI] U, %:S h Ȁ6P H*Pc҇H4()6i `4BN_r)_TLN6IߍT 0'0 k)(rh  Q{2LHjR]1XR9Մ*jҠC˩ @CѪz2I-52bԭcp[B4 {Tvh`>Kz .̄dx.5D@}@.24))ԮU8ڬgt*/Npx~_'qvEZH,Z I yYAP,@uԂYpV  p10 1mb,5tenxX^CݜW Fp"F@V2T`ܡfrAKH/LwuL@EDx;D-8jKx puY *@!# 'ePv&C ĄwjA Ef kG\̃nBYN @ @cG@]ÆƉ@Ѓ ذA ?~1'QP Zl@# Ңh*rp2g&HE2\0xP\>hbl"I1 ?QI'ZcAaICƋ@J-W|Y3g̒5 gқC6tg׭aZvmڷOVM2餚x {Ǐ3Sl#4h*ybh"(8D)sN=J8 ! BXPԳ56cT8ZPK-*TRAlr:Bǝ2=>v!> @-ϏZP@*\j1gf@'F9HhKhjѲK020<3K,\sK7LsK3L.ی+O6=? OBJK86 h,j8ʭ@hy'LTQ*yU*[ Y>C(.~.AT4 (: SE >|քP\)B0a2#d]Y.ma%ZrҿcBIqr1(%} 4H583@F#2<>ÌS^YI7d]4jd9Nyf7gMF蠋VZe~[霁릑&y86hJKC6ɭ8(uܥ۩UQo^zhH ~a/έSI !3cΏYBcR=B8v*VxiUH"h< H )q䢣ɫjIx ;, b6>{|}_~c JӨԞm`SZ b,+nC1^ J$,G /Z1v Q&Qw LaB~ Qk *TɲPR= $w F < L s`,p)s[`%a^_t$-IORch񍎡#2:qrpG?B Rw$!X7rJ||cW@.v'H:@򐍤ccM8dC@ 4BDeQ'0H<^,F{yP0AxNX8rw*`(9lH./t-ez Z;X;L&)0!)q jP\yYFW'GtX8ɇzR%D1Q(6ъF(p.Vҏ r)EJIAT-#-&ɱ!l9@Kp ؄DVt'^%F`;CH F`h!Bc&Pe  v1P&<*#LUC+jʠU HpR@:h]ZOǬ%( F# fbsKG)Pn2imp#%:l|IkAuoVORmpʖ&[n;*ũu{*7.r[=>ti:fJ VVJQ+:N  z-!c`zds! *j@A} 7\x @*Du(a{xAIY0a.إ 1q=&)Ԣ$F=*IFG˅&?I?^`Lɗ44=A:6wyt(AYɘjdCߺ϶lHE:f T4¢P"WQ`RB+p# 2; ogz, MA׾"Z*-1 l 8Fe! H$Ȕ b/QB)fZYp{Nim$[unlZVޅReo64nW;^z,w]쎷Kq?X>YHb?BmF٦F2u0|P#jZBAHP9tS|M3ATD!$A! AL FtR F6#+ФXq:TY] nU8G9[0p:_1QrsE*Y4\ צC $6J&"@F 3]!> 5P.,qiaCsBc0H%^Gg@N^r#0`ssArVtQwTruJ}Ơ'np,>|wa1}[}Z7}W7 W95}ar)pP<`("rŒ t@ Ks^)UO.%%#JqD$s[vZ F3Ȕr"-2A>BcIZ LY=Ev!.*!c9$jtNJ"`[N'@KyC6>AH!N=7ݑGtsh#0Wk+m&uLWkߌ2:ݶ|yE%|zo98t=e0NJBBa2*"F!`Z F z>nbء(l`B b"0F<DŽf$,J[N;0MZ>#$!僅z_S_:V-u &ֈbL2Z\Ggԗ8/79ˬwSmQ#ܣ@qome6l&`E Ȣ(~H F#,:A4aI>®fPus8%c0G?GŒb%P F@ W|naXAvb!Ք)cwijMHQom?Pp6зlJI2H.2IvKm S+x* DTP"&4y]|D67_& u"D J< , .)+?SM@ R[=#HȄo=~n"vMb\( Z z#ۺJ?iZ3|3*O(P~ 3ۏ6R5;eTY(aT`Wf^؄,Z{iO<ӹ|ZԤ* Pgjr[he"bի«cI.XVA𤪃]J8HpWBTiHh2-62R-1B؀z`Zl00h]°LTQ&v@ P0L @%\Y a $;qhA q)2+ x O,8YfT4YJ Z\۵^`5:'ʐקqӠUפ~ :{L(\5cRqhZ`\v5:RkFCrqۤQGi }+(ІIKlYKf[{ۻ;[ ]#~1ϻO[Z\7>xq|Pנ:vD;I5@1Qw漃k_^x˓ߚIQu{Z'<#Y#baDvcvL <"\$|&(*,.0<2a砖X&+a~pe&9пDe tt|_JTY?#9гϻ\DCqWggM>d .NN(n/1'',)^:.9N&jN `; Egܻԧga2fDNmK GQdʱ~  C;<@0M1LDbqqĽ\/D]Y]JbNe$N\3j"J? ;sFyKT)TA6T7>^~.TYBT`뾎=ZiSYnF* oks1IRdH=1/9ABJ'M$Գ˵Orܤ0R +,0\i@-7<5^i1ڭkf޾BD$ $.#쾓 T!'8d.gNaX5|@%af'[`__04_68:1orV'0D_F]@)4Am3M- нTpUeLvqiKġZ!$L22@YBte9P3Jz6Og$%&w|yHg`A!%4ekWUhk o|r\cxOQdŔJ&!54!Th0GI7OoLoCn!N(6Mz7ywPTP;`,ˌ<炢$lPRjF ƛrJ֗iB RШh0 s)A_Ab"X]vr%Z㪦j)GJ#jMô0 !-,j N΀c8Vs$(8P$ˌ-70* .HPKM8X0:CW=| hS2F`C!aka m)h2 <92Z[.*ScBz$YUoDg0.evTР'`WARF[^ain  u4ma/N! jJd@*A!YdQm[KEYZi]kEw,uϊCj`.k`h"z *Xl. g{0}7!'d-^*&9ܶpX69Шqܘ(4\Dr[d4:'B8ΊMC`XT\[\U+ T%]f1j·R*`l,bQqL@bG[e5+© d4YM`^8:Œ6P䥴Mvz(7=H*WJ\C[&F)B>-5$h5cZȗܐw*WĦ IHn_pc$*,Z P( -`EbI@9o%Rf7Iݖю^tBE! 4OlǬe%-zl+"Ɠ$PpfU[jWU /kYzVvU#[cHHdw¡C1[Og"r-B`UX栘7ȥY4BhE-z.Ta\ Fåȫϑ7졮4R%nq{\&Weq_F hnu]kx.(FJh/wezۻ/}.Tr"z_W%p^Mnwh&Xpx+ gXF&M΀ eW\ g%^ULXtBh, X'-Y1I:57U(ä(l`J|e,g Je09̴!3M#fHoխ o%rJf'M`4h*2~yhCx\NfpG Al2tUIC|ѐ (a ܧުnVʿA'wWZD̻浕EP^UgjF5ݪ%l-A[R.692`rYZXT. Y  -sxpCcHfl^W׈ 6 3 HDѭ2ABhF l˖;4 EKؚdZx2R:~>Ig@| rLoՏ>_CwJpTЮZ 6:Y&7 -PV f }C\ F r ФattؠpYABӢ.{[H&4 - K)Ak! = i3@1Ե= 57 .z@;ӗ~}c?Է>Pϙ[~?/?R7lvؔ(^TkL[T3Z(=DmEUT=TF}TGTH1اે@v(QvSvPmzST]UPW5U[}UvPUVU\]VU_ 0UT5RbmV`ZegVgUh_VjX%s^Vm WbW\@\R=q]r}nlUvo5WcxUozU|WlX5X}Uzdy؀-VXlPױЧtXX YY-Y=YMY]YmY}YYX$ԙYYYYY Zn|-Z=ZMZ]ZMY!OmZZZZZZ [ڱ=[M[][JW.[[Eۮٺ[[٧5۾[z؂=\M\]\m\ą+p\e\˭:sZ${[ ݸ\+ 6--U,c]@e-80؍5],]UhH2+ձ%^ d-ݪ-[޵ e1 Y^{읐ާ^ڏ'=ߴ m9=9 _i . eߤ[%`_8j- jq (`zFࢥ^5a_۹)*2:vQPQ- .(ⶱ_{hޡl%#n&v VM_ҍZ0ZﵝΙ X  A: )vb*:Ҁ/+P"air#.b.D;.D2ZLZ֫ A٩ P#"8*(.bipe`!!e@c-))^)a_.)- NZ&hZEc+1庹#a*Ja c)"lIr)J%&X;,V{)"R-c'jY1>覽A08ɺ|>f)+/(S}c`,SdM^VZU]2_}b5蛖i>ibۃ,j&]03j~ل^߬^sF>:K;.kSՑs@:~+UՑv k븞ꛭijlfa?n>+>4k&[ĞlŞ>M׻NƮξllž٤{Vվo=sh[EsXԶVUU\զmVm[VXl>Nm[EnnֶUۆmvnR5jnnlno.ou[~Uu]@f@~oooooop7popwpp p ? _p p &e8?qOq_qoqqqqqqqqqq qo$cb8eXr%_ehr&w'r(r'r,r--r.r/s+,1?2'.G5Os67s76s8s5ss>? @`'ptHGHItLtMgKLMtQQuR?uSOuP_RguTwuUouYYuZu[uXZu\u]uadaW eOtHtgovevfevkvlvmvnmnpvqws/wt'wuwv?uwvOxwyw{w|w}w~}~h;itksnap/ProgramData/HTMLHelp/Artwork/ttTipsFocusPlus.gif0000644000076500000240000000042710534177573022614 0ustar paulystaffGIF89aIUmŻǮ̱ѳ׶ܸ!, di8*lcxn;H4H R0g~DKɮZA v8KXlq<}/_0 r _ < _1 l < :7 <  3h *"!;itksnap/ProgramData/HTMLHelp/Artwork/ttTipsFocusResult.gif0000644000076500000240000012547010534177573023155 0ustar paulystaffGIF89ab'*#++*999.33*79M;FH%awL9@?=A@>^GHHHIWYXXXILQ[[gYYwLNoXfh[lt]qtQjsb^je]ybb^iiihhvfyzpnisrmxxxmsnN?t'*.?(5/5 /'60?2<nsins5I3B5R&v9z2p"m${-vABH&N4F#Q,T2JUQQHUQPJd\jViLwKgOmRmQoYsXt@~eTijimmxlrwyyzqhdzb|ouzvplPMywqs*38QPmu{|kxvopE|¿?F|ywwFMMRV[Y]^b^behfjmqwz~S?FDJKQRXbfa~ˀ}^aud||sZ<ɉ։đɗșՓ㙧ƝáʨĪȪӴɶҬӯ䠞›՘ϞǾ֣ͳ㮻跫ƯίӴȺղͲիӹˎĤ¾ȯʻͽֹʯ!,b'Mndh°ÇbHŋ#:ɘqCN<2t2QCM@2rrH.ST1&D8'riϟ+27h9إM҃ `@ֵt鮦;FJ_ FٱgV ڲdʍ+WڻxV[/9U^k+9kEof`ŕ 1֜/ʚ-;gՍM]涭-ke7,·K/bLV׮~s^O<|iQ9!&2!*袈F)Jj饘h郒R(5 U# Ps*0SU U 0.vc*0sj1sK1rvLŔlQULQac:`+DA"izm0v̱ K"5jc(V)_J)ĝ2i6覞q'pĀɘ\^:N>93 D,AcKWt q 1[011NU CLs[nCq1#0,+*CU#1¾ ֿIE ey$2PaU OpL7n>Qlapp14ɨ GPa.sCXe:)X' '8hT#^s`(dNlɾ~_p>Gc5W-e/dn TCb%0QZr~ Q/HKN'jv]z,/nX0e _F6cL阪11xdǰwLp6-з}_7p}:x L& 0 pw€=b\ 0U,`O7&Ȉwlڀo{ .d">|}P 'OTr o>졏1d=0kVd5n~(O3d4v|1Њf3>0;zstdA9I&3% e8/N~4#5Yʭ.5-g;:ʇu|kAzբ6sMbb4>B=kr=esb󰅶mqmBmq.w}tŻ=oX7-` ymW\x{o x -? 7pX|õ-t<6Mn9܎y`4'8}H@:tzH[Uշus]0@N4@֮ `@b{ Ďxg|"@`ϼǞ]xk}J'z'{.X=^n ;Al?ب>d(c0Gq@;1+]R-au@y~ ޻KHBD41ٛ  x (!C C  00l  0@88""ƀ DH2' ps0# X 7W #h V8ЁTX $ ؂!Lx Wv800 @t@ |h T6 p 0 ` `~ 0 Qfm` Zwx \L* 3C7 1# Xm @xsPf8 0t G @-a!0# ^   DX8Dx#IUxs mS؁P fX JX #Hu|  Xt' @ ! @e0 Xh ` T з뷊LȄiOm hw`iP 8t$leXC  8H p x uyhdxsVɌ遡 p\ ɔYu Ԡ 5 pP{fx`F ~PV` #@ @ @ePqtRuRUט 0Ȅ鋻OxmYc9 0ʸm ɍH@ y 9t98n(I Y9Y뙂f̖ @ Ӡ =t 0tgvPFe@0@kl󠣋ty~" Uـ`Xʡi%8I왖lC73` I"( j ٘XUX Xؘ]*xci9Y8t!K| @k @ pcfwHꗪڪANG}Ӊix07F(0ȍwN(C7 &Xئژ.IG脜p9lz-( ؠ ͸pn0ٯUin!I| ٰ ې @k8 8(9%; -[X؊k XEHF|jh {d 9tyi᪃D8Y3^۵]Kx cZx Wڅ)h):T{v|` Hf0`s 縸 XC{:-x J[j:ty랖 fphl|JjJ}ثHPˠ Kڙ9h[+wCptۿ0w( HhA۵ڍKhI ūxkiʐ+J)wاˍ}a XXj$Ld۽^?ܠJfxJip@fCttfVif;epm`NL)X0 ț!p Ry‹FG[MdkKX5< \]˘۠ۅ(x+&%+c{7eFtɠ P  h `b6e |,ئ`; Ziش9 +Z: ل]{ڍ蜦 2m((f ȯt ɽv˕l MR 鐿Ԇ0  c0 ` Ǡ 0b PҾ Lp v9X;mj K ukHI\{1ȼnX(#۶KۚL:/ kφ=cp0b ؍= d L,׽'x(CT؞*:PxYГek|-Xȳhe8L&3` * f[Щe Zv 0 0Z p@Ȑ P 0 P Nm`Ɯ 0(ܵYI{ 9ďcycʖ_ٳ1<͛ EX!Ә-nHn{U8tx+x`"@˚,x-ђ;븟>g;sTM^PF^u@yBz9fDk]Z9lԨA۳Ȃ98 dxڏ,Ʉ܀7=tPp~>꫞~@ bГAv0Z"C8 [ ݻFmԏI8ᨦ *;껥iRh :f-628ܞ Y @4   G`zPyv,ܫ1}Qii N=P  -.N$(فkXOl4X ,HNe P?Poh_ia,xyfz0楷U_K hٻڗ9ԔɆWy!"z"✮ ΁]O(J Z_ף/Pyas_ewy/xaprܐ!H|+:Yw-8 vfz" Uj?n/nq @Zj 5WƒrbuI#"haB<AD$@RJ-] PLf+{Tgڴӌ MOtبQ)ԧ~ZFԨQ$goܫ}^Ee(A f!^@T aĈ/mʙ8WXz*֫SYIU9ɱ/ɠs^ cj x藤Km6cMɯ:D+1 qx .Z~^7+Nm&ѡo^س ҃3ԞG 8{ܿϥ?eۖSmٲqv1@ tFu)YT9pgX%= "8:O$BLP,^F!̉H5^ 6M0rAt[յ0%5/oEQ[cOD ^ߒqX199$ $X ᵌa"3 MnA˔n:ے㒻E{N:r" V /tu֑1'{pDDrǚm*UEJ)@j6RE97 x %EOMՖ[5@WHEeͰddݕ,8C׷шLv2N6dkW$/<#-x7VkΌMzSwZG#v6xy[iXIg⟰/?m.ҥiOTؐPrٔl1'dgZљ `Dm0J/'C(K0n10u;Ѥ{M)6FE>r :i6;FNƅvG:FESj-Ma%@8‰g} `_li- c7|B`)7Сf J6D7bvpXd`PD3dQKGFT"$Wn"qF{d>{@AZR`(M$@WAk]s$3>K"aX(W- HWWE2ͩGD JG>r".X jR{4 S0 UO;]Zg%>1qQ Pԥ#AߕG!kj]Z֍26Ch! Ɯho] \ '4`Dأn82%N ^e*iYGJ'QBn&ࠡX1Vs <@Gu( SE`]:` )3UhZlT-Sl3jFk? C.dd-'xЗ`gC07 NNdG7Tzmjӆ,eI r0stx*G[E5JD1FdYY6Rq )l}pǞUD#:q:/eTX VCfC'<M䦎e. t E/VyC'q%Idk!G91Mk:>Ӝ:8:lՐwK VT (>f3 nI&ݏJ5e~ID췘h&̽ >Bc=󙿽 JR)$OBtipux9&N sNl½{ŗޕÒ4ݧ?uD:@(BF#f&🎱ՠ,ÍmLO~2U'֨8Pu.%(٦Ta(˓Y\ y{ږod=K7=uy:[&Gn%pGvW c7If@܏ItQzUX3%җά90Uj;^9 @_AyIN n<,+36@>AY"pVނ.BD `Sw(ԁ>aɉc-\b.QMtbƾ!D;0Mތ26'z6xP )Ńb  ɡ1@g09i@A9ciӧk :" -zj@({wR:9ʗcd6 쑎1Z+|%dCX tJޘڈS?Y23DCc54 7Tϳ&@ (5x+3ёh*KyZBJ/wB?hEh#ȈK8yM*A31CE@D8DAX Ypӫq94®# 8v2TftfDhF3yF#B[:|!:@'1ˆMs XlBþ;2+jC@$̣P@t@ <QS¾JBzԾ1CHKaŀp7:B?Cȁc芯ƜEaT1mji ؗq"BÃل +ې,!TDFdB{'++'{DRȼ+B|k=LJ hpHZ< *Y-` XI0?up @(TL܉ɝL3L6A=VX6 aM[;{O9匊]ICF1d% ٩Kg)6Fd=а mp1:7i:Z ڈ4>JBs|AEVc:ZqњKP]1؜p§Uۖ 1 b2681Vm5@9;L='3V/z\\o#Z2/H{3oȇwc+{|Z[M v-AU: Zh9|< NpU8 !kda ԁ3x)g`2$EqqiQ(;qZt">zY ,4$L??AHYLu#b4@4 v&C@_]b"A a9X 3; }~ǺNUغU%ѲLi` `ß3aq١Y# ')x pY'5OXbX|A]/q[;C*NY@sMB=Epɨ I mH >Xj^, WL>$VXeaE\:\8kUGܴNx iٌ1$0mOCE xEavD=Ԯ\;@^;=I,ɃnAD;@"MUI dӭб l纽Wjռ ze}NR߅@ƍ5uBu'^G"֭ LRqgVERE#4>𰋩E ڐfc&fy3'VeCVR w~I2`hLw(H%BFiV(KS+={,vvvMzuuNSuQOTVoNu)͢R\}eg5$-qq2Y <_`:DHD@r_~寱\eyg$`ź7*}"ćuÇv|x|Ou|'W?xUgxFP Ѷ蹗GUXFUAXqu@0h2Ӄ<C&lENxr'B>X 4jJY̓FTVrYr4rzLzX7ϙ<$J$w`6ȃnf&Aw(/G Ȟ\Ls;CVyߨp^ }x؇Ǒ}}ۧzx{ l|W|$66"^qܼyvJً;֞U>OylJ<+y78%\|+AqIq=..xD| „ 2l0:v&Rw'N^ݳ#ȐE,IcIrr%̃@Df͚0aڴ)cW3bӥKm.mIM4]䳨LzrԩXbc…K,P5Bq 1dѢK\ .S1)dXo߶$׭ȑE6MfбcibfMm$`زP]#FN87qm1c<O:ؓL.;2 =,>81LF9Ub9 VXR QﶴXX-*^M&p嵗N+ej0.7CLf&$4Ҧ=n$.H/0 /1N2b1#c䢹/284Jq$k9+\OkVnm2chw.1f,EMWq҉l-BO;fVbpdL'\"+FL%~8=vmDy$DJ{LDEx$وLo"-c _\C.\Lpb^3"'XtCF}+za6p,hV&u:jK@&lE\IUpxYIJWdRCT(2s(3RB^D06,E2V#"G8摎ZX71aBb;jp@G-A$؍.ځ(CαH/(cSC jL hf )e-e%ЂOiК7hHcv3\;yt3wvj,f¢a: p!N@3 f2K S9Tu%t'?ى$=<QZp[)'ppc-|iB8/P vw)bX>Hb8M|htc<ֱ>K6-q G=PG VMm-RQYӝ-JôioqU2yt $`Ci&L2* BBw]Pd%ToFڀ@ >@{m9ti 2An7ct^F oϙ~9-}"@TnxOl15Aacɺ1j5J10:7y [9X$KdUʣ=ʉ'Y؍Tu :y[)__t` EàבDU@sLT^!E''%bpXd(P!Z/}| {t(Ծ<|d@6UĵM; v%34% 1)`)3]-  ڡ% Fb B)E,XY)~ hTHה(W,|ɔdD_/9ѡ**zìH11%AX%0N魃Ρ8U~-] 938ץ؁`#tu|zHGF'=Pݵb(]H@~]<(UhͿK,ū_ė(vdFtC!0߰Ɠ܄Mh#qU=nV9u[:ٷdO3ßQ]:3֭pWPŴBE IlX D,zH,pBHZ& mR ލa{װW !Zi,BN@t@|]ND5QlN>34gr&80g&iʙh-f4|i7AłM>T$ SRd&1'PzT 9Yx噐ZR +P(xĥ9gQ/B.a$z8BZtiBdaZasQ`TpZP&77焍Vb6y)ofSѓ*F(Z?/O/)Xun+MJȊ&t&hp@<j$5eВR't8/ ؀ - @@z4ڭƲ7TzyRAvV>rS,8 -|ni,l7+/7'GtĽ`yWTG+po",/e l 3؝d)Ep^&ndd3r0J `&*0$Z 7-iB$&+\-HBp-9Sr¢huST,5<,j),؂-SS<>fFJb }AaYJ+a%^w+Nrkk9^>?PFѿGxGǂ,IOD؄uT}UE`rP(@cN&z&T;ي B G>Z@9dI&t"./tSMbxΉ?'⊅(Z&U:8tE5.\Tt)ZPq1≏7~|zΡFK1V⽕U+R*V@d ̗kF89reȕӼ={j{eo]Cvo{ŇױN_֦#k-˿a\rM6k41TO@ۗ;Qs*UJ6:QgM<|M[N{M?SﰮNbz,$0+:VaDؙgyYŋ'F6.̑yi3^bnL0'BNI⎯[Ъj)/V8i zM֊ŧ ?+9C[ L=TOˉ>dNBB y8P-6쉱}ӄjq|yNOTS}qH%D;%3AΌ49Ҭ. |*)I=X{^ ӂ39 :٪ : lY=qeyĨ{%&I%FYu `F;G$ 6 ʼnNa_kŋ_{֑tb /!Hu˭$#7zNJ*ȓʩr3)[L_06"z?zJj/b0.{4O8m B,1auE oI\3zRv.VҤ|T2(*0+Nf;AbMAMbX,wꮍB*FsȦw7m:pm ${q@QGpb+G7Zz>XE.|&m/[Нf8SRA%Oλ3/zԤsQ YiNϔbСlI 0-%ճ^:=U{=NxH,&|@'Fu!9W̃#ۡe'` ,I&4m:n9 x^DG).!̙_^W^E0N[qIZyJViQ A ߂/5^GS'G0/Rn">XH!xP:4Hb&D Ib6,qXAEU,7oFI\#-iƨ94iKs<[I S|QHBJ%҉[ԂhMJ Q3xEҥ&m>4 :)q7U!" 7DI\""p+6]"S YveEE.v8I@ NG9UF"ewbW`4,Ex+xtw vs H"N}&H+@{dSy婫[̾L^؄&.S (OM(Szh s&$ n-[hBm$hZKV8g"4i;@$A,.>n1뇴ऩo,Q~&B<NNN WG::`~ vt$>i)xZ攳9IvS.dZ%uh@ I6,  9ȱx6 u0\rca;R L`IkPŤdUz(`Ob&^a*H4b=n ZjnNzܧ4 -W!4S9 jkC$C7!a77}37a7u967Sd!$%<د-kۀ)<3fYk , No":p_C9^2_5ra@_c~!8A7z-7a77rA5 L|Y(Xa&֯xCNUm _W!¡A$j<dСAa¡UaayAeVF8BІHhXmjޱ RKr4g'$V0a( RdPw9՝R,3 SڠfP>IwXLTEKM4LLɔKWL)a3V51sf떅3뤧Dn-u^7 cwN >0' 7f\ +9+"ޮgVICq*aC7T7z5ӎX/r浧ЕDѮCu%vE'Bmx&yJ?RRHc^C^x3(jۙp9J(,LJ ơ[aھ@ogt9W q䪹Bu?(e2(>x 0a(ZWYiVf?+wE+$hl,P5O9@% o42#B9##y"|<);#\Lp!Vs ;(ZK/ 'W4_NA8w@>j-E'zRiϮ&u+@/-e<,4;PU?!Ї bU@@w4WC)uue\'C1^2~y jX+X(DwkZ@tffcTG dzp(06_1NB7ZEb*f!4ʧ:ajzM)kH o-!5Cdh䣞Լ[1Hʶ}2o& 9^B0+|6a'e>S[l">uJ^+h`lA@5ց0a.2\l &J-<㿑yeI2)J)Ϟ4h~ l;Xdގw47s|D F;pq)n# !XAt j $Ai@~$a.@4!09PcGx @4tؒj|&N2cU3(+ֺ5R0\(aíp ldL^ZԅZ.XDxiQڽ7޽,ܼr ۳uc;ph};[m-Ρ1Cc5bf[F\k];vAf7\=rp_ċdL`˔K{#tЇqn:ۧSriK[.oRT(a~4:{fÇR恄wI%t1v@ǒ'eu it4e\']fNas>+s-f ,$kclE d97Veqs-|i˗]%\i8|&hߘ&hiޢb~KWxBA_h,BKb)yҁ4Lj goh&!zJ,6jht)f'c~Mu9Y\b%" ɯ"q &Apa ;6s%aYarKnmޢ-D99Ifz>Օe[gƼY߄å19f-eҕ)試ä4T:7Prˌ},j,,(ySrvdVDQD7)hɮ\,"\&HZoQNc&OhcOڷ>k˝6t7kТul٩b>V4N9p8(#mC8+b'?N̓Pһn|j-݂a\205#4dz21?b:h3ni`I! Al[Sv5hdJD(FQX2#>|h`NFcc&KIt8 p>pQ/-b-88YDMjr,51_,R% uH9T(cLF'a:̓6C͆׺OB f<ԘƉfQŁV91#}AocqZ:|J\c?sPJ3DT=HtK*aP7&mV?}O jAv@̀R(xL&C>ƨ(Bfd`R(#bӮ(Au[ }1s#A]S Ժ1Ou i8y%DφXDN0MT&˷a!T +\}rzT.EÈe2rlcLOh'<pi\A>+cfzj0+JkBf1Zm`SFjq{ol!cیF$j#LxLk'*Mի`T(hY?pL1`MI)`\+1V42Vל)uU)3QrLtFN˗@(:hZj؂ '-8,X x/U,(Ə0qz'J,<UN<ʬc+Ą3w:Aa-jwQJ)2ċI '@C:* s`ьn H?n>W=F 6ċ1J6 e~@Z6 f8)Y/'B.PæXrV1"l2*%5"+Yu& 1(ύt{~A,[v:w_H&pΈuudyՉ* 7&`  p҆u.> /dȖ'uLQ;yyjPxu7`·|aG%{Rrq]2ޔ :4nm~}q]M7C\W婐n~%x1c25/RZ׺ߎ[c>mGk?C]V.Y4ܟLtH~}pe kJ&f2r m)e4eRR4(0"bSs/1(3s}1wu!]C]^b`B4MT4Vvy%l1) 2{k&m=ۓCBJcKWl'A&T4qt(%OuȲ\ GUFG081h4%St}O3.W&g⁴Y7|&xID|zK+UHkeJ"Q6_#5_B;(E3i%qvH"tmr''|w\Em Tf eD(G;d{RlmPp"1s e;7V8{3XXgB#{233W7qGYVG؆Xt9@xC>f^[z7x0x@ !0 !q I= !` 谍h|HFviJ6`i'<(%2h Jȏ3kOf WT0Wv'C9$p/rbj=&q&p@19y> 8A)WW5(B:kfW_uZȚ@5d_wqBdIp/i]lz p%het.⢢+#<$KB 1P8E/;%cwC%;ރ;ƂXDS8gz(<R p$&Y*.xip5wk<_c:|*z:;Pv&[0 p#0ಢp =c; sV]stkD`HLF#wNvB:)Ek3(E&s)iVvw1 l'EkYAf^"x(6{d6 а607*7ys7t[c?w ?54rYB+VÅ! ]y*PZ?F:2r p`ZV2g0g`8E8`WQ;*FDVnt{(X75dUɧw &t PNsT@bi64I%{T2\;s8V{uW34 XgPu@2:YZ8QRS >TWĚq{P(4 uh>Y1FPbfd057kFKJ s xU*Vmp OAj@= D 2U_j|@u5`[B>f," ?ΛhlA((2!o_ `mva*rSr({Ь{:##VF@ 0Jf#5 T4MTԸԲ>->Ӛpy9}wvu"|0%%,-,p%p|4AӴ^(HYΫ8<C( YDл(09+]j(3JSK:R(t9IO^Z_\ڦ/[F`p&(9DF`e~DġX_ bM+`+0tS0lx&sH(1p_a|8áM ݐP 501 *vp"MpFKiJY+4r-tڷaVO0o&s0tt 0èA Dzl9}+p'`bMJ$0?ΙSmSap ͶAP^F`\0A]^ARaHp \'P;`k<'hH4+Sɹ>wɌv8>h5 9 \(@68ƝE?#v۾;%Fo.}ay}4Wsy8ֈt7Jp ]6N8PS&/M1$,թ1>`'D0H'0^_ ](\6~aM70ȎN]NfZԤPF)1%8뺃΋CIh-tHY hlFOLYx1IP+x@, `5/ ([8`+X8QD D8UXluZ)E+Lj!+vPI)UT`ݼr1eڳ'uɳ]OAZԨPrgSQKbebZH%%ĖX!Y [pvJի~!CGA E Eg׈‰)GD&=:ңСU'Ɩ(P"rh*Eq Q.4He@r2Ӵ*u@mقEn;uG? ~|y㔾j=Kj{˷X\]6-DXjba唼. B 3l04蠂!:Ј$BD4G{$nI `cA,l<$"6ȣj)p#HzoJ\n{C{KOءGMXaguqlY%@H ah yo$oɐ_ a4! &Lpf<8^TF*F+VPDN< bAXX%Ȣ#ȑ$k53b,gQ3N'KRGS(x')D$5wP[ IO],X^"CeBaxՁM.K,pn8*Ԁf4Q,@"-]i *{(!F-FI,Wن totLNq)-KZ"*jJ[jYjV% \UUA '11W< !Dd%6̺r8ɉPVFb=aQƦYtzPJ*ŢY۔h;Ԧ&NۜK0$")Zuxx #rҹ>R'˺,ګl=r[AދIzP!0/ - 3:aaXSc8ZJ (9OH3R) m)9csYZqۄWTWX{ٗ2 <`YԸBYÊ?&=Ե܊3Lዋ@ڄE QÄ ]c`ԀqD9LP8$Jk G80Gh󿘨ӪÞvoz:[޴MM Nlz}[ޤzP Ty H~D4A0Ҋ %SX.M$L:8P8%$`S"O@AoZpόk&Lͱ- °'8-迗H30wysKڼЄ4Q}@NQQhK;Đx&%$p&\8 L>H+X,> ً ]iÈF Nd0`'N kbuv@M;(vMxQݎTz ȷ6(Xh09p$p.7".4 )Ot:vj뻄+ "S#8# 4 M$x ShL5lP9<DmzES崇MxQLvJH{T\0hK2UnMB*$ؔc  ] ґ\`+ºkmE`KX%0b5V#"4EOþS',l(  .L C?UJ:#ˉzxkQGQ|Q}ypkW{f8⢫Pz)A/qي۽U勡alK/. -"P\4,8;"Lk MXٳ֞%%m U'ٚ' #v\v 6]Aڍ]ۭoj#y JK4[ [R۪ 'NK0@M[RS) 3" @\ quBk.hTX"FDBۄ?-Cӹ8W@Ca+*K03K=i$9YU罤 WB8W2En3O#2SYALIE x -~s 5=lQP廋 '4N\b>^C_134*YT2_"xF "\CONߙuȡG L'hu!k s3,)cڛX7cce^e`f_]. 7c Թ9{܈و0P   XZeC4LZ \Evdv>\0% ^TdV0>`L9-tUiY.'$4klƆhjhjhkȆlji>i[l ָ5ѭT0 %OWp [Q(Y鴻[0C&j4/Xvngxfad' )X%@.]9[hhiB3s) uihkkhljNlgTU fQ\ERZ^0pXQ 0Gfg-"g#XU+jʽcc%&`!-l/5Pt%My]Dgl입YTipvTkonvDc 6=nEەi1% [I+Q! S?"0Hnm>+EXɪPЭ )Rf e80pƆΆ70L6x7TT`Uqhj^l*8ӹ6Po' .Dp_#ل[>6p%E3َھ!( 6!/,&(϶ҩ"e:=ry9ܔiȆj&φ6Sh0PTh<3 ` YWwieqNZο (Q9g( sَOYҊ$I#޽8w! [M8Ft6~k^ߖ6%F'd8{hk$\JS0-` Xu30 `7`3THl`rlpc.ǎ5 i1 <B A.T)I6Yka($JNŨUSuWqP.m!t4ԲhTSTPYpUzSP`07 WxjWgn K xzߗ oOqnX3l8pO_" gAK$R"δEh7qAk-3z(Z~+l؆mpuُ}۷ڟl}W'ȣdM~ {B0'ɻ hvL2\ [Evu l ỽ3X%nS }*х` "L0rgO'N^cq#;uٸmȒ$E RdH*CbKQ#y^p'ϞEpzk(F$B&JhU.etIK[EFs,Î=7d3fiyHT&dR2[yAꉰR%l"E`!4WtEav~m+bxVRQW \-6gz^EjQ :@ϳNRC?3_Nvsw..ͨ:\DPZE"WWUT;c UደמȘ"x^Xx aHT$0L-)4o-03 z2Yi8FD$1;D6U.r{#2 gHCJ uڃ: b}C"c _\B3YRՅg J.1(h"o#XXF0-F)u8,RKDoYMBs$܅ 1~N 3(-ԙT 6\B MLaCVP"%)K9De)oM(* h`'XbGN^{)0R)4SS8 C&X!dHCp\D i 6H+;5 Kl+ V2 UBMyrbLN& P` X)tJݏ~J34,gYdb^*\br#UM(jV_ìЇq%0Ie8 @y V: NVU'E>x@ PViY$QI -VO< x9dE02z V+`ۡXu(J>I| H׫5$tT#2A=dXɽ EŌ@݌k2v`| s3f.\q,P;aUS*+ھ*X8h m8C t!!; c8zq%w- ̤HNZIm{UD$P̵1}Y LepNZHPʺaA5 [x/ %MPT9i$AR̥YFuPGC !n_тLQ[U<߽9SUy镈WIdh\T@x"5<XLh11Ɇ PQU \/łQemuHɓHI'FT (bIvDnb'Vǔt"*~uFX=W_M@N':8O'ٌF]^<#0Wu!"Ah Z8~Vl̗r5=(UNRU1-S˨cX=SU]Smc%#((ɒF$ 9B%GbP p98aEaQ%yʢmN.X= RM^Ia^d]] ]Ҟ7P 1 e#ylmUَ܎=']5`5Ht1l5PC6PqR5\o:ofr&ooFtEb-BS$m\Iޡ=_HI1 1|L\L!W<`׺X|"03@!F< (LkM5/iF l ¤]rnn4dC6lf)(RCGL5(5,,g((6,3|[E^-!U!PUA'mHj!zהu%|yfcl`OhE@RvJ](PfFmA"Leep4Ip)hrC5( thr4T*q,p0ǐQ*XQ,BIzz=;,N]d|˷}Y5TVغ8qb L'⠣QEe $XnA_[b|+dbXCKh)7hG`C85i6dB*rR3t3 'r4,M>؂,e%#lՋ\_^zm!dɨ̉RmLI uh[)WT}\#P_k;r+Ǧ& !MU:iPbm%W6lxbCVI06*B*\@&htqf̂ fg**ks,/ۄFRiXdNHժ/*ScWM˘q6@A@H gL@Κ rXgHU]/ L-S/utYQDpFxI'hDjR*,8`,dv-6d*|)|r2MTŞjձ z%S&X(#a:SaSYTzW$WT~.3<d9S(P5KJ:/iVD`inQ؅"mj+2*H|bp/I`ҨJo&\om(K|,Glu4RZܦk!nK[Y\X! R٘A5<&F:VߤliP`4\Sj\]J[$n%ZEdB'k%HaA0+gBf@*tL676̭F"Sdo#i`)/UEE."V YfTF@UNҭd!Z ggةUW"cym6(eRifwAS0&Vx ܑwq6oY 2eU/|K"b7\Ј+Noe)F|xluⓐb[JMbr^ QQ[">6~cS͌I; ˻<)cuBfؗ8cكXOdqsKr]ۼaʟ&|' !Zr-hs Ltɗ pHuT>xGW (L5T>3@QH@7NHL@7)p1ω3EV~0a09kzD'3"!D2 UCV4EpcF9v\H!+{XhbI ]K;yl׳zhiRDP^bU5TNj r:կ^$0@P 2x0!!\. CGW (W)nv'V]2DK0}=+,֭ 7a$0# b S1iyr ˙Y=}ӽW3jԦq6mVm%z;7QJMBi_픈8Ίkb>0c/`3@ B`&1(*(K2+ :A$!҄M>+η?_+ 76W4u^GM&7 `c ڠ['<9o_X{ʉ,<"f94hCA$A!"+ [(d3(‹&+1{icJՅ.. B#iKhco2 6_1Pͨxo. ES r眑D'lh*;iaqC _D{_SLN>׆du!Pt@j%%dC B g̰B"u C&(M6N kXLY.I%Ta&ǤxoLCW`C*@j_&3@ǗI)\H)IaOH V-gYDeA dF$[@I-kC\)jEkDu"%Ԑ&0!,dlvkzNIAl;l>Ud0cyИF5<~Q>PZ2ƐXf4Z'ZȈT l5\m(dN3M L?IԬe&HM&IYBWBc$\+jL@,n͕?G8ar"]_:1boh[5AYlDUYC׀exmjψQ2ZI 3]^ ItaqKe"%x5sjc2L,I($%bd\عyY5a#І)@ mcFN_qFJC~*@kҴAj+ Mp !x<@:KNRt9ÃjBN3(r$[b+|S58! Pxu˄\bjtmD0l]+W$Pƪm`#l[PMtO ELtbYzqTp]*|1`hϕ it4QrK1d#ا2Y z9]HTRĪhD54ƺTM?b!P5Q =(b73ˬf@'eC6Bus:$yZ ri4x4\AbUU@:\oxiyllP:Դuv ըYvmbq+RЌTwgM{Y;F+Ip{hlKquY?nM=ԉk?=[p}͏9@ôlޱT1d# 2F>$pBk9F-lI$0)Pg50-8p90(9cn H&h$dN jc@*Vdil%dDDgGD`)LTXl/Z,Z B3–?X8!GCbaALʁP$ȁ&0GŠơ$hnrYڠl"* dkK60d'Xl1vC B܀*~ԩ!p.0BU0D.*D`AO @ʫUq1&>"qi-rVεflU*".B18,<(0@D }ĭL kiMDJt$! PԦߊ$N%S$!%YR%U%Q2&C%9fD+ 11 0~Hv*2htHJ 14d Ph戸jΉA Gn ݱ6팦|p"ﭐHpgD>h,B 7NlE_0S00<,jUq /[řhJj$U\eHDZv`%j (.rL8ej؊v.6. a Ng2 N#14ZL4bAXLahAXa&3;a0$ 3MD>pd:. I.3:MmZ( /& JD Q*:ϊPLh22 4mrKkáC.NdE1jj89,A 4a:k;cTFZ0C o-*p0Ӊ G0IEFn()$PyM .HS* ?6wq+%Hfv6c8RT4B.k\a!BLrFUPla̿b(u.}zvH&! c>mD/LD,&A@T (VcI~ta"FYBNvʶ\PVJHjA@#:A%SPU0 p> oH|6oM-TFC; BJ(a [ YlTJ5*xclG4ʬb32[ZQL\O_E%s&$!jfogsVgwvg Tt8q/V/0$(/Vi6ig6/rhqXjVEkljªi/RVܶV-Φm|juu8l+ jyp pV02;YVrPr77s5s7sEAt9pL`Uwu0$VwsumWtM9:tovWtYww/WvɡuWyavQwdrw~Ws!|;{5kV2:X%ͤ:`vr%`A~W~R&}ei2 &}7&׀ xX&[8]҂73'-8sx=Xa9.K]w3WPy8FsW؈'ׇ$_b;bB&n&]`P7cFe;XϘ0M;x;h;܄;;3FOس؏x;؄Ma<T! PlAQFY;SRf]OYW98g> ڋ2 ڋ jm`R6yX$aX`!W|^k4WYY%ӂρ!̀L@  ̠m  $X4alB6a`$D"OLY9AYfA;;ACZa΍͡:&itӛyءyQY[yaՆڨW Y[xc%Zr{ZYڕ$Ϧ$Maш :[7zݚښ10mxCxگSE95#QX88[cU<;QךOA*q*$S8Ec۵e-d!$6{q@&[[[;󼻸[3O4@ͼ{#&A߻m{ى[;۸;9! |{*[;{[ߡ !.C\N>\ſ.Q<aG.Sсĵ!BaMӡ@P6@?:('C6PBŊ+W@uժX`JUVB JٳW̦E;vضU+En[l Wذ{w_s΍*RSJ+W~BfZ\wܙ<`mk9t;@gmP}倃&(9R iJhB(?Fjuah:*n8$=@Y_rꠢbz, Qw4(!`'Nf'zDA)$)NܯKCV<4 l(k',F :1C j$h,*@H+Olqň,n//þ*0tA˻ ʛo"mBɸA )e!g!-]f?zbPǡ.L0"{&p:O;񐏼'O[5ˁw<MgF x@&8Ss{ۇb=u/O>{>__{k?—>Ok_|_(\ׯb 0 XhHЀ hpp  0#( ؁('-3X:&x8:H2HB )x?xDXNH؄PH z bXfxhg8j؆npr؆l8vxxbXz؇w d88Ȇ H؉bȉ(XH f ؉l艙(Xp؈،؋chHȌeȊb( \@ ( 0ȅ8u荧HH֘{)8 Yĸ\8Ȇ() idȇ؍ވ+ِhHcx Ѕ\X{HXYXg{((X$H؊$ɇؒhC"?I t)c`ydXY눐uȏJNɏ P F y49W)x8H Hٌb(s9xըsi]-pI|8ɘ yh1ٕie8p)g(XY4 ٘YJ)HWوɈiX' aI0 ) `99 eh܉sih艛i `3 ָ4eɢhiyiŹu()ɘ99uBi̘ؗ9X= 9 i_ Ɋ ٍwڕkّ湔_:g,ٗ 9(z))ZY]j)*9ʝؔW*䈣 he p9}J9 85 ԙjZy8ĉ-ډXYڔ)ŹىvJqۨmZiO^$ A Ny"rI+ Ě)0Pi)돩 !%o9ҹ)Yjˌ$; Ia)Vz8)ɫY* ٳy:Z4)cY ɧȄ>& 'K &K ж o뎳0 m{`> [ @ I cxt tP [#7Vziq98j[+uCɓ 92yM{8٘W};ʲ??`)P?PA*@? tAZ %Z 2}@ *D2J)@0/C Z Q -ٗJd*ȌRI Z إ7{zzܨ 8jءəhH+Z@$Z ~@Z 0 ZdP@r0Ř`C Zp S %sQ/  `kxpy`n| 1z䪘80l!܋hIꝓLIU\u$)HGgk Ȣ@/ Z0Z n{̧Ǚ,b M,ȩ'ČǜC ?ǧ `lZ G@ ù=,ؤɴi !็xiiWɝ:- Jϰ1 ꠫0m맜PXқ|~@tPdS<SLW| ( Z0A||@ )/@) *i`yw`D p E 1L^zhو#]q >`ٗ ?p }pCȬ !+ k0 @A`w >p }۝} >@ڢp@ ifiZX*ݸʻɚGk٭<ۥْ ILB ɟl)nؤȼdX{& {{~  i~Ё:=΁W n> ^{ |\ :z︥C[̢ږ)хݦu z-Y9ТۭLoڮ JʚX ò ZrڲX3~i ˬi.^>,ZuWI 벗 X,ɸp٬>[&Z–H%,UL06u|~t奀c |8X.xA~~%x8 8žn^S>ў~No$M֜AX~ E^.J~ƾNO9ٮEi΁-9% / >zk _%_> 0!? -$O1o(6O+# E?9"5//%5T@ML/?$D82UHkVÉaZ*UPࡔ+N2be2bĊ[*|R`Li,x'A{JtϝDq¨*N@zpEJVNڙUV[ev P`|9qٲYmTiӨMO<6ٳ9,:8nxÉ˹ӊa&đ%7,yqeuC}GVaҟ)F|4Ҳ4te%{9в<=խ_>fzuV/|=;w?0rnVCᆊU'W/fj ?di-VH#6 /t0VV- 3|+OD5JO-2sPBgD'ܰEw$M?We#20I%Zҧ7k72dEUVRǃ2L+Q+\E3BRJ 3<D+?KP T efI-$BE-$MBajS4D)4Ŏ0RNe r7QA刨Y^\eM:SP UYoGf6#ND+ Iš<Պ 2\ۈB!B,Bao-8T;PV:ςË#Q%WfEipPBd .N 7|KK%VQ{Q*P:mK&D/xㄕcLo@XX;(dT#t#`Vf#!p8jqW$<ŋ0DA\!Ń<B/(Ȩ H: (fڃ9DZnŇVxQ%lpKң?W!}8 mABsS\Z ?x@Ё-lB@+ ܻ#͵~\QP$⊈\AN! (P.PPGd* }#" dОpu"*( 1>l/q UpYYPLp;@ f-0қ>Tz=(?/%+n(ʑⅴc@2[h8q C%fD%V`B d8ȄDMcSb7Y`Y, DEj" cuS-YDE+;Yp.,E\ 5B5Օz3 SEU%46 ):r6BFc5+o"D+C! M([˪MȔĹ]U)⊳zRBts6c|0_ݼjuH5&&Z΁sY^*(C:U%.x~ P"yI]d=:g~*#? Aկ$SX@JSVЄɤzeL݇LPt3v}M! {#RR!c]fNUKsSI277`B uޒjZvȴų^ >CRiT![n|w$-r/}}%[fd1 5)*Uq'!qIb*#َ鲿` =ySt^f{"u(= Tm&+))z+/kT+%{,]#@ >3l/%p13X2(ٗ<+1~)-$bJ5̿Y"[ R6SC偔I?P/D>Y?X1PNx!?ĩ[P"";Y)%0L)?,:Y9[ "5;[TP4>Y,'Z9X*X91 0" #23y 2 £i)cYzq%F!j u$.kDz@ ? CYЂ0L0#2Z>#2p螄di? hET@FXg?002)2>q:2*˰lĕȳ|4DbVK , *С0AA[9@93Lp/ ЄOHXYТ U%K' +N2`X*6a$S!/"Gy= 0)yBΜtT{@ؓ!7C64 78Y؞ ̃<@2,MЂP,+ )M/"E"b$b,jØ?н+CDM-!2q =-Є<-LXDnLzl%ρ Ǩ2 {@ɮ T/P‫X XY//PОzC*A^?{L!S ӟ0?l$>ڙ܄b1Px**?,F3S9 57T Lq#pr 0CϬoԄ=-dٍ=% /x]2,~73.V>4W&9.ؚARz(9Yx49+1^ÈNНRW(9P&!TpT3!6孃 Jd1 .5(/> 3rKXQ +l>X?SFzMWHYNX?ЄqCQ@N1ʪ΃yCuU/HdZHP0XAP6_|>űQd +L) 0R"`@cTɢ-Z'1iʂ!#"&W0Z9% 2`PȄr.`Zϼ2YV>Z(R2hq܂@guv&8 ?sf,؂9ym\D%Qߘ[z#ȐͰf"UpiPFxɂH Y;#1`H$t{t¡($S, {V-We JUp1Y1%9n?aѬJS.M"1~f H?X&/>*" PiZEX1`iz*I0LƹN"3 :JUXy҂lv!8&:SǙx9M!l'aE=[zٷW. fbv/MY:iLHazYY(24Ĥ>KA@E@؂yPNP-<:؛`ٽV1*aC8_ŏf U2]_..cP8R.VQ6 n:k  y&| U/ʹ* "O hx;N- lQ: v ƜLns+ܓeČ;]*5+ ]-SKY<ڳچ:Gk_Lb8a4tY*0YaRXUM"hX^R{]0ӯGz6]CMiQdG>1 SBQXUW5,ULQl顔+c/F]l-xBi eVؽfU-ۢ2*ӦBS4lUk{Ōk\UtզXiwPy4rTTUܲ#rDq3eYROCŮtԥu>e̖!TTeT)5e5,3 IR^B_oןH¹ &E҇&|(B&JaI'GaG'.摣F (u"*mxP^IQv~YVR[uB *~ء(02&[l*hr tjpb`UuymT<,`ң&AA"xЉd^pG\Da,pdA"!l! aLba9+"pJIrTB%5 "t/uҩ+($' BtG& Ig~[U+9S_3ʤRpeT)^GAb@mb!Ȣx">A+B2s("b81˨m" ZI&\ZP ϢP (wh, F[ܑIP&1ƿ"}U.S[1!$If[ ͵Bb"#x!,<Ϣv`T:-"P™&a !,PGZ~'K=4*؜B9yatGI[!lBi-yKqT6YBOvqE*.%#N!/̂?A(,`Z0*S`˃?B0a^U,"*#v\C»ą:p\j-n| ,:!Բ0# ='6A.D,q? =1ZL.q(mH F"٠{nsIYdqs ʒ,.OHM#)=rIE*SUtl9Y@%$,YpME(,snM)S&SbBLP+X8Aea=cʉ_tD5 \ JQ(K|LdLOA} Yrn3* $ ErjR&H{G()% ~f̦M КfӐ;,-wgStQ(l!ɩ 6{Ci,8يSTb0T(X_VLfumjTJХr LYT]0FsdkTV}R B,<*F& 2II$Xs!ְ͒vJ.TB75hi2RsBgKª3U`Ɖ9 FFfzt].s\rx:A0Qn`%\I6=oONjф;n GfI/]I^ow%TTSb9 ÐYǎ~0}˕$ڮkme0C,⟜v?;+"xd$sdO`֑p98"+sK& )SV0:i `ҀbiN$Yy(@*Bt©*FȂL0 !*t^󱌋tNAшBA 9&L@hlB2}GexTp_hO)q cRt䌎O؁$t@n\Sx4G6-NLd5P1bwp}yذmKx@a,ЁK( &d,,;_'A0_F+e(&DP&`A]!eD((k܁@0}Ot&@qf<3m?eѰņ!KMxy+t)eH-›9OG'RIJ2'q-iB"E!%4jtN:J:AKABa `\.B4)T4%xtge8U"Vk-c陎(S0xDfQcZ曾Eщ=qmP ]؍ J[D"Iõh~hbJWǚbQWt%ԩ¤N5:O eΝ͛].[fhD[][UXyUYYTZU\qUWqR\~*O VU\13]JզjZ^`[xMv^*Ձ=ĥJ]Vv]Z5Q@ m-5-]M-:%uZe-qeAan2֦BmOpũ֯2D:]}C@Xm%D%D[D^[Q}@ X*[A(mCNj%j_ḾDkݖeA\KYEO [X`ViRQHFlJB Ukǥ+`B B+,P[nM]q/Qw}Mi(d&p~XSxE%FdD`*hQ)(ͪIH/V}(AӹB(0Pgd]E+<@t lxlmx`P8S -**d@l7ᑫBBg&u*9*EItN'fqBvZz{(Cn4rL6je-'.yV,,p2g.'rܧ['v﹅Χ}҈M5MMFoF$qRQtT\:l GTT':*HvG<֩XSg]Emnkz1,@vC!m"gQS\v!7ܭGZΜ+3F;=Vn%rj=PL@k-%/e!rMvK\om o§!VX\.s}[[=YjT%HD*Vjz<^Ϥ\Ix*\|Z˧;W-n\GEppk+kUj6Ixۻ [*;^ƺ\]hn8>pgmV}/Vpm^w1Hj=z}:/VI(mE]d==֓.TӻVޏ Qr`+SAN/˫SЅUK+=Gu6&~E㳾ƙT ^1"o {E+ѻӹ絛w.I\)`+ƛSof\w1r>JV[+V{SK`Sb=~ۋ@̒jUVqJʃW8r%*Y0f5+VFA9rH'iDeK/aƔ SL5ARH1tUxiR:}zsjNRziKfUtDUz*UģG fWTCJ rnV~?b-7/`ÅExTWmO!D%aS KaWa͂KhӧGJ5-ԧE=zegϵEU)U5nެnNv̾x? RFa={쎴cg;uH_n>{zGH"9po,3xPl9Qe"4+ I&d#D /P 7C 5CK@tfhCq3S$ jȕxگl>S%Xi#H2I6CI(T&+TrGyA3@%B3'hdVDHsl7d1(8Q^")Q)I0ݾ+;6UW{UWcVGزK|\T0,FNjʭWU9sGR]Ri`F&yu Vo 7rusvEuMIFW6 WJPz Wn-Wo_ub%mKj#xccߣzjcjdҗ n#F" &" warY\\zŐM(nT9,#Xd[ǭBFh`=7`BO&6&˭]RڏJj5u3:ƚqyU yIP@&4X c9atBݢ1:L M2gqZ^mih(:& H煕g+ګTRԃ}#FWN%'RmD]ܵsl Bd ޷6;ńRpID!'CVaM)ZZfn{xç" j&AiBaXL'j6JH~ ;U_@bֆ(D iqBMҜ?Ax|[848(o` , Q6 4dC UCDp"GTv|D/әt0 _v g7 x#4-풗-'9/~DQH (3jVиLŲv%}D*8IqCM $pv\^l\; 5AЁ2_96YTʾGHK>36eJW}I'eh&ҌYiif4&=ٔԊs%&GYN9«EP uP"AʉuMN TSAE*6b 'lFխ)VBxk^-ܵZ׹ծB[YkIFg1Z c1iQZծu-EZZٖf%OXv4= oi[ըHmk]npe,D8x[>&UfNr;w^׼.z+^nMz^~כNB񆗿ͯ{[N8ep{1,aFؿm+^׼E>4-vbKF1m|cX;Ya@v{$'K 'ydiY#bfd@aU#ZHdfhh2n7#ɔiqb y?e U1F=Yl>YlЛlӂy{sfc}PctI}:eE2Ύtt#0oQ,Z;ŀ1 _Ϗ6U'ml9:e̖;` n͍Fki[7ʨ&h1LFghOo;ڒֵ5WO{e7z #7}c<:4Fn;[,onuhO->Aٰp/9I]4'0*J׻ҭ1ABz~V]A7:҉u@NV b/*u(]GJӱ~ֱWQ{ǾKg? s}zL{nxD2 l(>!p ='Bqԟ޳'Sף^q{=gמ7}aϾ_?{__P ~~3>{O 'RA2ְbc/dЄ Jƪb"+P"("=-FB O5>oD!"IPpJA[-XHW!'k!Q=p0Do_5P kPЄ)@! ^--}Ф\a_1!^!('q10C?!0G1=SQqKq%q!6QOQ1q+1i15gQm1q (!1q11qՂq!QDZ=ñ11Q; o;itksnap/ProgramData/HTMLHelp/Artwork/ttTipsResample1.gif0000644000076500000240000001427710534177573022532 0ustar paulystaffGIF89a1B91BBBZRJZRJZZRkcRkkZssc{sk{s{{ƭƽενƭ{έƭέεֵ޵ֵ޽ƽƭƭενֵ,H*\ȰÇޠ1"ŋ3bܨ#ǏC)$ɓ\i%˗.c„)P͙7s)&ϟ:Zό7dTTJiҥPF}**իNNj+֭`zb hMlǷRݺ+fĻnދx7`õD"K@%ʀ!G嵠6F)&q73^3&A]=k!MNcW,RXWrmf"m֔7kz; kClG366ϒUߥ|95{|?|Ƒ{(15ag~؀Q8!b xF &6Q()w8`mRH18Qz "|̩F}D{ry&d^Du͡Y1^)$i9hAsըɹe}&8%F%dҖչE}(R·()49g^oyGι蟓:楣:ێԺ .p@/N g8 -n;G/Wogsƻ2ET PA/o/A^`L? [ϗ>̠7z GH:Kb@@ 0 gHρQ< %H!H<%:P|xe` CD v/Dx b,A3,F5z1%,}.D"M`F0~q4#XD!8HIZr&#YI WL-e, U:+hDWQc*xK^c/hKJRRUD C+Yb.2c&M;ld5yG_2&7'ID=ʎe\mE62f1[yCf8#K[ '#j/&͆sD7xNls 5ũ| S>;YDw~] 4D1Zm&tiGyHrӴCg RDjJJfsA%8jS"ԫ(M e*l& yJmsIUdYCZ~&RݴQHֻNS >xҙ1yrԢk`}:F>Um1?s\L3Y.=Njѹ^Te]T:5L&O6m'uKZXX.V-+fa[\Ը1֩|; ׃-ox9d|ٛ=psa׾}p;`{` Vp` #v !߫0-\ C 1E b8(._C*4@ >xca7%;PVr,*SOr<-{]@{%XN3d5Jvs|7yws72L&2,zv, t hDъ~'hJKҘ #kYgsiRyF5W OSM]MpeFWҚv?~f\qa?e66'<ߺ>9MN_\MKrhifl׹hNK-Q$g,Aҗt@kt6=8xգJ=W&2 vNd~ݮl\7ȿᄈMc+|^g]|il 遷@p3,hѿ } HгwIznecA6={ګo +_ ~nZ/|r;OOWϣ>9}>n~Jeyyygvglg} H7wGe}ɗ|qvn&r`|}Wz|'}.7׀,WlBtwqF~q~dwv}GVv{NVyr/1@exe{wlvKwhƄh$u:duw{Gx [nږxFkRnsv_&~6;H;kn vu}ȆL{|'^8jk'x?wh`]WwsWhjv؇ux~gg/:HF&[pyHg؆BFfHod(u]x؈Әvu䋺gXm۸\&6riGhCi}gv:GrȋV؍gFZd)kۆrx]XɎmɌؑ 9p YJjomY8)Hc(iW.ّqw'B GVQG?iԈriRqȕJO9p>u_ hJVȕIcs(ޖ3G9k`ٌTV{h 9h$Iv[gǔ9pٚɚyeo-雽雿 Y9yʼnǩٜIIrod'9"9Yi繞ٞ #p#7G9(::'P Z *kxY' ڡ ":Z(Т/0:2Z1z46<>@ڣAv|ȑ-JKML5PRjSzP\&Jj+9@(,kgʢnhlr:wqʦntz uju hʧ_l;jnʦa{m|  tꩧ ZjaꙄVz""ঽʫ iznjҊګ`*՚ڪ0h趏qʫzګ : *J*j :J ꫾j_y Y<9p*ZתZ;ڱÚ:)jwJj犭花:ʯ ʱگڴ+.ˌ88.7˭ƺڵ_+Ld;g C[sitA[ڮ"i˴(jc[۶ /`Y|gje+$[k+x{O hCXp$Z{ꮸKШ̸YJ.[˹{ ڻ/[q[ Y{֛+̛'5Ud⹽{曾["ШSimܙoÛ9{ۿۼVD#p <\Y`,-12 6\8L:<<|;=A3dlxNPRTO[:KN) PVg+5{ٺZۤ Z0="0=" =^<#H}n{2Mӄغ-՚M J>0 P1 E޸/RMDF< nm<Ha,N< @tNv^;<\莮>,4 ˼-gEڝ^| <';rh| l븾^#Pwذ',q~sXr?,@>^C|Nlf -ՋMXN-Igm^ƙxmߕܖ7nuBS05Oo_/X "!O'?_0QM5'520?506/9:O=DFZSBE>/P2.@0V?2Wc ^`O0]d_fW?YB8-c?Wp[otow0O/~}/ 30O/o/_<?_/m,"#/%o¯ȏ̹$ood{HJ)ڿ?_*/?_OUQ(7h$8`A&DH5NdHbE5fcLJE~$9dI'+PcޘYM8i괙Ο=Zу,#dSNNZUYnWX;itksnap/ProgramData/HTMLHelp/Artwork/ttTipsResample2.gif0000644000076500000240000004333610534177573022531 0ustar paulystaffGIF89a 8:4JF7JJFVS>RRJZRJZZJZ!1sJRR4LsZRRRgcc]DccJkcJkkJZZRgcRebZcpnup]|exusypv-5O)R5FDW-ZAZ6FJJRRZZZ])Z+iIme{~z͘քގ˔BBJJJRRRZZZZZZccccckkksccchkksswss{!k!!)k)s1{1{)9s95=GLZR^^eq皳댵ƵƵýɫƽννýƵƽƹενενֵֵֽֽҭ֥ƭƭέέֵ޽֥ޭᵽޭ޽޽޽, _#I,Xtð!-#B("ŋ3bܨ0[<)2dȑ'M$reJ*[ʌI&H89s-Ͳ%Q-I, 'T|Skju׮` KvٲhVk[l㺕 w2ZZջ߼;Äj2b $ `'SMZtѧMNMհQV-6ۭg綽k޿}% (ďSq̟'|ϯKΕB "瑰PB BPgS@ļ(h& 6^,T…)da‡!Thg'a" *"/24b)c@(dDidHY!>6KF餔PNieXReB_v!"@}o&gw)tg|g J"h2Ya:*eNj)J馚`!*%by& paj7Xx*8넴YF( tZ|-9"oZ?)n㦋+B~)o $!P7px Эu׌ q2b`vތ{m)Z<(򋳎m.֙6#8߬9| z/z(M H,݆~,vG paiX8 *}Fy Gwd5dvНD~Q;5! {d:N㍧K;oy.z衿% !`6 Lŀ4,S<5p#H1 (yhs7L1qC:w0PO 6{G(/S5wx0ðDH,!;FCx/4`@ |`!Ђ 7868a)@TB  ,Cʸ2A e@5q9D80 0.0;Dcl}2~'P xӟ?IP-(B9І޳4& TԢ@@AT(WЇ2H>@n@)xOPR/min@93~`$F}A@p,#)}CqJ>' R8Nq:ְ> g͎YǭYZW5mMrJV)0~_:&5 ibKX:UaEgyDC D) 'PP0( O8gd*5 T`[,…B[B L| \A1pq xK^)5oz+^hy:_vվo|j^ n~_wjf7Y&G0h5ʎF Tk`/5 [%/8$xx*p Q7'v/͋b%o^$WA)Vd)esM``KԴ_a &VL:s3 +sxά;BІN4h:8~KHCҕ%mLhAKZ .]TTPV{BYdXֵ6a[W5kYغvue+vv fSS>mm4q;ϭnu.^N 4(<ȷ}NO;?(^BAjQOE,rA (?S򕻼0cNw7Dwr@utFg;6y,h TE,P̠x_{ ?7_'~}q{|ܣϽu1ȇO?O_?~~hxW y` 8Xx8 X~ Ё$X&x(*y'+284Xp~6؃>@H80FxH:XJhP` 7 ` P90X8P  S(p xD(P <p PPp p p   ȃ7  p 0 p  ~9P|px 8 P `p 귍DŽ/`         `P g)  0Pp ')`@ В  ) 8 ~C8U @` 8@@`  ly`PrI@ P p ri  9  p S90ٔ   @@  Y~H X`  e@` @xw@ Pi  $981h`0֘PMyJ(zX@,~ @z@  1y BqiPp 퀘 0`i0 `$P@ 戢ꗜ(W   Pp P jP 0`)Pժ~0` Ҋ ```8p0 𐖊/~  P ``z   (~`dg( @`=~ H\! p [ AKv@ЯujI)9N@ )+t8mKzB8 X{ȷx0ʸ>H[+w{۹HK {Kۺ碛뺲۹;˹`h~0&H c~Ѕ{7 P { K0 ˨닝뫿Ƌz~`@8` 9@@ ~ 8`p|8p(+ +~ < ‚PKм8@ {)\»@00KZܼ4ܿ@P~J5<ĭ׼HL  LL<ł $|pp+ƅ@0 ͋ū xK B8Pع p` 889`h˦| |Zl c(r< Ȍ|ʢ{ pL`꬀E \ϝ P<-mϝ =  ]П mМ mҞ,M#m--.=<&-G'I]M0M0 ] qZqbqd]u`h gim-r&cn=o#qmrUlqym|w-yv-rg}և׋Mjց]؅=ى]ٗ_]uc[ `z70`ڨ}ڥ}ڬڨڮڲڴڰڱ-ڳۺ۳}۽۾=ܼ-M}M` (K(ם֭؍ M-ލm--}]m߽ Z  >^~ > PZ}0234~658:@BDE>FHGJNթ,]S;YW []bdefh`mcqgv>w^xzr 0ZW]S芾X~Z.鍞臾咮`\ꓞn>.띾걾 ~>왾뵞-(WY .@@>0 ..N~~./ S>ނ* & }ؚٖ.잎_Nb]כٚ ?ٖ߮MbM_}~貮=O. VdN[ p`b?d:i?[?Pex^..>0yco=KX:_ ˾~\N@zgzOxNh NKo` z/}bMo찯/[o{߳8_;xVn@ط}z 2> O_rO ϞA\<_~s'VHq#^"YQHOz(ˆ"tiݻv}exƓ} |IZW6H4b\*$[3K'E;9Z̸/)v+gi&ĉX1V.GYL+re;\ Zc NyR7{bEBn{u t΃y: %Ӽ r1P2 tʸCz C` 0Cwͣ"e \_x@KLZ94*@*ʲSM^tGz% (łP  2 U 2G\5:b,6L2 2)=p1Z^8 04)"]5+6H(dcL@' ì'σX4 ,rH G@R-BM67Ag"8'BQ&;rU/vtj˺2Y$0H MqUl ) Ae 0ErAƣ)B>h %uJ¦(1k BԴWr@mcE]e4e&.1K\=`-i?E&_%{ฃ{6lG\xx&qcyЂ8bgi&4 #y@9(ɭckۣr1U,b! $ II3)p60yR0S2aW*I,4-CCNW`щOu|Mw*B|b<^2k9rv EY&*s1^AKv Ŝab*Edh&Yv{]= T;L5#G6rLC =yזm/]n"eO*.[cﲳS-neyNEAUEDIF5 oځuܷ'~;,$IH0"apx Lo%03015~0qrb8-w;Noz[7ov;>n/zaq_89N83o-r| mr8wyaNs;9sN=9o;G]Szխ~ug][z׽u]V~v]kg{ >s{~wk]{wq x'ex;M|-ȁ[c9<!|@Ts?%p J,||>`xP-Jg]>-ZA{;< UH>TA޻Ttx|ЀTIUH x h @|DāDȁ  sЀ[JAhFpAK@ BPs@'ĻɃ p|H#.̇ pˁ=K hB{ B܃, 3 %[؀( AK@+ DxW{BL;̓8 ܣ[? /1I|0 hD3X= {WLD8AAD @KEs0 xAp|@@xXLFKܸ XEWqjE z?DT pY=>t변?p`Dp@ U8F| A 8=sGK(CA%V@lH:}@xĤ(I\I<;mkZXOIIXжɟIIOʢܶJDJɣLʟɧITʡɨJJJJʦKʳʝJJ\K$K|KDKIX:m|xMN6o L$LDL.+^_Uer#ϊvޥc.D17$fP/4kuVVyޚ/.`Ypu^u_G3yb&7CȄ^__u*&=/+ZJm`׶th)M@ opGz 9A& 0DNTNw|~w8O%6:s/wuMGNOR\xuumS%/>>Wm(,`*.h_yzػ%mwWXUX!oqԪAW Q{t覎"jZ97֑Wrq2_Q,3vl-Mm$uV_.=>yٓՅR]پ{{Zsb*/Y|ٻOٿWY]r\Un^\o|/|5ٟw,o. j#^Ȁ:}}}}b^ڋvo~w7I2V}~:NL}'%,tFWɘ-G Fot)s%._|z7p@k!Ĉw/F(kG׹{$H_ yy ZR($g^ܛh(|=u/K 왐dՁz0*b{ו`.P5 TA=ʕñz).ɫ@dzFe O7 >lRk«*CD}8,Pa^&UrTIS{^G۶A N;,5XNצlBuq!Rt97,[iukWS}N_&^|5IÃ}E!:X!ր飋8%u~"m5A*Ba>@`CP(Np-j=~d]87^A"!\ă ;(xš` b%-iDY[fPӏ>)v8q- J/8Jlmuݖ$>#劺P!A^܆aw=8@ "@"XQs_%tT͘!/XLTߖZEO< -i+d2ѵߚehP/"DxweUe")vH"X%8A"c?z?%b ץeXFG汓UƔȁ覵$z9AjU;m}sCÏh`qeu%/}1~$O=2A+l@"Q2e+I]NufR4-}[稞OU5=SR>%T˟ln!Vէ3ns6$>ծ($}iYa(^-@YE!8jF2Fb,Îa0++ b)S=E[M8eN/+c CR.G~ Dieo1胓fU6 AP+wXȀ@(B |@6H<g+4( ҐBCz@63ZSyvғq*!8D&Q2NHF8BM'Mxe,92[ٌ\fq412mT#G8#qGfK>^lKҘ,2tI({1|59@%q> P%(QyGI&ϑqu2D(ÀȒP/Ab N"l#4/&2IL_Rئ7a6sf5K2L43&IcS=΄' L_IPl3iNt|f9 efaf1w8{F6`C:At(Jeғ-5iIcR2iJ] ӛꔦ7N]ә,SjKOM}S թJTUխj\W ֱd=YӺwc♚jxu+V8@C9E-^[O-mcZmlkp[徶mre\}nm].׸ŮumfqnMv[L͈EbQfȅ.`^Xp \D .pKp4}hRю~4\h/g4,|lHD*VD#Ҧ>$C[a A#!J#@!I:XQZ%}B`d! mׂE>YX>w~#h+dWxU"<;QDhƶ+`{ "/-UsI.D` t<ҪI1b ``H40|l@'qw8x>.Ўb?9=4Pkp эN'R{x"As\؅tz}hE{|(5! LU!j}^VJcg<Ѐ% ;Ѐq  清8N[ T,}E ?a6vk^S\^w B>aw_<3X??/??.dZF!т&h'06 >6JV`f nv ~  ~&ԂqMKpB/CE#]1 Rݕ R'#`Q5 .!Fa^!:`aaj&!`n!!>^a f~a!!ʡ!>C-dXSJHDžDH"h"V0 !B"J"NCYbT""n#6"x&RݨӦ)"&zd(^*",+b+"('v E-^bPU'X 2.X2"1:2c3>#3:c54f5X4.ca#7#5J4Z2c368v#4;:j#TF%UNUd{pQm PWT\YRͦ#ee@KZz @ P8gz''z'{{}'}Ƨ{|~槀z꧁g}&h}ΧgB(6>h{ҧz'y쥳fVGhR%uEmfvE/T䏎(md &,ƙn]O~}XEZzap %[^҉.HMy aKNWD0Ri՜\N̈?<̉@D^è:%hKCX<CD *liE"ë:DjC)ʇ&wbfHc(fXf&ͩ(9%-?:LCl98J"?%˧ ɀ"B<߉C"kC%*$Bg+]C*EXH< %8$v*?/ϯHk \xN*ZfwlZ:%V$j: , m;յx>"X[ol@?ԛI@68p-?[:lL̻T,x-%h@jjHʃ@&kG)`갖 ǡ2Ec|`RPԆ8*<l9ې9 ȵkDLRBҰ€n/l@n ?M?H8%-\,=n-D&ndgRgn-ŬFb K[;qb\%|*?@(ւÀz2KҲ 88@N>T >X*^i&*ݦY*{hhijD-h@-@*yie]X!nŲDe:kbiP?Nխl@K !,ʰ%|:8hx;{>* !ǨˢfOHL.vʇ:bZD\sFD+`#\t%\E?D.چ, DݘsRjNk%je/|@/CTtSM4OO4PtPP5QP'Q+5R/.`@TGuTk @l3R!1@12ЈxJU=8am!\ѵ ڵ=u\\u^]v_`C] ^^uc3b;vdCa}`[%BC_%Ux@Y*GzG,)/wV%m_m6T~nk[4H̒\.YObp|>4 to9cwtgHWw zv7'ZZJkM:>6AHE(䈢z>'ZNeV~v&}2#8M&8~/_ 9[Ǧ&f&MJ[Ba'MPݤS8Ol)n=1DE4=}68}s?%n>0 yoA%W99k:A939O9/Tk9E%YR2S5Ӕ80TXkQ JTPO9׹P՟zNT#:THuNfNpZe:Zmw::GU;V2\=G::Ϻ:ߺ:ﺯ:deB-(;/7;?G;OW;_g;ow;;W'[F l)Ö;ϻ;﻾{{< ޏ*$$@B>KKS~>)}J|")~链飾韂~~>)(B~~)(ݟ׉>$(;C??GO[K$",_c((_e}+B)??( H"h`B2TbVD +&D#DžxeJ+Yt ZeeN;y惧HT)RB5Z :.lHVE*!JTBZF)H6ڱCǖePkSXq{g̙@l0˟O*|aÉ />`C)nx qcĊ7kQ9&!),|aX?t邊L5o6Bnz).haƦ+&yOkP·ݾ(^J9zCkoR C!:!ڃth=+<$%7u<' ;;e9 RA-" q'K ( 6`2S.xP2/$e21dqFN{<SNOSLAlE?\TO?$iQNNJZT?ZRJZ)9sZZJBRRJRRLZbc]DccJkcJkkJZZRgcRebZcpnxs]{xl}yuvpu,5O)R5FDW-ZAZ6FJJRRZZZ])Z+iKlf}}{ˎքֈږBBJJJRRRZZZZZZccccckkksccchkksswss{!k!!)k)s1{1{)9s95=GLZR^^eq皳댵ƵƵýɫƽννƽƹƹενενֵֵֽֽҭ֥ƭƭέέֵ޽֥ޭᵽޭ޽޽޽, _",XtCD("ŋ3bܨ0[H)2dȑ'M$reJ*[ʌI&H89sElْh,ZQ@O|2L5fh׮` KvٲhϪ*-۷^ƅUn]^fջ߼;Ä.cƌUb $ P'S%ѦEiר_ˎM[l۵Y֝6߼NJ*ǡ G>xQ/W\ΩPg}{Эk_y$HG‚= ,B,!S(h& 6^,T…)da‡!TxP|,b+آ,(#.c18c<4)dDidHd?6I>)eTBiWVZZa)t!"@N obAyy)')hI&袊2Ya>UNJf n ^ ! evlJ؞XИ*mWcNrdqFú2pFuV'Iؾ-φ (:1K뮺~z $aP6pfqNÿpu>q94g,7 Xz1|?2"n:X9Ls<\ +`aDa 65$t~Q!3EwZ450 gpAxaIHSvg {z ;a/ {\92oy.zD& !`6 Lŀ4,S~;zρ=(B Ђ& uhBYPa]A4Q [BP+m(fdbr Ї <L0ҒiJ | d: 3~;ܠOIi'O) f=NZSu9k]k::t+ZWڕ<;زVְby&4@!( SD x N(a؁Q@B*T m[VD! 9\̓\$pQxq8 UPrK$w K׼R8o{^W-o|;_w-\ӻ߶/~_UYX 60ZX| JZW4 W^Z*5ߒžv],W 4`0d"-\__#UL!/Kq\d-F6/Ǭ^2 +4T1g>π4gNfF3ю4Js–δ1}izӋ=Y pi;Q>2k H)KXk^pֹЯZNl_%]@ѕ~Z<:w͡nsӂ3zԵ^ug}[{fO;׮'O"j 0w;<^|qwC~?;/|oy+<+/Ώ~W| zƷ>G=CӾ<3O{+"H@v#=}|L?קo>/G=_oOW?~8H~} #-|Ё "%$x)X+- ('X6x8(4 |B8DXFxHJB؃XL8TXVxX1G A`b8dȄNP(el؆n(g؅_tXvx5xPxȁPp 8 ` P9 % HH0 ЇExr5 Rp 0 p p   pp P  p  ␋9P p8p @p ` @0h!@%`      p p @A`0  0`Sؐ` YZi @Z 8 @i h @ PP  M)9 Й@@Д@ PH@Д 0bi9 p899p p@@@ (^hP  @ ``0șgٚД 0 HI0PYb 8Pp`` @|9 K h% `|)@`d xI К ` I9$:HࡵP P0aIaY @   @S: @ pɞSZW `Ș@ @ P) 00000抁  `p P @ɯp0` 0[ Ȋ& p gɫ + FЏ @: ;벻 ЃK~ j  7*k_@끼 p}x |F8x@i(PvZyP" ( *#&;և|XG҇Y_l;x;{[ǻK[;5hHK@[(h` ڛ 0APR0{ 0 ` 8h{Л`P@ -@8`p@``z>`9 pĉp0P6  >$\ԻfL5l0fPPK@P8 @<̋s0tps  zlƫ[phŀHkƫe񠽉 0ɑ|  9; 9ΜоP90 Pψ l'PFXr ћ p  Mѝќѝ+ў$ҜP3=ӟ05]"Mҟ-0]Am<]" 9FO]C)]D --5= 5g=KYZ2M-6m !ʃ>8r 4Wsq׹PsMs ؃m؈M؋=ؐ]؀-؎]هي]وؓ؛ׁمِ؜؅ڔٖ آ=ج=ڎmښڤm٪}ۺڶۗMsb ' }|ɍ-]}֝ӍMʭM} :;k]}M.^ N `  '(*,.0>2^4~68:<>@>2 0N(_*&nNZ+P>V^XNZ~[\^]>`Nb^h~jnlR8➐t g{wynu~瀎灮~脎膞茾.^~閞鑮铞 KNg˵wnk[nꥮ篞 p篎|n맮t~î̾~쾐ȎNŞ~^. Hʋ$^I  p k/O_o%)"o"ף ~ہ۲۽>>۲B?~N뼞uv ۻۨ ÞV~5$^O`~}0=ϵyw _x, 0I`xww~#.>&|n pG};oo罀@gOŏWxJ0:o}޿?~x~"Op|<aJ,yq#O?+dCj:le^F}=Nf^ GA2US͆,b'x56/>L+DA?.ؘ/{yWHg>tIR xQ׌DJE5[_=UNw ڕwbu=W?Sܹu '_z0Ȓm|ϏD>8k ~Fϱމ$shAn (( ʁ L06 .[5jШG:/^L!>0 \я &wK,H*ːF4xK;z {ɠ ,1@C35BhG4 $W2I͗ | Ġr47c(8]ɡC ` \ +,s:좎"-l@Uxfă.rH*j$2Ȁ%@S=&E8XB \҄H=_< >L00k!$l3u: r`IqD  r(  Mɽ(ㅓ+Ǝ5 @l! O{k! TDO ڡ 2O-zt:CuL_v` ~Kdvҩ,tZ\%K}ѤنrTJ.xfz]ɄH [xg?U6su{vE^87)?:^D;!<,C0%s^Am"X`,apt{L/< *07YF9@(."7"f.l8p,>>o p 9 &rTZ"(> Мb d"D;Rzzg&-qϼ,&Z6SF+${}Qf4=!h9 H1~$~dprǧ?)bNR7Oq\R,3, +8́ ܦ%xm*U{vU^lZDHCˢJ%)1Pq)jH8>Ic3!I! 4E/ۻ!=|*3.DFHŁrK\OYlgnG BAc$9bT`,G!P4AJDHDr!DȂ #΄M+|$HHG2B@rjݤAҳL&#Nm*GUm$-Q|8dVV|-#T s8$\=^Emd8RRw".gC/M][HWRbO*F('cX(cqQdEQ1˟\K+ZY]O)ʫUW?Je&XVI%ZP&J|=kZ!+Izϑ(9vybfc.*d`EV,tElZs"V{9-9Ǿp{8h)qUґE*89\t~%0w(%:a%U|b">2bXyqb,>6ʉbX0^q[\c8Nn1dU1J~b)8[sYbK&FɊc@Yea`{Ԉc@~&4-?Zщf4B/Ѝ~!}IGZӎnt/mJӔtAh_E lGRֳ~e}k[ֱq_{ֱֵ]l[v}gּv]f3fvOe tFnx[w}o{wyU{xF'h # _xG\>S<=c'y5>r)xA.u ؐ.xL\A~i=>_:ҏtCBoԥ[:ֵtsFz2Nt#=`7˾v']Ym~"ޝ/{{w^'| x'^g|n:^|-ygm=yЇ^'Mzԧ^g}c^!^y{|>r`9Xa|3 } #@>[C9D<~@_DarI:,XIY0|("Ɂpx3J@t\ȇYP |U>T9 I(UU `pDXI xH hXɀxHX?J?sЀ[J@  BUp|T 5āA[qshA=>8ˇ zH%DL X(H C%lX8DD ؀Y(p 4pXJH87tIBxp Cb<= @ 8XlID @H|SDX8+?6tKxXPBS D04ED @K[?Kh[BXA` ,FԼRI [w@B$ɇCH!A"1Y`tT x?['D U8Fp !_T8s@I[Az YX! H18=ӇH?;C\J,HXZ˰O˳L˱KK KKlKK$˳KKKD˿˹tKTK˻,lŬ˸ĤLƜLǬ̼|Ľ̵TLLvແ|xMN@8{̈́؄M״MڼMܴMݬMM,*WpMWjjדWBWuWz#{ͮ}WWpW|XR~)`&$.&|I Wj)$+8ٌ%.X-+MzِbZYV-V:&^7[@Z]PZ}Z]ZmڤEZ(%.gZZ]Zڨ[$ɲ=:,-M۩ڻE[U\Xѭ]|-n`<:d 8ǭȝ\ɭȽ*Ҏ)+(5E]̀ͭ+ 0!U˵=*0M)ȘP5NE)ע}^^H0PL. -_^}"yM-ᥨD8_~ ᫲݂W]SaUjR{_޾`4h#,  _} ^:! ܨ_~8 ˘S"^)R ZȮߧ "!.%PAaVʀU Bby-eͥ,db_WZ(\,]J!CFZ`Bx^,^ /hjH!I J@7c`9e g CaUX@^02SRQhqk| I6F9Ʉ^0@`LH(^1yJ^K^GC(Us !A=ّvU(^}pWZ(0Iȁ^ZX咯 *WЀ2\Un Buia P_ga  ؚL(Xh.8FhuiFLFOp^9Ip@o aL`,"$ 0(x^ Pne˙ub ; xbd'Nؙ.Z2hxdٙٙ-Q2B^8_Ȝn ;>>fБ_șٙI웸 [Ȁv+@8ɚ.A˝祎рӑ>7l*Wp=qIț- v.  Ff^^8 ʁJJ F8=l]@ \2ޤvî횈xʭ2FŠ0 n x$ w0Eb a P.ܛJ-Pܿe˺f[R;(jVy %­8-6c$&?f zmk)Zse^jŊ_B^o X,+1tJtKt_P֮(WJ.AxJ/K?uJ7j#M#U@uZ7R7tyA ?`Xv@O8Odc'0v`B!t±('2Eaxdwvd/v f| Z_ *0wqWvnev|\*]Oa~2wmͯ` #ZOցb)"Vh`"oV*>-Kbު&U// ^uq1FO;d+xd^"K%c3xA?.hH/y]yI%MXhN(D]JaN] R%oQc'x!YCh@ѢRÑ+dP-_/u5$%WMdcEVQ/PPIC?~ftS f5XNmA@t[pFTJU 'BB=G(@T׀Zqj eDFGѯk>eܝ/6M\A ?)A9YxzjR{6'-3Iڕr3ÏN\u P 5I939%Yni]aKfc2=kBc6rZ1QE[UY :k 0Z bi R) ,*66yK`I*Dp6k@]+Az ^)/q.衏{@* %IP N鿣|:we}׮9G ?SZPӤ"uN=RZTթUUR Tb\O֮Fu[kSjԴ` [ֲUR*XV5hu+]cw4ԚN\bc 66e%;rfY~leEF6e-j][JiI{׺Uku [ݭi['8Mbb+Rֽ.vr.x]=/zӫsҷ/~묧 6/,w>0W~0_$*10`@* ^B=G"T &~1'!4809q|",|+Qb["q>&UǷ@*rcIP$B8s>TA[őc,9W3ȅ|/ЊW/@Dx *.P@2 pt?:!r@C(oDd -Vypј Π  `Q VL6 !z  6)*zBfMb]ej>fa~va¡nB! .#Bb!'lMݙ//l'~"'r"("))v)b)"+*b+*΢,'b,"--b(".0/^ݭJ/<3K3>3F#4N4V#5^5f#6n6v#7/T"6NM7z#::#;;#>G@6?  IFC#XkBڅ (I$G*l$@>TdeLdH4LN69L P-(d %"dPO*Q*P"OZS6QT2%T%/eV:UVeTNVR%XWeRa- OGLLNI']&bl|T$_rC~Bp00a&&,a"bRc:aNd>&c^fefB&ezgvfff~hb&g&hvhFjZkn&i&l^ˊdȊn r$" GQՈrBCL/T@tN'uVg]t, nwbrrE?g'A܉GJJOBztZ'}֧}Rg>D d) ĸέfa#ȜdNMgt'VbZ@{@ cХ]Ƥy$Lo~#r?TEDhEPBPV? Z$B*L(F M$>ND~Pp(K~IB֌lhL Q=f!&*GK|!P>HX*H%Ş:88B$> )?% :$D@Bsx,H?$s0-iĂ:8>ł\-nV,z-!n*$ȞR*)ޚBRmĒE|,C-d@ |'ʞxx'h,HϞ @hD>TP.!\z:(mzPn]™ȃC!\> f>i8*-mX@Ci@*hVjFPh"G㞬r$K,#iCeD2Zh`YHIFp1FKWķC*^~z_ԴrFyq&(E3T[Ə>?ălJJKMzHFDNi,D6P^JI\kJ蔦*W>&<,?%<+? Kex)n*ڣL^$c#[|C8Es7I34O5W36S6_6g377s7388s/XX::sQr JL{2D)?Ƅ㞯LF !1쬸,@ %OCBCt8BCDD3tEKCs4F{EgFctGkGtHHtItJS_RMCrJd>1CS(q#fNe(3f4b+4#T;MTu5Z5(cVGc4fo..Xrr)0k2gަTL yJNq:O< i08O>{їqyJ55aDqtG `zd`nڤCEI'E.>+-sS,(jhll;c漨l,nnvLzWqd2Q$B]?G%QlRtQ!$I7&Q!a&M)()RzMAȷ ={C}|}~C{"eQ{Ot{zS485xA)0'd 5R2}2U4O28x844=ӌ8Bs$>9> 9'9/S;qk`LQW9_g9ow999;yRC-BϹ9߹9﹞9::':/7NmX򩅜ϹOUg:oOizs:{z:z:zúzġP:㗤;;{'{Whݲ?;GG;WW;CvLw?nyy:-;;QE<Ļ;܁-<<<*ЃtIB?_\l@)lk>>@C@s~sw>"MM~??)?) )@_@/?3G"X?oӾl $/?X@)8(4oL "(R*EOrlx j!Œ n^>"wJ1L`Ԁ tlp 4&I#C5ziRHZVzHxDid ʊp R4 $EEnDV;(jPJŃmcQ;euر= Hsf[=ۙ`h` ~ 9簠` 8ƃ1wWϠ74*-ͩCJj`C9n?cC |{v/k'w/"uAqhpHO>JY9,[ª;d(BS00C 54d@4B 3 M<壢JB+=$BdpWG IQd4)#:dQG R+QK)r+rHH <%1ClSM.u6E||r+D]TF}H!E6%Rd0ݔQSOHPQDDU]}XamUYYuVYcUIV[Wa-\T'z PA5H$lݖn͖Yđo=]IH?+x|Mz3Z{ݗ~!sW=} ^]%Dw$zј:AYKd7FyeMn9aYuΙgq]>wVh^ih꬛뫧lf찭zkn[m@@LrEyV]͛r1f 1bѢaIjt(RNuիXfJ+׬Bvt,Ye*0Ge:&6e!@4  9n19xsؽ rě_^t:tԠS߀z5mշs~M ut~:xށ4ċEk3h<w1u#m>N}ӏ? 8@C ͖ 1l! C% g[|,a`ÌV8أ 9q)cFcm'gjC㈈mc([ j XCj3 %{6ȠQx __|CߠN/9/PX Al5Yi| P5Ԡijzz3րj驮 c91k[c$l: :ꨣbZmV Vڣ nZ|n.b0N0N>L 驏MT00"j /* Zpg</Hq7l;+*j!_kr&r bD 4XL |첮6aLf^;<@ò*4"L2ĸ~j.ʨ<z񥞚 =;=_B6[LL!;/h1g\駉詑?u_W*%39{5  s0300À!WP5 +׌bʳ0>ü; As Q;ғ~-M?jB 1ŏ=/2(N9ʼnb~fvC0 c M30ʽS =}os (~z^L'@Bx.r0Р>|@ °ޡ15O^0} }`&9,;D f^ c2X>0nNskas3U|co>Fjo \0R5X77>*~#0Gf.glP3Iv/{+]rRn}hwC&d?:}c!16tu m KBtYUF[Yt2Vځ;]R/d 3>p'Mh0dpaÖ¨Ns@K[P&BЁށXqKU׷NV%bt(7"lg{ & ܗ>鏻M$/0(2 fI2RWJ02}b Xq诐I8d)7Җ|Ú@3pg) _aJgҎA&w`eu- TK{~02k &1VO !5YD$H$&êf1kb A (tx-x$Z T /K0|wBC4汋f옦nQf; ks~3 a@ ^@4?690 xЃzр=(0oJ f)~`NvI҆\ u b7'5q;J@`-=4<2,e'Wlf3L6 P>hO4121 .υn0d<ZvblB_ϟ!hQϖ4׼j2FӜow׾}>s{ 'gy   8~(Ȁ؁x ؁%h$0(0#%1H9X2/:(!(;H.3PgY[[\88Z(]Y8 eHkX]_o؆m(^HqwtzX\؇jp8}؈XqHiH`@0pXw( 舦H芫h8 XXHxȆHh(ɸhP{`0rXuH^XpxYXrX!HvxoxH xh͘8ɌxxبznxIyxwgyiypGyw,y&s'x8z)2ymyrgIw艠hhXwYٕ^]ywV`Y_I|`\ɖfIopwnyZhyYOX{YSXx yz7)zxWיWwzwəuWy皖z)w`YĈFiY(lחmy{Ipɕh dz`y[yXY'wȉݙʙ OKٔ(xɛPxweiٖYuɗIw)ڕɚٕ y i *9x陠iɹiɠ٠5_Ixz!YIsȔR1A:ީ:9y} )Jjqʗڗ`) y 9ɥɛ)ayЋi  xPؘޙ |wjzXIN@ڪX r | ypy}XiP VzϸjWzr  { Ⱗ @J @j7uy㠕 wP0쐮 0Ī ky@z p `z lq ` 0 t P بyI {0 k( 0 PʟI9GyU0ڨ:i  0 p  )Y:ck` 겿p ppǠp  p@{ W`p c{w  k00 j     p 0+jtٛi%W69 `@KP;   {k~;p{pp 0Ǡ@ e u0{ `zj| $0z: Pp[» Pe+ Pý0p {߫Rː㋐_|Қi y  :L@kprpZZK@rļ؋ `I `` s< ' p x+wHKǾ J `uky`vL`M 0믜 ``[@ЩƇ:T&l4 ̭⠼`ś 0ϛ 𫝺Щrw-+ppkz@ PDɚ0ؠPЛP ҝ  @q̭l-|J˪ % в  m:p-,[⻘Tkx[؍Zy`Zm=k^Z"jMan~@-mji `,n̠b+Y`lIC4wqLꕰgɯYz㌥J, ƏiYݕ驧͗ǽrݣ Hznsjo=DݐiƦ;)xkǓGw92Yxyiוj~쇕^{?ym!ɯiz[}N}}!>W}"N]]X94Nz5.M[hzٷ8Hw 7x)[k)ّIz^>vZ ZwIwzLjʞhsW暭_SN`\@j`.dNowt֦Q9愎w wk9wΠ~7'>᥾jOw~v^N5ꮞhؗ ݢ MpʣƎݍ}͖싮Wn1΅:6jlާJ܉.ѝZizz~YbzٞrYhEϮ-Ү-y*ml^YyJW9A㮞~|ΣΡn;ߧ۽?M ߈d=X-OCGJ/*ZpZq9rWڟzn)e?/d~g/Z12Am/fRSO84e_gw޿N^YٗtO & ^Ho/݅_濏ړ q}7rO=~ ­_,o=?!@{ ,8 B *tXaÉR1E.PF2d Χ"ڧʎy зK˶RkD4Ϧܻsμi/Z+(5ؓ_~a[ ?QeG|ɜ\crعl3CqCܯbǧ*(8ǂq$ZpNy#zg%EHyr<'_IKО_.(OK4&ng[y?N2'sv̅+sP6Ь{@ ;V;3T) 60Gq6 J E ' rـ_2[G D^D9^ `w/%xC#7`8%9Clu]gSv5 .\Zsx@ ~y@z!`j5v5aM6xu ȷߐ`򰩥|n."aUs/1ꮤ*3] CM"^"$dl\N '2eW#ؤ^&%^n@ v$n`lHǁwv]0y4gM0`]&$^EY` nEs.zPx|_nѤ%Fr1|y?KY:[3Y+ZhQ^"q'N.@_"3lة/'M:ۂuRQw=Z]@p/6^h`n.6`iu@q@mǴЇͅ6&lz%bILQށ2 i"J' v\,0=Ʀ d|P4`3#_́ nʅ96ЋՉL8 0Gp&~>ibH 'n _&( 9-4 ̃еu+J]bPf.F7UɐT8+XwF<Ѧ9 bq4DGz㍈kVj 9lmԟ!uFgdG.;pՃ;jm*ٞE)꘲:#pq )1bwhuJ$x-uTEVյrBUe+T W5qUj^wAr(rQdEbX6ֱld%;YVֲlf vE.rZҒ(oX26R OE xֶmnu[ַnp;\׸.4K9ٱE]Z4կCЉEP B cm@ XDe ]\Sbt[v@ j\%ZmdZh#mac;聤E0A|\"@Ѓb@1ukb+;A?lh#=2@ H=Ibu'AJb؁gFC'A`pT{&w@:Eօ17`HSKQs9181 h; 8 aBF7!^ jJﵣM)s=/weta'OPiXpWU;G?"Aڗ7\u?C{N\ S5kvtK*cڽ;y~+6@{!A@($A *+,.@"½b :j!ȶ78C8́9;<7L=?:A$D=BCXNMhFԄHLh9*{c%y؇OEP {؇TTU܇R|VEHEQ̋JETlSEZd]|@]_V\EZb| ^cEgd\Fa _Q$EjdSjFbTEcu؅[؅s<ǻI^DP3Q(@ !{FZEg<}LEe oHEXET|Yőp[\ńHLȀ-T<B=ESF}Eyy@3ETNEM} QE~EREUQ=UTPTU}UV\$FtUYTOURTՔU B 2J4arQǝbgM%+VVg֨ר!Vg-q.ti]WoGJfoe.y%rVmVvsx  `)ֿ|Pش{pW%{Ws}tVdinyֆW؀؎X}׋XkW؏׉ٕX% dL,*(V֐u5luiVY5٢-َeWyYzMڣYV{=ל]ڑX݇D3W%xُn-۲ڗYֵWu۴ۊ]۠ڍد ٫[s-pJtk|U0ٻ}ئ-Z[Y[n܌5Z\5ҕ7͋5% yؠgŅ_pvZͅuTLכ\EY݆m\\Xgmu^NuWyeYU[ږߩ d/u[qz ^1޽]jv߽_~u`mu1fʹu` J _޶ e~ `ڀ_~ޜX)`ڕ߻h.vE!` {{_~Ȇ ` gb~φTOy\|@ X\`@,[ P~@_\12I 7Ρ~؄` ni]l _@ P`V۝ `l vXa_9v\X.^0c  v._SeH`0WO XŮߝXdA\8% ,ٕ/vJ eڅvv~#4I։iI}~p ` 91v u ^5ݳ%v xc uqޅ\(su8u YuВ\P(^If 8hҽ\ܝꐈ Vb2T^ Xʱ_p$n ~9 ~thWfjn-dއ 0~ȇq helcYKg_zX%]c-^y}i (/vF} hiv|$.)hQ`U\h]ׁ]JLC씛 TT6]Ӊ4SWWp_`%̇(rSt׌UG5==TGHJs._s<r3W$;7wl |MV : ( Gt xGIgLJtM p xtVWuVNN [u `u aa7\d au5ZtGIIRtxxRwWt v \Mg]"\`?v[ Hv[fo}Vc}·8($!0###p7yPpPPgyUxUXVPR`LRyRȄX8 f ng3`aO^zgg[rd@!(y{PWy_UyyR UL Y؇ (ޔSz^Mz~>zW~͋ 4 Yak v@%H"|#|"|y''{yoW}gV`EGsT)[p}<@Nw("|'G_?gԇ}ؗ 4) JE oX6}hW ZV’_H_7|#Gt W1lD$bŊX4'z쉳o=le;^w&{n$I%JJ28A A9hŬeΞ"b^h\G~g'KrA۷>'ABv_,ThQ#rnjBO ?*ŬIÃx8  7mb[ҟ?}Xuْ; >>V>4t rxWdX^gSaYHY?8Vd.Y&fDi]gL}xxyDXvQ{hA )$%ol@d  cK` [l&l]P*jSH@EP2Ҹ'Tez'|rMpaHOKp ܣ)a`"Vb)d%d-JhfJ!qSO(ތ+r^e챆 ?A2;&,C@ A;/=[0@?@:k+j~4++;⩊+kpԢMZ/Lc?@KnY#؃]U*Wj!cX!ZeN I-,H(es44wc>T/nϕ\lv 6q,%k6k:!7L+x[4FƎ ᅿ'ZNC@11rm./n>biJ!OdFYf. :0۲/pߊܺzϢg o">K7sadJVSLr/K4n:^)DE^s(;r2x48Ta8©cjHZE`bIJL/ЅFvCȋ\ʁ.%1k<,EEABnC'iaK7ld h9qΖ``:腏PcBT%$QƴDy% qK^Ogc1T]? }h*|' s9pG>0-Yю \j^P/k! !1P©@=ы?:{.+XYb0%p+h݊R@YVi:T$*^́+|eQ`Կ@ E(XXXܯsCZ h>tʀ*8%61> $I;Vj7siG׽կz?2Y.ҚCi}=2躓|񒗏G.߀(N&Єr9F P 1@_(9$~ -`*ʍB<]aB'=B٠)}jyLZO!ޫX\^fAR Z`^21`ʩ k Fog )H>EZYG̡ a0a" Y!YQ!]B,H %Za>g;D>E JKD0F NK( f`AA)!z*9҂:b*B""8#uALO!Kd7CZ |MS 6r7~8A9N`^^ r!==A T=`.aB&taG1H@&l@C?p@0: NHL4Zz3Er\$8X!+dH;~:,Da=Bq_K ^^M?B; /laC&?d/ "VnEz89e:dC; [J!>e\B&&kf-lE?-$:H>-7e=> >K_\TT1;VretefY~&Z:Z< \̥){?D@BPM?g6H@6XOt^ (FZ&fjf*5%Zf:'xxydKL&k&Yl.d.` 0H@;H@1L\0d\pXK03LJ5a#KzzWA`&v^YR1Dhg>Lh'77d)'z\B'A^?J0}d?H@0gmB'`*( iu6)b'J)%<`iw^:'*h>X%\B'jfMznCh>D@6D@pqTb =H =&zAk\BR)5<>\)Zh*`(y)")A|i`h1d@mn 2$"(v%+X&9Pòбg* ta2ɲ8G8r*9XTqS6g(Fv:. 0$3$%p&G'VWBs(2[sri.@+uQx2QGs4x F300?[2& CA1LBAqq/n*GretG-,q6 ^ȎUt3>׮>s1s2&K61A*kDG4\S*XJх(Eo0HIVt%t@urX#ZG/Nov7]^3_Р5_MCu< ES,9)U>V'b G'=7pXk# Z?/4F7\/ARsjVP`3#v1?g&KC8̃76$7rSZ/α%4Owv8h8kYak;F <mh1/bǃ>xz6{C6 wqq}}Go$ DD8uCn9h@\[U_Uh[x]=#ak8yW4xy7=vc 7x A71~7888wU8,d`T(#,Zi T\t(mo8,3@%&S>åӃ?X78d5r(z$8pź::e /nGx8=C87xo#hw7 f7@_~MںPOܠ;ZKkjlo,z̻{/[4H;PgzC{7w?G8?L[+@$)Oqd3XssA{;<={?BZ74h?9yff.,kJ`v&<̻{<=|s7xGB$,|ß2C);ƷģALQ"<Ӕ!͢;;C2H˿<C6@3|8C_}[!ɔQĔlBP-Qz7>'{7;80??@x RG"!NC:t݋AE4={| &V?q]\>6, N4yQhrrc/ LɯPi\)Z|¥JdҬ!ؚeˌI;i½G\߾ ;0O ,P9;i '=tvp S% VKfӬu۸r Ǘ߾{#X0H}ڹc硶{.f@G{zm0Kb1; 2& jO(GR)'믿ʩ BB R--øF8kD& û{ĉO~ڙ@|᠟)`$XMJͫp³ւ7 w ĿRV| (N93;{ $\4ɖI%(fvɀx¯Oo*yo(vxڊ(*"C7U7lUkG3J1:@gƎpT3BB!zrͤqH5uB2PMu h5W/EoJ|ERm Ό䜳; re~4E`mN+ g@L H=mK JnUe ntV[]`q `QF=Ξ9'4q`20vb'j'_C&[G.dq YvӭVH(9r}&S8;瞻{HO^6,Dѭ*}@:iNԞd) *H' Mn`l>! i[g_݃ӌ/l_z gp Y o-Cһ:\cX˯_}=vg>;C'|s $[֑-6cuzȃ(WTOюr&U1# IӝNUAh! vߨa%DD K{W!67aWS;b0pR&€0uQB|P *,*BO3 EJTblC_4O 8ӃT:9I]XʥApJR!IQB21+`!Bq|]Z\1}[PG7=EQ1csb*霣LAsV?qЭ eL!Yme8ձvxDD!r6m~l@=~|H3{$h:'}@#6)yTsMŽR +@+XAX#!vT'1 Jر)ٶHăwG)ġ샨+*Q;"5ZyIKMt *42%5iFSL}>> ydF5j>j{T1 N v4U-( Fɘɮ !*W1+ίRp:cZI4) as@鯐ɯ-fΖwʭR~`c'&Uf6LsYRx ؆=Ϸ6@s^;#%yۑ}o CQPacGN>_Ǖ쁳U  ,J!IBՍ$J[ZB0 r;{Vޝ` #@O6 ~)0aza ִj?}uU~a^ط2~ocq ̙6ʋ#AA رdQ0Y@{ !TGd$t&7bNft! H'8"_C g8"}E~H_6Ilҽv@ni#OߓpDLo׿@acF!/~q[|0+^C3U#VOp?`Z@nc.׼P>^p3 [;: Nx@ggp[@+p~p[u>l x+QkG`?$zP*ҺÇ* AI`3-H{&/KB1UcF,55$q/&hMdIo6~jW v'TmĽ<ʂrNrnGO!ЯЯZ#6aF"*(h$,-N~/ONOl!ꮏD3'Dlif"8@&ځg[NBmR0oXĐ.Z`!1Oxk.ʶL%8Q|! gHpZjO< DLAL΍ pI`6q/No0b {0cPr+Gh"'~Dla>p$5rQP]X"KPr[9eqK6=`NG2Aa! ёQ'!8#"*f'<'.P0qF\İQK1oHd$0cYB'4!ZZZ0Q!''" 12#OQr #0$!!& P*QD}A<E'!haB%龰.Ib:g,RI'2"M@"YZ/al0!<AF$KP,%y|@!&Ip5!y'7(r 0A 0?_1(O7M="ax1nORG"`p>:i; s8Fl 4RB)BBI=CR$!3>n$Gavځp!l!8Ebaz$!HRB2IIJ6{>EtDM%1@KyUK Ř(l|LGp'(r6`4WUM<duVV]A"zW#`rpX%Q$F -#~“><2gA:n\p /C3t#_0mzh6 8vc=V̡RdQ4`Qa1R=S#Q1bOߢj*\3`c g9g5?dS$ڒr(+h:" dJxa"aثlƖf) mn=joA6r#j**p j'ֽkk˽vl *&lm+ֽAsvl#`k=j*\pii:;;IbA x Z7!4id5r y!7zzS K?2K{Zw<}xw~7{dWZzW~^N8i7 րjX= wx#(~Iz7qGzt[8Zp:7-LԂBFiX~ 8㗉헃xIL؄UGj:i)Q:ujAy~8s׍m4&jQxD;ׇјxeX}:I ׎39׎WgxX؍W3Sst- +"rc0Py8|8^׮bnزp٩lx)YuRDpux E*3W j7}vu491Q/1@ù}5 zEJ؂d3OY|7QrJ(.x[ڟ?8O0yeޫR!'ڙU8szR| u8-Bi8O]:iQ'⬮s)j;@ʸ'9au^Zuy!ڡوQ-iy-*s'=7=Hd-!'IaW2{bxL"4`B ?W+'21YA<%1a:~d ab-*PՏW;adڊ!=4My0z'G*[a4!!{~QG~!a6~a&r+=(6 !|QXc$Trx!c4yrw-a{tR(C㋸QjTl]˵ \{ {Za|%uA1<Ŀ};]bM91<㐔PraaZ4*uqǓDpa.:}@y!it×cDcھ,JR_Q3&7QBL Hj&<&.6Ia2`Q&ȵeͣwD*Ɓz${!/zZ-|%`4 g6`mE/xA[=6;sxaYS|P:m*+[Wzz+Ӂa.5D<8=4$,2/=&[ozܺ|ۡwRf8˸a(*;[qHd'R9ؿRU)l|fn! &$q!a3xa%6-bC>7ג]z~q'>=}$~Ovx&=Ia"@Q|p)IV^AQr&JLu#Z# ArczbJ5ʛ)'pϏk\Ǚ ?[P\6G44l}TX&}4`BC_m؄+/9ڔvYȷ.u:z2d?~I4I_ʏ*; eL,YLȓֳ4i{%˗O|ɣɯ\ңIНw|sij?hw޹oٺ7^Ŷ|ٻNU8_1˗BiJ̖]:YeE-Z}Jrzh237{n :JƐ;7xJߦ6|=ʝLYtowwHՙGln2 dAgX Z'JFG<%lPVY^o7C}tOqDs&T wc1wma=5Ek]:# $M?l^feܣ[,*\Ji2("V"R򳝆CkS)szx-)Y΅Fhu3.eӱWhZVR ivgGV!y`!nLBO#uO^*g6vjNYfdR ߘYKZ zԫܮ-jQ'욄ks}$zCt#[iÔBWeX(p怆*a6FgzUjEEgKڶ`bܜ;^\beh\ X 7,6L6鰟MS\6nŻRM #fhV˭Jߊt\REJsl?p+>8.˲dʲ/zB6yϳ{z%!]qR)q+F݉tia*.vT+[ߋ'emK_*:=S˰OӸ-*)q`k&φ[Şg#H1I= 6$Y97:6N, pNmCA _&a W4NGG.r dIL;@9l@;Z6-*sIRL^^mG02FekYʳ7=se;k/7W'S>Ȣ-lB,|% mȕ<`O9Õ4?zQnc8Z:?k[qxhC1EP^XR.DkR-X$!,٣oA ہ+^;V]-KhP:nako+ѫ@MoW$ 8RMOH%%YDl١q &\8"R:w4a2ٷq2[SOPAtڦfbQGs g>aO4g` E`W$sUdOql:xv#Kh_iT'8fx2~G1cBP@*kQJk{{o!q=WUTm,7{)X&{EfO1˲dfm.v gC@^!}%#7HS.@h%-g*j:@sYb%ZX`Y& +wqaUp%1PCCEdB]ni˨&IL=#*!cP-Si<%LbsLв/_ i\u ǔºp’,j DˬߨP{\JK֪8Y*g2LmQ]2; Q@1bpxT%\v 0*<Ĺu<tQ )lQ 7 Pn) L,/WxiG*WzIʏ/}@ P  @P>Lw?< l `P n\P\T <, PͼѺȠ ,v3;MK1^az([tjM۬mۡ++(\l0n]@,H<@<ěplpP_p  <7+%<@-P͏/\ <Ϡ ^~hibMhZЀXJ%ʹ4avm(ٺ`@0#|H Qli 1M 2_  &]̛\t|*ؒ`,Ǎ=P =nm)N[b<6l_p Ȗ\k^ĻLb@ /Dΐ1LSBcGZz꽪\tGƝԀ ғ< ̀ 6 ( yR}4l9׺6+1; ~lRh,C,6F|<>n;}rwo<ʦ ;%ฑ⠛녫۸; @1nnծ5 楺Pl[洞@ޣŷ .N^ @;^΀0n.~n nLgFLXۨ}a=G> `))_.(/2/(/,:tQn~#{ۿMn5:{L<{` û1NQ 9k۾<+obKꥭYM_:\*\\FZ/O)+/O'o=jK)fxJ4O8qS$xct~k/*#Q۞Jbd7QֲO"<.ϰҿ,ڭ>ާo_|{߿Ooở~}>Ќ4'+9B '0 C CCB$Q-DD/0[QN*AF{F OPv ALF&+dH ?wDH?$1F)Tr%|E(*;5]t 3R*y07Sd3J=6_ts2R DsFREH)tR)JA(yd1T+DTK1KV\UUUTo 9t0;4c͵^Bgygsg&gx.蝑n~iݙ2Gd]X_dyZKN fy&gy{hvnvロVt=Y|U4Q4jZYe7;`[O]#cU.tZ(=c]N(F,hD$BE+Pa|A@ф RgbXgHU +Ŭt WR9(_uU9Y.DZ)5roB#$2HfЌ/BXGq*=*V!}SWAṦ&F=1ˍ,cjX+Ʋe1! .& |A$ty".X,fQ Y.L%9iFUME =0*p::6mwcHDwdi>3 p@曥4 zDٴ8"<%rqUviSWi4Ҥ FԊ$f,wV@D ㉻\/ ΒUCg1_+ވqmb^湭=cP>7in6%lz8FQnM){8$^ֲ"D3IF=/tT_:/53UmC9cj6npvhT0p[\msیM5|I*iw֡-ܜ-ݪim/~2WSTFj9VIToF0)6bwjoVyYZ2l1wv#ԕv{-fB^Lp^єX;;J_ C#!RهqG3zXy4h{ +ӑ8{..g0' @</vK)cڳq|_3#!Ŝ"yc) Q:NBQGӭLW}:ӹ~{D`FAigյt}+O˾u_ӵw{:?x_ ȇ y#<;!/4MoZ{EOk?5_zٯ}{/ד41zk>׽G>yy4}O{^_賃O?_ȇ;[zX @@@}(@L@\@l@|@;@ʓ a@ A<P)$nysɔծתڙp%MLr .*̎R%4LɎ9BcW4G..sHM+r/ER΄Q)j'ҵp.OTͨht4Q4,1ұQ*%TmPLxSmƥsF7/q9MI6M5kϖRy2mK1hg[ljU4S˴yͰ{hrōumrS˗XkSMd8gWbrU1A,XqH*\pa#JHŋ3VǏ 1rd(K<2%˗.c _\B҈1)I@Cz Qf͒ QFU : i*U {ѩه9zt҆N,QO@s($h 8V$pbՉ-0Ca'xT%:I&hc_23fL&119y͠cI6P1f՗$h<h T(cf:-Zii&(ƨ8VZ !무jD 뭺묻 IA8,@F,.;b[-^ۭZ44 ۀӲۀn+n+/H , @ (05( ?q ;p#̰+k,r![0 Cr'\s7L8l4l@98sH/t$H,3X3 A[g5]5dKhM]{h mg@0 P0xMNX@87,0 p 3HN呯`Y#P3p5*XMA:d\p_At d`<#|PA_sp;w~gpB@ ;A?Lo`\< _7t$8` t 0‡?! \rgEo(D.K g l{ytx@1Ao[u7n _T+8 'g$T+P pB x+< 0Pf x4 8F "Ƈ*r_ 0DᏏC Jp dd)" B͌R 8p!29o{b( |Ӏ rG! ~ l=1k< >0 b|WHN\'xy;.`A( DcD4L C$i 8f v! 26MV@ ՝Y3 h@ LR+@xQ a`(0`ɷY2 NB뭀׀84k"m#rJ 0Xʗt"Xmrq#G2$Cc7!u{'xj'( +ߝ 9+Ah D@`[HrU $@WwFa1X< q*pAl q+CTQT(hO7"+\P=~+|,˜^4=!@ 7 m f+C\+@Nq=mG\(@hTbxQ`Zxju;wP.g(3+%(\΢@Q0(Vx"*NW vFﴊ#@㦅۩ILSZ?%R#[)xàc@GjR"*QIn^] ﮱ;v^S@uZ@Mb{ pf;^G_jI [m;؝F]Fu;vXqFzwO}#NpenНp+\ D d][6?|'Q jɠ8 ;*ϹNrw4\_=HjLW;O9Mz-e /? @: {¤-"G}ikrR@FҠXZN^ `f8A ׸k<{`tpp07|x.vum]؏ prM?(\@(AWP <=~C^}Av`~@ӓܙ&,)@Sn^n[/&1 bptFvw;X_'pC*0Cb|`7{mwv ypE|v WrfB.0l~*`(Xtcɧl8?|}@]ed!'pY#6I7^W7pW;tdF,`gstT6VHr4ma6Pֳ?95N 5=|As 5uW@v)p}F@~{~ xX7Wh N/V+Gr"}'ranwk$llox¤lic6qsBq/t}:ަmKmm8XŨmummϸmx(km7m(p 0q--긎Բ-Ҏ.- 0yW5#4DI333oBY3 9bS 00SB$Y%)'*)(ɒ#ْ$14P.S(2<)>@i>YJ(I(C(P)L9)A MQQNBqT"_Qe!S"2Nr\`XiXbN| fɖAH$ȑbIA"r)5raRS*M~I3aA)u@S .(fIYbbrIGK`{`F`pR@!+W PXp0 G @`sqd 0bPRp~ @ b`i!kfoPt $(N0 ef ePnpwc$0`yY"a`N@Rpe"`@`Y`R@@` Е˩qiNPT5lpop;0J0sU@PH@~ 4s M":'!)*Bb**H9H0f@2Ipaᔰ** :ZDpںJp#!A=`3Zʮ@XC=C8cjڮگ4pV--{ PB а -8 {'4R5!5!{"os*6%X084[4 68<۳>[;kA۳C[xA5MkON;Ro WXZO_T cktlhboжnokp r;uwKq;x˷oPVj~붆{;˸Z{jIa o0۶r;o2`+ 0۸;۸fTl< Vp{Z`z [kŻګ[mHm @`V ^`?@ d ^z , V ,op o' + `Ǡ 1{j۶V @z lzDp2ɐj@+l`Z P +૽ ;:)`;F绺T1쾡 .A O hPp@2 `` k -` q` ʼnKn ' 9P@ k qZp 0  oɞо^` V ࿩šk p\=PfKfpkk{Ŕ |k+%kL%یT"|̷̛|j[} j=}M ] Lܶ8H1=Έ؍o,v胨HA ԍ=Lj7weDwfRw|QpW-ipY(Ae|'c>Kf<ԋxoe[h_=0xIS SHwrxk {|M.*8͂]qM}Vؚ 1p7h)nȚ9=wǂgm|%g6s5gUpY{I dLx%Ap K w^qp J C@+kA0͈P -@6I0:ߞ) :>0 0. +0 .A 080 G0~6 @t # u|]Igm(0%0 ; #0#0H% `}'qI q@0P w)@ (  0߫I7*U =@ p `sWx١V3}#xT .K:6j~5@7 P#Ps6[>׉E~obS p5R>rb<68A4%@ھ.S !N~s'^>AN*f@^nn Paxn)f0h 7`b4_)0xH A N2bW۳ <}4p0KL3t ;H@[/*3,4B_kAP`J.*vB_ A yӆ_;02 j-Xr`p!3v?<-m/O_voNlpN*0=Rh۠5Ytɞ5DC@4;O83E`PЁ  2p0ѠE5ZGEnXB<rǐKI3̘5ILÈ6t3Q:f3%P/%ܤ8RсU$ (̎'\`Y@t% & 6k P`Y"RA 'OJP| %)4"R\UZb@%a%|sQ Ȥ D`도 0!d,W<Ub? Y! ).yIU& R[o3YR0ňH M4d "x9xR3Xz)d[j#Hp T?pĔ?(I@x`4<1c"Jd`!A`*$2P[ Em ![@X f$:8ER4)A?aW$)" H!O hn#)h @C G9jb"*jA@Aܺ*L2BS 6` XcըX7ap Ym݀ b-Wz %hvZjkm"m&B2A tUw]vu7wՅy۝^eKBs5#x` 6` 6òVJpJ̘ {Mxc7a3թr): .@3 *B8`~^yaae p]'pH2ZR9_{`hk'aI"AM># A l`z6cl~_S\Gp!P! J8g&ڤ◢p,dA&&=Ao8X}Rm& ~`N@l6֙ѧ/`y۲Hx~g閵 Z#V@6@&T`!P@*P0(JmVAv"! G(B0R* MArPq]H; sCBwXD[#6RbH+,XE+j\#` `cF8QscF ."8@G@R},!)BRDɲB؊%')+X+V$ te+O*%M JAjS(Xe-pR-m9\re-cdf3LhFSӄOi&5yLdZSc9INp>(T9u.f7M{ӛlH#9 D L#`Th%,ST9Qtf8 #Ԣ"%CG:Q ZH) +@`T@ІVˬ9`px Ba@!D`f<׎(SD ֏#]*w0h@s8A``0Xa 82 !s. OB;ч;(kBdwHLC!Y! { %#k&Uh,5N;Ґ 7̀ )@Pi+$k[0X3a C / y^2`fXА7$s5)X` o*A A<FN'*zxC 8Pa+'F. 8`g NG`2 q!D̈:e8 # 8A r؁Ҍ>` 8,0?!P# +@. w8 p@T` ;$;8BH*T0i@0N08-ҀFig:jRZN9 Zֳ&PYZָu}k`zP5V4՟=ɂ0[v%HR[mc{&5ڑhGvn13M+r['x ޾\xlr[|8%BcX"'V%JL|Ǻ ~azB,h#J tp<-I\\N+AKMtt@P]MfR)8H$3^(Ni쩲 hn*0a 8FHWAڿO Q" eCGD@$ႰHS$*(4 q@A1ObY Q "D%p Q AN"2 8cH> :&PpwBEQ.? @bF-,1+A D<6pB$'\dPDvC@ⓄDiHD(!#>}1R(!VXh8hUJ& hR`&@%#0JAC$;hWMpUS? dX N8؄WKp I \@J؄ hH0 &%H` (Є?$HK@!1j3 I*bBx`-2.Z?00#$ˆ껼י#>E\E]E\<]\\EcE`ˣ!̫ [@2FiFjFkFlFmHp UXG|vlGwLX bqX3XL$hᙝ4ȄNs(eDf0FAO $0\'PϙaiHdyM"H{{4%Hܑi8F0W  3@m(Q1Ñ zTGG3Є #HU(I13yBl&(mhdPgٱ9ˇKsȝ@9z ͖DIMy!ʋ<ĭ, Nxle0GޤЌNνNsOOOOOhY =PMP]PmP}PSD|lP P PP͆BAG Q]QeQz;LQmPf@Q )j%]R&mR'}R(R)jHeQq)NR.R/R0 S1S2 HȔ6mS7}S8S9S6 U,:"Qs(: ?@ ASP9#99GE:͒;ԏBSTND9Bբ dR@؀QC(;6TOmYԜ4^P xԐ]( VHhZ1iM XZԂAӨsXVluE I bhs xxO% K\V``#X [x% ^Kh%XhB5Վԍк4&@N@MP!> !0A%WYQm[}Dp)2'!L KPI!-z"pS5ݢSۭPT>Z$-\]J0QTɘH\Gʽ\\\x΋t\wLvua]+tG%zt@0Nx\MlI͇(4]4%P=$HOɑlC8MKٱ\4TGJ؁J@Zݷ Lѹ_IxtVFtKa0DvB>HV`4LdNdOOdN&eSdRFeMQ4mgxZe[e\e]e^ehQ#^.fc>fdW\8YFfhfhȗfxfifneee`f(gs>gtNgu^gvnufef uB |g~~g|6h>}Vh~g 罅ӱh:h6hҽf!kZ Flܣ9IyչEkGE~ig4fؐ&nj~jYxjjj϶zSꯆkk66k P3]P kkekk ۙ] FNl^l hl^lVlnN ij x xl~Ȁ vmئa#0.RȀ( R xk(N2FmG dȀ^dP`ha8@-IO·)mvoPP N X\;?(cHE8k 3_Ά`x?p a H.mnn g0Fp( `HJ_n -峄 _x "@N5lss>sA7 0sE؁] kЁJΰ٦^Nizz @;ؕH!IԊfIU[8HBl8H \1$ЅDU` QR%8gXlDHX2!HWLP``XP!H@@BUXu?DlnG1|E< @P`B%&R׏RhQpOS8H9@$HJ7Td0tC ,$JT* KL& tȂA$ИH!>.ʤC<4дM/[0S%(<U 'Q#G hhH@ඁE  h] rt"CoϢ=f`aCl9ΟHX:ўW,:tk^+m(HƒGa|p(H2ȋ'>]uG8Q׍3/2c&fR'ԛ?;ٟzwwYD4 j$0͇$h"$"yVu`i5\b,igT'pyu_^^p6}78) 7}'o uݹ9kFtN ?jW~]/76W}EDaF!G񆔙@ r8 CFi w[R1mp ̱fap4`FЅt%K-AlH7&mAp g0p<!gܠ=p1! r)ՑR~0 lc*3X h0B(-lanx u#NqT@#CH3d'A kנ6X 2"0TQ3#DW89 P0(P3J@x,dX0b*0 !g{J4"J#HHBh`&pKxF 3 G` @1pC]C݉$ q:UwqZ-؉ΜԒN4  ?~4,@YPSMCπ& I m>ҏt%%)HQҕT&5I7@H`,)Nsژs ]>u8(CURjTLEjQjԢ5d*ӥfPjUJTRuK]dD h[i$lr}+]V!4`4Wu+bmV^pX 4D, TzVը- gźzV,R+ }-kY `2mo[䖷v[ @@*j6p.t d եCh[ pW!A 2t׻r%0 m/w/dLWuj$~{8s5*"X vnr"C,^!/5p8`8//6>/v/[ L>@HPW2$$p^r!xA v^&E.<,sLBeܤ!x+cL@v_, C43p@3&$'L0A [ w&)e;w!`A N`d9kH&8 <2/!X{`*e&X f@R2]z8 ܫg>w@* (@27QЁB:rU|;hT/_Z@,xs jwH7yl `l<͎o+(9(||h8X|up T.cܥ5Ӭa8vW p3x/x`2ox21]GhwV'Fs}YK7'X =tc_V}*b|'; N a1G;^n+ ]շs/ ==k#ȀXPgjfit790@ H x+`r .+x$XEbgG @}ι]] @ Z坄aѹ @AY Y4 =WAě @@ğ BI@ǡyeא `YWt^s@ 񠊉\AWP\UAFVvWy5w!w 4!w^yU@ bغmA$ZYX%bYv1@`q"U(!"neaQ@+lQ@LV +z.VTVT@.z.դ-jiE[ee]\VDc24:[D#r!We1@$>XXey7:"-;JD8UR#T;#s#s]dV:E(xx n-$n@c:cdDj8R,a{]DH@$ ٹWi*ACdM#fq~ދXaY `YԕD؍?Rc?a Wa@ ۋ"N>LZV@@auY @ \@  @ M$,lE~^@bR1S?&9:D. `c @ѡSJSc;~#eY@)d Q1&?BYRHja&Y}@eecWjҤdJ\`s\ @^ֹ`WByJ&YJR^Fq@ >!٘)g$_%^$$[ !Ge^u!#pYIX' 9cFuZ'֤Y}%u\Rנ$u&fedCh[Pt'@|(Lh&y&:d`sX5\YWɨI'^&oNk&ېFi !"R'kgYcARk2bbi;%(~c<)vCi^:(?J=D0b0N0ZbS^*f*A#@cH6L7vꞒdfu։> K7ʪM=zj‹cxj7nc8 TNkDZT$.+*KMfn+P=E$(G+k뺾NDZDEkk,D n @H,PLXlHFƢJdžlט4,6ʞ,AHN,Ͷ #͂Dh`-&.-6m;itksnap/ProgramData/HTMLHelp/Artwork/ttViewImageControlPanelToolbox01.gif0000644000076500000240000001455110534177573025742 0ustar paulystaffGIF89atįʪ̻ȵŻŭֶ۷٭֥ΤŽZss)11BJJk{{s̔!!199kZkksƄJZZJRR1BB!))BRR)19Rck{!!!c{{!!)9JJcssscs{RccJRZ19B9BJRkkûߵܽ嘒fbED650.""@?ZW}᫤PM抄c^rlʚHG=:XSzrpoԈøif?@Տȧvsױ`]kgRZck{)99⤞9BBݤ))1~摋ȿľtlڲUUµȻ־گ㨠|ijɳ٨ռ7ۍܨ66mǹгXs̲`ٍ[sm͕CW؏2Ȥt3UcڛR񤳌ݗ@6ƥmټhsZRټټ5r2frUm̲پrWRt@}FF,tH*\ȰÇHŋ3jȱǏ CZ(ɓ(S @r˗0cnl)͛"i\[e@zs(=L<Ўy;gI6w_?[`m튒mz; z‚-fF\sjN~jU+^c+fn)S*S'΃6[ וeJ&زfJb)^G87@peZc vEڞ[ӎ*H)1 TC2 d@95vs$ $A$O s @Z D(P 4 CJ RI::2O}s&?j C! [08ͩNwj T@@xJԢ AIw@@M@%BXVխ +VPZ*Фig@@Rs3qe\5PХQk%G8AbXedX?a~0bPW pֳ@hAΊ5-i1PШVtE-hW4hw{ZкڴFM~Q`TcfYvյnvۊ1uۄ! Vz;^Q ,a~{ ~@0NX ZƊZ@FyЂ n(\V׊ŝ( ^ @tЂF #Bl c@ Ђ":@i !p@(`Ma~db/__jYGA#l "~"Cm ^@!P!oQ8 t[С\PF]hY.̔ndЈ?@Ѓ&9n gB #"DeͶ9lx|Dymz<̐8<oF rOKIomQ:J`ܬ1 ?#p<_oG!>]`.YEfPA "8]p ҅?([c~_7uf))p0@@(A@{{WO -@5koEWwi|ci:_?d;4@TWjW5p9 Rw[Fl~fx6kDPPZu9w~lRtGf(-1|7sg|5xGP-ePp&F oT#uV jܧ/{qDbX8H)fɧ<=MI@7}i0}vr(Mn4pHp-&yV8qPhPxn&0FxV8EЈ p$[bMP6&NgBWH9=(Hxt@uc'p~@Hq&~oT'it[(8p01hSIC0Љ|``OuU=n+P gUF8@Th(P}nVH2l?GHy]c'm4I`S9AIsZC. n:H6x^S{'@9 %nL0PPOGQ5:| wScX00T 9jis&oP09of@ DK+TG:)&7!`(P/ryfm Z?@@!6@9!@h-pz(#י Wq 7s5-{U61CeZ$n!cNLNNN+ lĞOI)HN/@0aHb[|͖)6l`syS-9P+Pm`X8S+O}V t.mZԟIpY~F Up% S=6 JyxFoYc%Pԥ+O\ʥfQ4%PoA&&5}{Ĉb}ʧ\|_浧?*v`Ri[jNEL#u P(-<iG(XEN0mEv@?jgU V pzjy35WÊ`UZ Vu`S4F:?h YXR1UPzŭۚlFzJFijyb}{P?zLzP.g@G Oɢ/WO,KwTˢ۰۱SwEG\ IwZP ypjp\ddpp7:˳>@ US@˳8:?{CGMuSd?npr s@`۵uo0Ic^PuTc>նnpCMl+v{iE(йEjRAa@+fYkP`DxDzThG"[`4A;h\@zJAQW{k GѴkE]^`HAt?e`gFy[GfQyDUTT{Zh9FeewD_{ {TAkD}[uJ雿F;[Lޛ{GTۿQ )ۺQ;m4MִUFz;[OpWG(eڼpP#l! vUd8u:EĬyut),G&<|+l|KP$ŰŢjPeڿSÚۺ4,vњk5ZjO]ZV| ΋><\}$uv]Y V`=Ȋ dNǁD|UmÃuQb?@XM1pWm`:Kyn T|σUτ|@&F(,g1o0[Pѿ F@lE~ i""%Sމ#NI '\E<\ukM(="0& JSA@;0~~Ek3-J˹H6l,H*~)3p:`0`O~sļ-ӌ:LX]pՍAJnP`n2HȽ˙LxrD|ݸ~x{[H0 Jw,P}ԗ{ٿ G(U]D{Ù%H,@}Q8&]7h 4ͷ+NF ν=׋D(w0K "pNlHsj} O6'jʽ .zn (]r+g*5H#m}2kw4ݨԠZZx}3ƭit$% J=+׆&}N0A+E-A]׳]yQ׼W`L1 10&PWNIźTm|T^p`O0*V$[w ӻʫ%`']=V:Mq}ߌMt>X!#`m@m;.F$XȌnGʰ m;MKw IkIJC[.蘍e2Q7n`wq"2r.k.QQ:::;:; 3̏0:_:O;M?c:;:p:_?;::{5r#u7 8 t!C "hP0^THQ"Ď /&` IN p AD9Ό̍myM=~ŏ'_޼կg{ϧ_~;itksnap/ProgramData/HTMLHelp/Artwork/ttViewImageControlPanelToolbox02.gif0000644000076500000240000001461710534177573025746 0ustar paulystaffGIF89atįʪ̻ȵ؛Śֶ۷ٱҭΥΤŭָZss)11BJJĺk{{s̔!!199kZkksJZZJRR1BB!))BRR)19Rck{!!!c{{!!)9JJcssscs{RccJRZ19B9BJRkkû޵ڼ咋ifED650.""@?ZW}⤞PM抄ݤrlȿȧHG=:XSzrSN嘒poUUԈøֻľʚ᫤c^RZckgk{)99fb9BB))1~vsijƇtl`]گՏ̲µȻ騠|ɳ٨ռ7ۍܨ66mǹXs̲`ٍ[sm͕CW؏2ȥt3UcڛR񤳌ݗ@6ƥmټhsZRټټ5t2FfrUrmپrWR@}F,tH*\ȰÇHŋ3jȱǏ CZ(ɓ(S @r˗0cnl)͛"i\ f@s訠=L<Ў;gI6SwdZ`kmk$z; (z‚5vF\[jN~jU^k3Vbo)VTS'6[fJ& YgJb)6j Ż7@peZc9&s3'.;k-6o@pd_rT<@G:E.yh(]w S:'`VN,r3 9`<ߠ<ԌV%/K+>0O-2]7yc- HG7ࠒX9`GȡL:N/VbhseuiZ;r_,QɑMtq 41倮 0S2Yڗf% )3^l8 J2,hsZ3M@* 4D#*16KMSkNs 6$Cxn(o}K,x }貋XP.nL0- C %P6_'cr>pb`[*u)</P1G)J,bzԲqI̦(U'@-DmH'L}V8 TWmXg`U cl0@knp-7w˭x}x݄mw 8PM(G.Wngcg.:S v#n.{>Ǿo#PDC ?|WoC@hgt ipE&lO/&E;'haa/ P_^O~s&HP̠5h r 00 `0tYP)KkԦ,gIHB^խ6> X /fB< @ֶUn+[ߺMab\=۩ p_Uxb\-+&KR-K-@T$YJT#L,~ATZ֮lkc < YJ5kYR6x !NvmAGXKskj ({AŮv-/A0Jޚw C(V CRײen\Q%@ L_NWjA3 0AyiiEp[,$ &  !F0ՙƞ3d+ n9v!@tE`$A`ZJ(3fi?)# BKqzƑ:&5#.0]CxI)}!bD; Vz|7Pg2V [LxA )p;+8Aհހf b CpF>R[s[ϛED~t@/mA" P  =epuOsH[sϙh+G(M jA蠦UPDKF'%QҠ!`cCȻl]~;)0 eM #ĀB"\jd# tEɄ͡A-.*:OqG}+ w\EB@xVfctU!:P)%IĮ^pF{τ@K{MK[Ǥ:@wyeO{W{٤MZ@jWO{Lkf[ uzۃM0t[v{w+cdaK@moq0rP;o@;meChxbm+Fڶ˹DjmF[cV%T@b+@rKH$BKjK[FY B{Bg{LkDj{2Ƽ;;ɛW\^$Խ(b0dPܪۻիf[joK+;H+;WYZ FKm;Kۻ \RWbHt `4Q˫fM4•[IHɻKdԶ/ƑU\QEQGKЛФ놘~јT_ \F"ehpևsIО jGf ;=& Ō ̙x䣠fK0=00pO +8@@zղB{<-=@#(0JPRA0; *p ,̻S,cRYΤdǸ,+4 mXv,ֵk€yu7AJfL09x{gK~=K mԎio'J1J.|-PicF:KHm DRDTԽO8(k7,ͬF,F$O;1K0#pЙ^ M>٬ h|fMJ@Umx 74$;Wݺ[Q ȅL.Pٳ+0Gx$\H1LFamwx&ݘY " nН}\~$>fm$@=`-F% _tlJg7Zn?oK.vej怭K܀@|qjJv*@H*0I#$@--u3>]`n`C@=e~8$j&Ta۟-NFV :4(@,'P8H7N~JM<<(UI P'`79З ;p!=sVсЛ޾kܭS.1202@( X@jz5nb7/Yց̫-,P{uP+6؛~ߴǪs')V*`I- ˰%<$0lK6m7l? ʝP@4o}~zAGiFNK=bhNKi${v ˪/_tB՘8)E<;؏ckٿ׏9OO ;; ݿ6?LO;lӸ  Hp4C #BDC&J 2  \K.c)L8e S惟 L[)ߡ\71}lT!EKOd"~aqxEIT_r$C"t]dI7-\sG lRBtH'L7PG-TWE?\w`-dmh=h}7rMsmxͷ~7܄nx{'@>qKagwzzn:O4 č@ܮ~ώ|;.|nD/Wogw};X`qG诟>o??^$g8B:'H ZDH= Ă7BЄ&,U(| ]xB08̡ q0 D& @wЂ&:PH*ZZȂ ш! F2h<h4o9vݨF%j\\X#f \HE6򑌌% IZdd%/IMR %(Ʌ,n_4V򕰌,gIZĥO)HR̥0Ibdc UGH0fa/`jm 0(H5o\4/L\v L8re>mL^2t&4o9dx '+ 3 EM; &@ͅ6T (6ʇFԛ.>kyel-g9 fhPr F-z1%զ4Iңԛ@e8:ԢDg/\ּ*m: oue\[Y}U QuSgiϻ~}+OgVҙ:W]YW"4?Q g^CJYVv-ghOSC hYqP@, ^JvTūTIYm++¦3JUorVftwMۺa5gnUH--;V t]8 QŬFjYfT kn _|.60mouKXam'La֋M07k>]@;L7 @IO*-(397q"# RX L"CM1 )P#;Pv2|X#:Xβ ZٟJL2hN6uL:YpV>y^MB9b6MhDѐdI[o071SӠCMjIԨVS?հcM:ָ^sk@Ϻ.a>vZ3ٱ~6[-iz1 l*l[ٶ ogFwmeu[uv=fz|ƶ 0-0A  B~# 5pgy18,q{70br6XMm|A]n@ @-s|Yn^pԠR9 }G}9p:'.v;C}Y/ 1[׷HBusv5ḥ> F( @Wohx WfHTv( xm8(\~^(wX~88Hf>XeSX(ʸ֊h HHhx,؍ۨf-ȉ؈،8߈eXx؏x}zjWvii ֐hFiy릋zd&v&}vFo݆{Yjxvv"9o,j)er| zM>@vg{w x6)Ro7rqppGtftHtnH@pWx7tJvWtsVsrǐWDIzLgxVn7ukoDyyXm<FzgOpyuvwi3iD7xhvx߶WeIwGwٖy/ijsinwq;zm_{Hsgx'seznIuA[AS4[4;;;itksnap/ProgramData/HTMLHelp/Artwork/ttViewImageControlPanelToolOptions02.gif0000644000076500000240000001257210534177573026607 0ustar paulystaffGIF89aį̻ȵŻƶܲԹ̤̱2;х`;:`Ӹk:ӝQ2_]Ѯ}2_Q;_l;:2;_2`_Q;Q:l1Q2::2`:5kQ_Q;:s<дЙ_P:}6j́˪ʶԶ̯ü˨ů׸잜̲Ǩ̬ڼՙb<@G.WnKdO4}ڦ~ꬷnz;츻n>ﰇ87G/WoOo< 8/I+|_~槏O+t$@L:`?,.y$&rEp'4 Sn0\a&X,8[@ H"HL" C07̡'*ZX-z`H1L<wQ$ҸFѹQ}@d"'IJZ򒘴(1 (G)Er{%GQ d'<1Mb D%&+QQ@&.iJ5v(71]ƒH/arAMpšvܦ NJm 2!Gqs,p L =7@M!A @) K@s)iKg-P{Ⳣ&E%v7q'KMS+u+]:W 9ժQ,A|ڳͯ^g]~BVJT/KVz\/`IaF) Er#hQ u;-\Jp' ׸6>q`oӆR+q!Jӊ8GvKbxP5Jۋ*[4r%éJT"2pf&Өe{=Ds,39B3.QR#HBzl8 ]]'}JK:ҕΡ/QDו:ԙƴjToԦ4g=??εw^SMasNfvMjS؁^c{m.vgvMoGGnwq~{o]\3O8]\L@~@D70;x 7h|~0L}H27>9ΕOGAyy <C>(XԍC&@=иjއ?@z`u(PC@FG?0{2>0){{N]h?{8ocn@_o=ax|Es<|uxλ襟eC"4'm#|3o<B+=@~?O}} W_O~.sL׺>g)vw7>׼ЗZW}]zܷ~gz6}~Ci'Gqxz"y|7zy'zPw|y%X{{1hs5y2~ǁCreGtT|6j7u (wdwuCDvPXqwׇqau~3DžS{FhL@tXuH z]t-7rFtfGp[nfՆ}p{x)'f8؈Fg(8XxxphƊH8XxXn֋G8XxXʸpxИ88Ȍ捾؎~Ȏxx؍R8ԦKQ yJLViXȍlIT0mLpS`kCIɎbc`MP9G XXlL>)*gpT`#Y`o@CLfhj04i8l aVThaHLeUIP'eJIikl@_a99lX!9ryU`'z9iR'I~Ɏ\G06kJ ()Tz)M`V9$g]xm^`#mɓgYTVyUg]fkYٓ-9dfkw =iv)vIޙCٞy9&ߙkI@ɠ3nipMkH h%' آ ʚ(331#3=ʣ>@:2/ڡH3i)hLzN R:pTZvXoZ֥^:o`6dnfzjlڦbpZr:hZv|jm~nx:iFڨ:lfjک:{JlZڪ::0 '`7b'ж/еT+f{dkgh;{ K_;Xk%+K˹k%*@T;UK+[=k|klj6{[~`na;K;:;8;'оIl2sKӫa 6klKp]hmKW[̶9p^K{ **`pgK;,LBp<\-/|.0 -23j$;@ Ʋk 1/c~Q~>;itksnap/ProgramData/HTMLHelp/Artwork/ttViewImageDisplayOptions.gif0000644000076500000240000006157610534177573024624 0ustar paulystaffGIF89a$  " --,//0-6700/7778=G5A>;JF=EX:Ny@?9CC>JJJBHVBRMIXUPOGSRNQQQEV{Na\Pc_Vif\rm_tqa^K`_Wdc\qmZws\jifaj}gzypohvtlxws6O=T>Y?]F[C]A_M`JcYhWl^pIgNlRmQoYsYu`ncpiwsdzc}pi|mszv|mjws|nw{ƀ}juzimsx|ǎ̖ɖФŪƩյʵҜúȻѾѼǪůҺλٱȱӾݺý¬ŷԼͬμչ!,$ep N8x&@\H1"EZclj3nHC~D*W(eȘ)=rΖ4$`24`@&J?x 'PخaӪ-vmZnKݹv˷'M+l 'ʤ!4!JyOKhÙ3Μ52"c@vHCG5^ށ:HIhFm5׼[WbzGq6no:tAuֳkν׻cNyq qy]Vs괉` v QmK g5H(bd{dC`Cj6Ć (b  ],@{!袍х"{67DJ90c9#sM:ɤq77R6dNJ٥YZC;(E"mCmLxB> t!"Iۍ7 xJq2F4b2 dg `\x)~,Ih2h7*P 6ik9+izB*#8j&kò{PfmJ1I~k+#b2&,poB*CȊ$o1VzliQ\\PЂQYP8  * .ʠ-40, 464xM?l ZCъ Z PM=PmBǚϯ!Ѻ x*1H|fA~P({\ E.18 | Ls˩B,ؚ곱,AmCY 0< zCa5wDXw+.8;8 9<,x;౽q . =J`ֲ<ـkb_4̂TW%`v v$YlO]~p! U+>#D^P,)lr#vk6Wn,o E # P ؠxA(€S 4ÆL' _@,hA  + >|A+ЇC|zB,P > Zh!]<.|EQa6+Њ.0 #b*+d>DtKjmuKf+]*zЊ::gwJϼ(?,|CS$ ~X i+X|ނ\-_ #b,bNV{Xd(}*Y39MdA`T)DN88hhâFbS^4&iM S$?H8ziG,, 5C@3p ?h:hB+i@ D(~Xa/"7!Fbq uFp+<)G1:q")ɿxѝ e۠ƿ Z9Lu<sRF?\& qMtӭ9Mq8g~F391FM1(Z wD Pz-aI 0a2t\Dj@?xRh9 Z=MȦ?ޮkC%d.+M $ń@=LB %(Lw  -%; N`| 7"p'`\ xA(9VwAC2j^ >6gK|$Gyȧs;[W&& /kq~=KE (йPiw̠5{ ?}?=xSy{>{'!_ ĀO/> <><>=a/#U7.0&/AL |0!ϡ )@ R~????7~g?~77~'@'g'R`~~W"'gwo7"  Ѓ> B8DA FXDLxJȄPD8HHBEh?x=H؅_p eU脞aȆNTMC(tr^?x  88 h 8舔8xȉ 8XP px 苽ƨ،͸ȌȘ٘` HX؍ר޸H؍ȍЎԸɈ e 娐 ؐ  YIɑ Y%) ɐYY$I#95i//i,;BE)Hɓ5ɔ=I 8 p a bYfyhjlٖnYdq+I6IxsɖqoXi Zp )fYRb 9Y )٘p7Qsْ8!M4ِD9ٖ~ɚgyj !eɇf8yd胪plZ脯B0 VЙ>؜ɇ[ن_XY8Ɇ@ܙhْ|ɞ ip PX ]ie J j H tlJp@Jx : ʠ*'z& H.:.jj(zЙ{ٗy | X =؛RJIGv H n0 ` @ c*a:@i \P$2څWTZ `u Z|w 󹙐r)JZ fك-2ꢆʣz  @0 z 'Z= z'ʫ(+4ڨHZJB[ 4ujꚬ j:jʞZZ:`)*j+Zp ح* K  ` ! uJJp Tڪ4: ZШ{ .Jj5` jp)ڞY';Dꋟٺ/tJ : Z :p QpK}Q$jS0jg@S ~Sp3 (0k&*)KFyi !YaYR {p 7 pPlKrZA[p*n@ p`y@ =xbSP[l Pʫ 𼽫 t0 lpp p06ڃ@ '\0 (p6zk@ !y[ pt0 @ 0 *&{9*;D:JZ8hܚЪ=x "0! i S`R "\p kp8{[nS` "p [[@0\(PZ$`ç  p RL6< `Q@ t ~k etڱ'p 0 p0!PÆÔRɚ [pt `~@ll TY;˳ ۩@  `l$\l(y!kp |<[`tp\,\0*P\`u|t <Ll0 1 tk`j`Ȫ0 ` p!PLj'pu L j"@V0< ){,dXA>}Bm9ڊ ! @0r\@vU~ Ĭ ЧtS *-]0,Q$ipL $8]`p p lPl wX = , {u` Μ k(V@֛ t 0  R{$,ڹKڤI 6z Rp'@l@@k ۖ T[P]( (@( K)།;TMߔ  }S k@R [ K i ]#0+˺:$pVSPupuPKR@=`@Д6jª9 ܏ʙBz > ].j몮;*j@Nn3_n0-"\B^ʗ@ `HX5;v; ZK5HkDM dk*+ˉI@>b :p/|M޳bͳ- ث縚*z]N:A^z^˕`k)WIX\z tՓ v6m0l60.ʮ`X{XI<y΅Ixk ΈN_ fiLJeuاsx酰@ 񝐦P #O v`y:$@_yA?V{gX鄪И {` :C9ګyz:WoXϣZ]r5[oP>>Z~Oz ~ŽV?mojxotEτoCKyTX D*|0I-I+^_FƟ!+ ؕ_9b/߳ʟOj/y.L___?J@0+O\aS JEPƁ9vǍ#U41%ɓYtRfL(i\2̖7a sAX _b *Z!1ūt0ql:050ÄʿH5)U#R+v2 ӓq[԰&dѽZsMȳcӾq0O$׌PSS,ŕ1K.#qP]LY R TJU. R*CcCt9RiۦMv p3BOgi}P6&`eL=+yS ĻC0[47ZL!30w\kf`=.VkWVUPay:>fvO⎵yuW.W޺"MR޸Z.9ԁ0Y:Knd7ףZ߂k!AHv<)!xU„5oBgDCvDUZ:!)N,UR?]յлeVፕNX5:( HT:`JDI $W7! 7.;W:v8TTJh7׽xJBXcÝu(DJN57 9 `G =O|T1 p*"q OLb $@I0#×׾t#jtP[N)pl )`S6'R PH@@ kNp(Ha\AM! HPxb nHN5@$`WQD:6@" Pp PpPbY K\`{)'R09@ )! .H [D'3GNiZZޠYݡ$,@4)>5HuCmE?ӥT6Q_:ԥ_1YzԨKiR{׻]k_ҷtQR3 nib+ mo 7lk7]nu6w=ovl^=tެwoovgn{cx.o}[wɝn9 \@ 5 UlIeRs\8Ta[8,Wl/y·r\Eω3]5ԝn\W͉X\HaE@4m<@{>wo{ދ.|?x,G*IDa@|c DşC *K>>S@9Kk#N.t ??O?.@̿O kNȄqV (P 9;J0lTA Ac9K;AtA LT <X@S?VPPC /2D35TH2TC1B P`C0C2LC4C8C0TCA,:) N0+A GtAT:$\HtAHHD=NOL>KĀBPBVȱW_F_lAcDF; LȄN0F+TD T";c$,, HAcW +¼p,?G - yXT@R29ɛ6VWDHWHLEL.AKȌ4IQȕH @8( :LFA,kA< A/Ȃ.p8Bq̻:8Ȅ..PĀ8p5@HE8ppi<0.%F\_LIHdAcVLFy><l ;iD{;k9N8PAЄHX L4 ?09G#fD4C()H9Ȅ;%\BbƏO K4@Li+8岃tİ<88N FHVHW*X(-O HM?D{K劄+G ĀCP .q :%aΟsU$KK (LsYࠄL8,Lߋj<Ҕ4?SF=L).8J:C(:;+؂p:0ޣOLLL <ybaYNDȼ lF4O{L|8X00MI<740Ϋ$VP8UXٍ({ V ٙXY̽x;侮WKTTԸc˓Ȕ,Մ5>OȄJꂈآX[۾ؓϨ#L۽ۏMGXp- ;[ܼ[xp~!Tk΋̖D p֑ĀyLխ]OMۼ؉S;ޣc.[J .f<-JT ײ\ӄ.prT%Tυ;-J%XdH4=5ۿ`<%1?&ENё)J.(SaT}Ol;Ux)N.ж# alAw]T`HSDQ ƭQW#IK>K=]h|Okc,Q&QyDG3PGODc8VG>as\ӷ..x;qac8uEU1΢V(HԥΕ$["&bO`mݥ]=DMlDJTIL9O#f:X,p =[t W.KƠz}}% I> L]Pd^^F<I@TLS5~LRW\w;vDeJBFKAɈ!@uNΐ4=|>QPh47hɹdG,QDkYfcc -^ƹC$|M XҴlW A!9`ɛK]PSQ458"MeeLk6@s=P䃵FC|bS# ,h` Ғ2\K` ѫdd fNUoֱ5΄OTNOOtmV?@:⼾c H.pz`H,_maD|ұ0{Nj!ym̯.ZQ8Ռb7[H(W:ٌؘ 7Yu X7L*Y7l(&<$Y>6eԲpp9>pW^KNNWHԽdƓ>R.nPN;I&u]UNDXXWtzO4DcDi>PHX k˳A^!BclVDAβm%n0DYT?g~D Md3Bێ,0Ь7ꝥ鷶kL,]En/>DINltQ\W].Q^kDV;Hg N/V;HPx$̄J x%A=9Jj5h Sp/uytlbYTffX39ld_N$0HPD-bVX#BժW7F2@S 0L VY&0\bD2aZ%RY$,`@ TqİLXRZʴi*L)  XBթbU ye'AoiVfj@$ܿ{>@g#WMԫ]%=%͛;p"ST](U exQ867f2DN~N<),]RN:3՛6fG = 0eNH!0(8XRaVe K+[v*U?>ݞv[^ a}XxMA6b`q $DS'uIn`Gv7&Wa&1R օO@B(q7PH݅Y%B9Q0IzAH !TSQe)R Y_Y, }G%YIX.t@^>5%YvJAEE&@s `dbQGDB~DboȈAbhĚE\!Flщ8jvvAZwv!jtc Vg,bB)^&^D@`}mI`u~}{g}XS,,| L%r @/`$`Dh\xQ+'Q$;,A :1b|FifG,p G&^Tft +h9m KVI\vWlֺgu )|u/EYr+0ɡ07uM7 R4wpU,=\ $ A8s#, mz\5PkV\TlXT*va22lOɗf'֥Fdv$='m߼GіbDNo߃C'9MVtp'54+?\bs1k2"x<5PY#U<" v`E'j'O2p0 ~&) '_sUq7 Ǿ|͆t(@Y[z`+d-pQCuB&=j[euYB ~lm/ )MJyRM# Bb5dez*TrD ', iIt͔ $FKNϝEj"#WzK^洮w ^w2PNBLġ ^&[t1b\a䨘Ŵ8b4 E,TO1ʴ*^1]s*b8Քּk h);" Raz,HHHda;niH]D@/JB L` H,ĸ Q+a:!V)P\u0haƦD]0+\Fc,$0J[RsbOD$L\P9Ӟ)|yU.b:_ ؿ ՙ_k2 b#{PM@d+ jTd1!xQ Xm9L嬪d.KD ~'UfSEΒ g+6:uu ,;hWܭw]aѡյP(⥮]a׽uC|ǫVw;SGڦQ$Ю$/FՏqmetMH'jo@)u2Ac5(3ȴ4肩5 -UPߔЦ@찋MXjWWT2 a%#a 4MdI~rf;x  VX͝@gA"0AR`O )TJq?PXA B) Z jHsk,<|z h,Wl4}WVMs:5u[N,LaI R؁ e0* "щO|⃑ ++5*~mXBnH Ӷ"+%1th[Mz+Y^S\ "2ŅNd! 8j#uv/tb  v ."4N&y t|SY`Nb6& [1裳3EmӁY밅b \,rod FE &@G#Z )D u;Ykz7R2DZuKֺv э9E$V` 0mXyҜ)p v6SQ?>/3W~/S"o*ޕ&.8)ā[Ȃ<B38L$r^A~SBtB3*+ɑ\X`DFDR+`+X r F`fDf`M| V v r ^ z  ѸڴF"2.j"':a/F-.b,Jc+-02ӱ1&92΢-N#5F#,jc;#"op"g7s>]+ \=)?u^6x͚@O,qlAL*$-$ց(lzpx*<)\BAHl*D`t칶gi;^jSEEًg~_*U8P٫һ\IH>Ѧ.(Ubcd@%lt\,,%4@|%D%$| '|'\¦gcD=s s=˳=3A=tAA4=3?C{'Yjac*i/)N$pD6!EbX&Fd̴LtdK_E&+tNNNP#5P5OӄP5MׄS5To0G)X01=y-/=5fQwJ\c=)eQN_+ٵ 5aa/b_LU6诅fKt0ЛR1F̉6 9v)aq]٘d!1~&^2u5$jrbXk+h 8nJ1[hv֧4nh[Rf/#ϢLEXEzި܏rB6gΆhw_V7c,ED#8$fS{HK'_Dt KV">JDyaSpbwhKhron1-n Rs<+@lWnDDLB]uA ݳFM%_ MjM=8VwrO5.k6 -* L!`u_N+ TaBzWH&D TPy'Pz *eI@DYV8^NeO_3g_7ndtAJ*qBB'tADPTD]t X,4āթApAh฻|X i'dALAm , }:HܼXQD;iFTqE̎[t']" B[e` R,l@5wșq?^PO\?*A ZrY(imZ÷YxMM8%!2pq.g¸մnD ڥkLԁ(=I''@'`elň=فޠڇMl}I :|+\WZ7~?)JԪfT5m rv8 7'\pyŜ[:WO,_לTOe@t*;2e+*qdbN8>сU'KT |r8Hdj"/cl**jJ&WX"(T2c:uSH_AjU Sb*WUb@ SM 0X:Am֯nƥzKXLq̩+.a ʈYpᱍ!Gx]._ѴW'R@KQeUj)Ko2}:Ԯv:klTB@nW߆z Kr̿cbޜrXRK91yVʪXZPiSPxjl1D7p\7kk/ێ#L˜R˽TviT*(U>:rZ/)⢭-󭫩,גM㴽+Bl"+: ˬ&8ȟ2Z05ZS F1-,1.sМ 1k m*MJ&>'CR.ZZ(0(:=Ӝ[E$n?2@lNL))Թ%a:"|P# ŝ;% R$UJԶtTjL@LN:)CN9V$ 6XCK\MOUnXV`bx/^2TU^+Q`;MKdcfj୶0q{6Y8ߪ@ToOV - 2I!Ìk&:E9 ZaȎ-̎-`(Vi;-󊲧B=V(%TSrsGƺ@7M,U!5B.RȤ X$O`T`$x;$V^T2$VG:\ 5\HTNT*;`1\YX*)H@>)EFRZ3O7>g^y>Mi ~y䵏W羀Wi; ")(P^Ce S  )A)b ؁"- R!YHIp.d D̖Z rcb&.1S\&xFy5Ή\*QKҴ%A |%T6/;R_@Ѓ),pa _X.D"DЗA:Ԏ^j XHĤqwП-ۄzB+q3ɇ~GA3j{Ϡ Xcisd̚=g2 dI* @E"f ؀!(PH>f B\AHY b19t$&dE$V~(Bz(GL D Y@HW((,~^ `t(mѣ AL < N¢Se~b !`!̺PgDocIG%L*a0NLA YE8qUY` Ev{2a)!tHl`CEQuAY".0Z5]nhw1QJ)JaWT;Let"E=c8ĵ xYPaiR')%:q #ID 2RTULw=]s N\פ%&ڪB)U/aQ*5 xT>!ŵ6\(g'N_N0 %i E#)HAV40") W?(n~ .Ӟ=C`A:,]Tv.d~WZ#m%P %H,a%}(U:"L! Q%⃃p"tS & uaSHA XE)( ?ϬWI{9.CVR& RwdYa@ Ep85$[@&$6g)8T_NP$ Jd")O թ՜fE(0koLk}T 0 ]L$R?6u *`7\mv`ׅ?fC{ ?+"!NY#5t* lvǬVϻ ys˓6AzJ:Rp+.+ O|0n`$ppOg UPw9%QqXw.d<5.]g8O/jxIDJeBKgӝ OUY7\ׯMjRٗn=P{[s_嬳q* 5Bh7y)w!`/<1/yя^9SPW/yͣ~}C?Sh>C=_{omaZ(}MAiq"/~m;rX/ȡj/o]'I|o&Я>gUtr@GKأfb0 pF%Imà3X(lB/GKoUJ!Pi0HnPfPU|)J hM0N܌apkp Cp y %N~ )=Dfp/I0 Imp P1 1 LAXzeplgmkbX0 qP7h,K  0=a 5qW go΂_bL1 Pf419QoØqs5A`q 50/0%Ylz3pziDq4P- quQ0qѱr+}D0 Q# ) ?q"7O#ͱ#򈢤<#%q3LO$9$o[0#52g$I2^,7r)1''1'p*_"'*H+4ưLd$e$oP cj rC co+Àp%1G$.$ R+rq'2-%S/WͰ* r21/04+2(r,rGW+-&92{2-?S7&q1s)mO8iΜb,2y9As0=4)2.1{%).Q1(3:028o5240#k;33FQ~' .&S1us:u2>i':9rA1<ӳ3Z=6 FKײs@a2BQ-O =sUS5._SEl+B(m4 ݲP4! 32Y1; 1suq8'GQJAU4$G625 8~\sIs>A/T-QT=q)P>&KaW3 JPAHt6?SJs}u?QQ/#>Q8U WHs.?UBi#`$S2NAs;;{7=QC V0-G"[T:!;q0AD55HZT\bN"4*}5E5J˕K%O56n:uGA4_73i 1/Q!_L!Rj4+Z1v__IVQdOQ_Lv3%4?4c^=[5_u4BM4hw>P/aqg5Z׳iE5]uaR6^UX#Έu4ң|W 2L״W6kQ$e5W m3.D4uW6GV g2p]Sݴ1VHRaJ0G7f71/i%QbUh EImb9 9c3@VSDEl˖h6jr_N%%RrKt#W:mW5DiPKVNUOmWmpK7E?5n8\@(}`@45`͍?=6s\ J͇A\" 4 a _9yV7DAy8.s)H]͑ ݭ] Q|̻}/}y\ ( 8= `;<1\>ijm=YA?ٗr[z~=a\ ŀ4޿s<;ü!g|Y=ً) ϭ<=My>Q)MSC]`k\k @ Lc{;G 4;9ƻ:?%)d6KtNDyٌǸu'   th߰)g;o7O}It7ُi?_[} V%:vuVrtŸH~BSj(… "l!DJEpS;.QaJ1*JXN2C+VRR ֩SkhO&j(ҒK"-zthSSFM5\z-+ ƆzVɟ6} +رfr%NX\T8c*S*f,5ǎ>XrQ:9JGWYhL1-]tز+=:**L7W$8LT \yrˑ jf4SS?_z:Usl2^YS-Or+X)r_u=7^N} ٴNO5uUfisyx߇x) *a`w+6ވc:x*< d9䎬$B+OdIIF#R~eW^Y`F+Ty4*cɗ|y|fzz#c&(jh|x 䢕B:xJo9_ 2caIJkފkk^⫮Kzlk5)BXXNl{e 7y $n~{뭞+n / o禛o;{L0o +//ٲyq r*r" *2ʮL,r8"3{b)H*sɦlt(j#1N Mt;-3O'4P LJ_lu}6'_&O]st=tF۽ըqzZqG'uqM--4y.[}䰰RnCyxzp~zԃg:+gYܾ4|B ' /'o}KOGωw'g<ؗ ُ6¿>Y\)s8ݭ}/3ܶ4A(D@& `% 41(nDv] lڸtx)d]p9' "E "`DCFq@!"PP"EqAaDArl]tÑZS c48j_F@{# @(| dGBADHA&PTd GBڗхx#Z^?V1ȎT MgV hHd!IEN$XIF*P& 8N>o'C EsrSKs]kM D".@SԢ!ʉD99uJ d ?QvUEM N*:53uf)|8Ds@F7AD"b ` 0QS3ݠ;A:ҏ#8j5kD+ lH[2#@nP=ACBp_CPC $Y2s8Vӭbcc P VO_2ͰU!,7OB+iѼnghVvh@B@#p&H]/Qvt]`O:MY/mM#P{TV/FB 7TmPA鱋j6i; (z/ @ 4Cz4gG20`!14ΜX_Ɏj}H i9ʇr@k0R$b @ORW)uo[ )4N0e~؊pM[W -61(7ufnTUu`U::3T "^T"E'Y6H EشADbah (n„xԘbbx7VU%zԤ.OR+:դ&)+eYxn[\4޴c]F"B& jd;5A=lQ;bʎCBT`y;SnPax _R$BVFč9H" &]췢zKbݬqW8cVxIa&G0@9 GL|9Wr|v Gؔ>òk뤺UF[:חXG;uwHT1vVRm+IoLƀV:nUͽ_-Tzs?:5a߄Z:Jw 3_ӎ?]Q+~s~ X[މqq|io~ڪѦͽHr:I_ufVءFcX>.Va sť} K` ?_v/`U'/}A/߷0"*(p GBeWdwuRu#(%4 06$(Xw^%&%d6uH(8:H#Xvk5(6x8ȁ5Hc$LXvN#4Q%:8x$ )v@; Pgheq q q vyhyv|x(txȆtxxi舐hxWu(jHj8 (@؇؉~Q Rpv udCcxv(h(hHވHh(xڨ YY ӌ038-H6،(9ds-Ic) 9ɏ39 (yyv!y 9)Ȑh Ydgɒ-y98I" ) HI ":!:1!K P RyUS\J[iSQɔJbiNٕgI\ZUaYwNYmٕbi^ٔt9OiYmID;itksnap/ProgramData/HTMLHelp/Artwork/ttViewImageIntensityDialog.gif0000644000076500000240000007303710534177573024744 0ustar paulystaffGIF89a&v## *+)-4:00/887///<=F2?];FFu;>w9HA@>IIGCIVIVVPOMSSNYYY_FXEW~WWk]]uMPdVff[az_rqa[N`_\vHZiHZhUjed[pm_jjiff|gwypohsrmzyuvf~=T>Z5L?]FZC]A^NaJcWgVjIgNlSmQoYrYuhigvqqvzdrezd}5_sy|mjwt}~ov{|֭)/i2eJ*x˗/ aYU̸ǐ#KL2ŗcذU|<[X.bk%$@rɞMNxp#ܿyp#G'>~"}4zɉޡիc1>ۣ/ﱾ| v_|^ǃwQtt<֛M t7u>D1Bۊb[>4ύ77C;[BR; t7<Ãn?T鄐NL nHf=HbbÛߜoYgz։xf)螁'f|F(ffZ:ۙX 2w=F4e7vK98zc.2PA1;H7C") e>ɃVݴ:ܐ*Ae:l-0r9%]n[¢;;;ejdf+Dnd r>- M~7S-4l7| #3z:P"50- 0m zۊe]:DQL1xL:8˪<˻] 2-l߀7 .v~[y .NLQ Ã=:@5#]Ǵٝf\:D}.yĺ:OR7k1-DrO;j 1 `2֩ñ=ϻ cm ۶N6 Z{V OpZ?#p@QP:`.w,rb,P` Y8aN@ hLC: !,)(`QbE  &z7BZ#? ȢD$QBaU0 ( ,-s-kQ׾8oȧᗪi@V.HG;Y>n!s9rA:K'>D|?juCM|o N&aꨫ; Ph1n%d @ +؄N ޑQ,rV/'Fʃr|C<12O2r1bgqp" 9PG&h8ODc hG\iΑHseI6ouSIV90%V9eVhPe+Z*또u2mq {1<0i(|"E.0 6/xASU 98z Y ;-F.x.6]v(STAr>0r~FcP"<}?#P(QMȇ n#p0^Qpղ++҄'5FQm[@B~5*u<#gVm}#=yZq~޼Y>:q~p%JRRRR5$*n B sd[ ZrK 1p*A,F4bdd t! M."pA0q@\GFQiw#[{ |-3t}bK2rUujMT:EP n#KocˊֶQ-P)øq 8|]7:۟vӃ4G9N{X?Ⱥr p/#"XMPHޑ{Ǒ{> {H {N{F!((N@ ǰK6)A6X*x9L6{Eg'{7)1|73xSU38  x3 QPɀ l؆Ppl6607xtzx~؇~s(XhxHz yXK{8A0؉ȇxȉ~؈z0 @ lt_E` ) ĸ P -Ќ-،XH8xԘ(x(,䈍蘎H(㨍xx؎ x Ř @ #pPP I(*I#-0/)(9. i8ɒ6:!"45 0FYK DٔB$& 0 p``bydY@PlٖlIimycgwiyYhy }ɗf irIboIq٘a ) ) )ɛ)99YiٛɜYi yyIyy[iyYʟZy ڟJ@ə I9 J%: Z&)$:j%+58J;;::2'J:?:AZN-:EG0:@ J&MgڣJz:*O*2ʦRjFZhz)@ʧVZjw {ʣIzx,iʟ9Z$٦ڣh: :Z!A"ꥣ)꣕n*q6zyڥJ:Zڥ꩐ښ!ڪMz{jJjZf*ĺ؊L* zm*tZ:ъJڧkTڧz˫ʱ  :˪ ʰK*š,䚭0ˮÚ*ۯ Ī;Jʪ"~zkʣ +6*zS k vzj:+h[$cʬ$;ɷ`0|Z i": 4㐸2;ະ`$` ;۫k*Jp۰{KW;q*0 ` [` up@.y"00  ( ~` ِ( 0 ktz$ j KGK= j*I ,p` ` 0` ,@ 0 @ @  ,P|p! pޠ` J0p`` `*L 0 ,p@ @ Ќ0 ]; ` ;誶[{ګ:{1+WJС[)Pz- &+(`@& p0`p( 0 ` pE<  ` +pjܟ0N<@ uĔ`L k`XY  <-`CŤ@ @,< |K' kLʶ7Kɡ: :y@'plp0$  (&`+p -0]`W P͖+@`,` T-|\ (``'P+ +l` ^{}gzY u 0&`” L @ 00χ0(K Ȝ 0]` 0 N0 ˇ@Ͼ@ `P<p@ k@M00i\ L` l `M`ج˓`  -{\ ˭#(z[ׄ ܭ`pн( 3 (Ħ  PP T  TܰS 'ƛ ;nP~ܲ >..vdJ󛼓>,J^Ψ؂ ,0^`۰+Ͷۺꇜ{ف^)iɖ>žYky˾eiky)`)jYΞ9v)ԾNŞ.I..~<iDO o /_$_ o/O"?*(()1\ N޵+*jJJmMSOKJ߯{⪨;ީ| >F Ū`pZ ]J:$J}YYv T>~m;<(+ؑ-ʤh Ox{Yјߢ ɺϵX? =J/j¿B{ܱWΊ+Ȁ߳RM [ ?wP!‚:lp!CRbÉ;2th1dD"n,IqI%8RB/gLc͋}L Tb==>> 7}D] ic˜,c WXA^=;"̢*$ۏS5I4U+$_cxm! zXg5J,֌ = kVq=~%gɢU/,C߾v fyĈ56ݎ^ =5`Pž;ڽw?׫Kt5m-|Nj/_6O%̺ĚzJ‚^kn6r4 &bpCFC!@0)CE$U$1 '61m9iQc FK\?CRjp.4@Jq4nz+" SM7߄3΄!rl",j‘3PAΜm0/g) *0n# -KQ4pG4TQo3T.>l4UHpFuйF29qT1[ۨKźSUNN$Ve4v2sHJ2u~tYmqDղPćK<ܑAj0*ݭɞlAIvQ n6Ē[dmR* "SXWVhE Z1Kp.*#D ƱbJ3_n@ĩ)DXvAs(,0%!WX#YB, ^~M&6$mn>X̭`AV G%7 Ї+6 ƈUGz8*icC:0g%y >&`ҙ>aT~h+H -$f8 nRdPCaJ%R` L8S h?xBH>V6\MD5vKkH C,y~AbHlh>9~=~fP KKtYli8p{G;R `j$?J4};n 2WFXCLSI =k q6/})!N&j:73%5&s4LB1M-Y@Dfʬ0զp5ɦzS#+^2a,SxC7i!cI 8%*']I$ծʆĩ= wm0_-hm7}%mh 5m=jj,L&tOJ y㴤ukEZӾ6nj [OjN<|Bbjkh{ۺfZ+ v}䶶\cSE5Hi+8}L)h/ Wz l>%VY&Uv [x2 E*bROZUbb^cw'NJыjbKթ,APieemn{)&qԱ[wIU7Rx-19Az _27weve}E],|^)BYZU9-:*eo5W++miff}6MehJXrł^͏B1N`=9;~GCQl lbFvlf766}lczmhˎl-nik;67mn USi-\awz-p<Gx}p7lLcۍU꾌&tl}T6)m._nI~x.s5O7娻*UJU_oη{tB+Um0өT醋G1*#E{z4Kgg&?Wz(~f1f]]5\qF铩Nw_e3^Z߅v'[$.9W%mo4W2ߝ{=褗NW?3ZL~!Ŋ\nibe}OL+\qfO_9 G>Ϊuȿ~Z@{\w/qu`'?x=f^=W+ӕZҐ{| !#2r:É⼘, 4b=:; )Ӕ{h-1qpa@z(~H\:pцr̗hA}`9,,dk9a;0);s?kҮ7cðCSqHR'{H'|qp@q%op Vqmy| ol(lD=Dy0%t O$qDGB VV@TxJl py_a' 0RQ øÀBă؇  1 %fh!bqh KCxDц @ !@|4 p0oo@R 8`};JT8{ITJ<W & # %h9dü˽4W{<  z8I@%*Q" ؜ }h$V lpI` (~ !WR0qKV@sDh CzICؐ?$.D؟Āv@ \sDL3 3@C /oʆ8I`V0v\-Cl `!KSDJp[a 0 8 Nt Ld8xSaL iI` ϰ4Jx_p3@|'`*7t߉3HJڈ91jD U}kl.8D]U>BcC#fQ?S .*r2:W4rdׂttMx|-V4yWaWEWWN/,=7rXxXXqx؈؈zXYؒ5Y=Y5ٓEٖ]ّ-َMٗ؍YYu؝ؘYٍ}Xٖ%mه؞ZE٢5ڒA>$@-Rֲ:₭E.ႭZۯ%b-۷E׷m۹-m["[*[-:[u-=-ܯ].:\-\][@[#\`:yKḹCP10٬Sö2[=2M{>ӵDq5;?ss]9^#C.+@1^ b-i "1Aڂ7"3_ 5V%R:.m-^/C_Y ZxAE#1d֨\1`s F`Yl>PV溬CR y֮UZBQi}i "F'n`%Ƙ(6bh-iq⃰b)^-.n|%@c,^4b"0yy:cQ۔Ik ,cB`BE^d2nD6dDGfH.dGDN0FdJd bI>PeEd)VbPfdT>eTF|dSM>d}>?2ɨ~/ Pe8$7ifKg i& i kٱfhno8ymgqfrf>hHh^bi"C92Ka/b|HM!hR AhK&hNh!hf胮腦nh&萶ބh&2hhhVh&7`Hn`=R뚰) m^䵓*MꪶꮮS&F&8HHh3{"3Z]) "6y3XEx&egNFdN9!p4X@gh|&?^a6> 4`h`2ȃyH{}eAk5gmfަƪy3`h,b蠾xk٣H>#qL^3z w@bpec} ~֍cgX;(;6 /ïg4L޿ e}'w{05Xgn@_.AzFO 0ynDqprgPq@7`3m 7H@B@l/o.yލ בȎ4ǒqHT~X 'PJŚ(ҤJ2U45]/EΜ,5Jǵʰ3I$Jڪ ע*ޞB/`#-0PY¬2Z\u';y9~+Lo-&Xg]Vزcdf3c1-b\ GqFMx[4B]q#Q7Hɐ]Lk4b?ZmtI5XϙыvsƱTRI~lM^l1f}a7Y4I$٘g,R Oڭ$p2{=jKSrmܦkٓ~ qB h;:CxN/E.=a=DŽى7咧eB4&X ׳BդlJRsuL|:ȊybiE|@'[FjH +^"3ԃtΡ K W<`(N(ӒBXXLeZH7Hy֓R<BD,ftzڈ,^ nѭ8d40IU2+Qet?cr% j8C<%K s\v5ENO|Ǟ(y:`pbCjAkki =qULt~)R"pz3)b [t=H82tS gj>r>DM24#u@Pၺqۢ}ۓӈq8jX~x-}=mlT Z+yp{rGE0Mk\}s%B7Ba%l4_ >&0!$1Q}3P8|]yiZ;=,1. "; zgsucB^s|k <]# o1 ̩J==HA P.9nf6~fN {SYHJ0 sG GG՘a^hD`\DRSMk.W-ԁrI2Fbz"l!VzUA]-ItWa<lVsf(,& ﱾFW8#/M>,8+La;6~>1/fZaX~|%{YCV<.3iǃRl_ϐ;!^/R/1&pL@0A ވ+:l[T<0Htq>ݦs;cD(zk+Rl 3 իI& 9\3#F$@h( 8FNjEޜVX,@ Sq`<W WU?a8<" w>1EWѣΌIŞ'Ʃ`BAο]]T "`%!d`V؛UEB++_&hQ?C[m q?䃑RLHxfJV} )Xw: \/t\c !RȨܷ  8pB!`Q\:8چeچ-FHQ ߘdB dpx :Bق2H`wL"SG:~tӽ7 /!`+<:Bߍ]-QJyZH!X vP9ePD#镠W8M* ڠcyͭ43D9ĐDG%V6G]t_w0A u@5F4JcUJd`P=fM">pEv|#`8Ξ 6A`)|)DN$\}]0N5O z $2Q%a1@ZLEeQL1IY^EFPf0(@l SA d<"GѹvdI`AAJ[ 6_\[ZѽFڛm퇯K=h@'(A^ /aZq?ݔ8pZ \I_$:%6%`&9iNdSEM}b5YdDٖ mVENf%R]p'RX'g^t|L1҈AdlL@.^-:y"EzU KH-GİVZXl-C'i&>шCR7-(.>jϦ`PfKdz`#l-&_Bn/C Bl:Q3(`PW)+¢x\\v^)ƤU^@"Y: OqZizxmt4 DLP E)au2C' _~Q h>Dmd۶i`A jh$WH$jFbQQ6t9<An~b9d`^*:p@y맾c9W lʳn ~ƫ+Q"XvRؘ *kzbEdyHƌRHr$!,= i. 휛C 6 Z9),fm Jl11bõ>Hf6ET5)gimy%:j'VmoeBrg b;>4_Bƺ0ٹ+f"CeU): i'eh5ަZb_B!l 24LYfl-CC62e`ڰ 4f|ϱ)nS 0ڠ!ť2Adjڜvn=hwalJ#)k5f/bLf{2ߪl|bTu*_"/SxFvgr&;Bx^i"".mɦIl|bdK)rml*Qv!2ܪL渁c~ $UəJFvcQ6^Ё>]ljpgϹ%R؃cT O,Rj_A5 2tjT-UX|r#%P%L䵲RABb`joG#jJpF 0_pB,P@ff.5,H᫾~_WXȢ$*Tb p'D2Xb.(CD|]k0 3TS@s'`5Bx'(4Tn.Mf[F \ j,2PC=cBA2x'MAn=e QP3b5 To/o> kI#*KҌD_6柪HSyC.xOD=kjPA5؄+; UAN7k9I*43)s!$Ez[,7l,C:Ts&`2^vk3yFEtgC=s|+k 4ă?N@ !L؁EO\yJ쀊/9U|TsY(HsvKOD>Ox-4حW dAנNS+9֫RR*B_[}%KDpJݕp]mF/ z'9qi_.Ak6&`5H]Ys XJ/ңٺBMJ֝8\+`{x:j&g^uh‚ ~j>c<^uO789@-'`{9L.պisB%()SI5>q[|YhzfsBn 9;F=:pB[UFy6bJa0BTB'\Pq'xJ-wRPu*fT}`eCޭ! 7) 2ߏ|i;95M;Z=[8-~dd⪆Ӄp4)2<[kS-^ klyZ9Kvd / M`. AϺ'և#J5pvѹwem G-),@j]- mHuöas yJpuB8u\ uԏfM7qԹgO?:_ }TR~=:ӥKÇwE*&}Q6%;U84QZ7H4WaHK!E !=&ur^-4sgϟARUQ?E[شrتuv,2fQ8uHia^>nG2>Yc[ACYKq,}{AcqNm3_-{jJ ,-тK"xÌ0HtK B20dS;+#M|-y 5{mզʿ,9b3ul"!_[gDs8L$Xu<O( - o{q 0grmK||$EctZt|Id3'X6KL06;s& cqXRJ*3}V[A@L?|J RlOzpn" 6 *` *KH` (W $`%&ߋb+ DF(@eYxYem~oYgagwh>7`ܴRCqkH|ƑZMHyDgѴn[ݶ'm^nN{m|uVG|]y O[pyJrtEm1??MI*Mo޾z{ӷ}}{|AO{R 5ZG ,R#2;/8v|c{_ 8/{+'?}3'B0za ׾%}6cD p DXۍ,U8Q BYsEcP#G)ښׂ7R5 YS7B-IaTᘚ=>,vC;k|cӢ53?㬨(` jRQD N@Ed:N1"AHTI|אBűlTcRs)K P">&S,~2B%s c~?u^墉7AQ2R‘r?N)jo4G*'52S ^~HYH\ AYBr!QMӋ<#HNԏ+Xa KD* {"8R R؇7( ~ApLO"HE6, |Xj%J`8QMKwū#l.?jn\+=SJǂ>2e C9N~)`,oXuhA8V +), xcA6 @ZÁTB@*ZV`GkPޘ7vIŁ&)u̵ai9i=SCrݒj.^\,7VIF h/)Hq.؃L`+n/` 2hA%q3oTb ]RՀ8,.I cnIq@7ڣrjشDxT v.ǺwpE D; ɢ`O@ !v"m+PЅ H(?Ʊn,jaqx#b)&#sQcSl^elKSRWQZcDxMv-phdIG? Wch+RqlXkΪ=Q;h8,az7HVZ$ )廒,TleX@vaA *6f$:ґ_"8&#kf<|גl'Qs_S1^1AXo$U%9LFNҼJ>\݌۹ƀPG}/`֬Z ZP`X Z\.AHa*n. JO©Jîk,"g5r.L"LpJ~ ZX+DZn&VжAơ(@aaׯh.H N`.gL7nh,XPNRL $kX*A)(oppa,A0(Ab+qm V v Q1QAQϱű1LcT6AX,PȜO*" Sa(aAp)ZZBI0p,_D*fУ8(7jc'!,x'r'}'2(6a'2({'/1VAn,btoPΟ+On,'̂a,,A,Iq.6q`&!aOS11O13212S2+32391β@KA@4j i6bƌQQ)Na,JaF9O*99GA:La:3::S;S:ɳL6pBA pAf^{w N 2e^[},~LڋÁaNXXZZ~! W Xc6,Ӝ׻k+$"msZ[`vN@h\M\Q&]M*.K *XG R!Q2QU! DKI)wp;?O8dAENkЋjjNZK*HYTyYIQQ4”$nA‹*n4ŨU?8)\3Z DX9&r,r MyNNR2.42NNRr%/:./3B43JwJ^Ϯ+ytzG7`:$? dkZqz)*r*H4o;)ڨ9z|sRyo.2-Q31r22-.9sFE:z:5QJ+QЎ (kq;("A O6Ic=[-Sҫ?1K;ϱ2?4-jYZs01o05h]AUN&-rn|aXݐMmMkҧҸKsGO{p~3"t|VT=_57ѻOUI)86J;hM=oo.uλy{+&'욄B(,,U,麵m;μ˻Pd]TowF)m!KLвqe{o)qrc͂ȟ\Z,=3+:ɑ<2Zɫܣ1.'ܦ˿<49S;ʓ΋ܡ<\ʏɱܡs/Q,)΂))!})-1=5}ӁR9y)Jz5{3θ^5\ZUMܭ._Z0oZ*:¿K Sݽ7O›a{sfڃZ\ToR5-8!.N. O=]Q=hpUŭ+~\.&#U~ Ĩ*=6kC{=Yoܹ0.c/{g[}V 7療+G+7]r|Ƥs]ڵ^͎Y| ~y],7%nؾqjT^xVۡбFZAH>K||ӿ&-ᚧߝѢ1osR;⣐e .)dU) `jϯAݻaO%Z Dzs|fMuZ_58acvA P` )x* `jF/ o}R;D~;uE,MHA'> +J(ȐIBSɛ6qΠ!g9R'{À;{^B<@y&.kCR>K-DjRI+,crq]NJ+o,uYA,*C KG?WRKeUC+XM:k - 쭘"U8Ƭ(ڻj]RVJ8અ_ĵ͏hq[)V*2ВE<=)̘IN3E#)s;PLBuJ|y~0L+r$"B J;p@< @]%%` PB@*!f),O%>Pb@Jx  p EL`>`͋zs@*]bO*(L  X`A+NbB?]pKm4O]%_x*(wZSz=8<>mg y@`ބCxcJC `vC"8B@7xbp@2`7 cJ %s&T:YS`;jiwJJiM7E1Q1Jzηޠ!#}Q#O= f?-\Uj2cn fY- $x+B(c/& B6(9g>(` `}8v(xT/^A+c|ь|󬅏xϳǪ6cpxO#ݵ\!آJMq"*ghÈt%fpc-~Z ՄߘѝTJG?{R?~R.Q/|(:EuWxPRlB? Q2T1 JV5ԫXǥnixn(-%ZѝzOФT)5j|PB5jN[YVQcFIazS~R9һGdu]MSKijkKbpPHI,MAQhƱa] 8 4$e.Y=Bl,W{^&V,d(AQSv#ZT䩴OR%D-xKƒt|[ޮm/&_Amt2]rTeW o SK _t"Gble7npcvJ]D/w |b@3JaRYdsf3}z°֤@>y˪oM%}~BĖng1ГGZ@IPbә45iOzӠB&,.8O`fw-Wi=FλQ(GWדN}g+ȵm bi&FqԮ6BLdgN~9y Dփq (=g8-^O'U^)x|Kˑ{Q {/nϧSMKPB$@ ɝ-PųDA cyAC#uY!?Hꃬ HE'4s#\4w\T/"\ ܍C=R?,pn`#qHF%R*%Dhq$I,тIh)aܼ]hA6ӛ>T2e "F~CyKs쭛Uo{"v&6Bޣ$4>78HA Ri"RQ RB%. |ilZ(h9bAd G+xo?B s1'qU -7ZCp| `{Wm:'pEQ+EaiUt@%00vF#rYZ9?P0n#s~4" Z@ yg7;pK~J1-9+'ʘ jqpa]` 0 P Ib" ] O$s[g ,qX !f{ǖd] `E7pBwU!U~h{Y;7 !|Xyug4map`-` " ]0 00PAh Ԁ8l[mRB:U^\^go9q'Gb(s0C %`&΃h yE;E PUސ 00 9mg[bc\#[pZE_-9#uX eGKUk]UD@vpjeY^Gp7Xel \en[B2Z'W'o()t\=yl%ET&S:2u$DDtM za] 5_U@!4CUA_a;YP\pbuZͦ`YwJp?PWǤ栻6P'oc1o~co2fpu0?KrWg(KqsGi]qUƠǵ:]u.`T&ƙK):E;`AzWbUu\U]IGcIȥeUdy:(Xb*2%6u!hfVvdu5+Q+uk`hbfI[t)iUEYY:[x5Tc:q%\Y*G(z5kJ\E^tFfHHlE!*[ΊzT{[Ȋ3a(gŕh!Y0HYֲaDT+lPxVP8}uPxP;\\``O;S TP{Ͷ\/z(~qs*fYV*_c;_Jh7:clzo>S$W<=S۸ q VÕSc:Њ>uUzT)vu8yA{cHh woXԹEQr+hS0R1sJ3gIzp&Koꏿ+gj۷*Teu{X!;_lڪ-/\!ʹUg1ջRUoXiBh`GoTQK r-jyHka n *zc&u1/nATTW` ʹtTB<;8h"@Q 7Sz&e@3 @-Ps3 ,  b WuBLR JR[u) l:JVۗ P} 7w +Zhei-Rm9l-rpW{I86@Ip ] ƫZ ۏu[Z3yzo / “oɰbo'N yS|`̀!{{"$2D( 6 ^x jm p xe{ʫy,$ ʼnz҂4S:~O}陮a p=RS}"Pzp/8MM$Uٖb-p P 0֡b3P ̭ʯcUNȼ[]$~GVm$ aH["w]>Elj u lD'p 0} `< i `h<6֢zxB8̸+Q赻΋Kp=jN޻uEӎ,r,<5+Ar!r؎TW4sEևbsi+D Dapog(+}`+B;7,MsX"#;go?zcB6o]~GdNMDTq$M$N?NLK?M?M?oOL6Ӌ,ͩWV q|I|7Y|ϩ$Ȫz U6TPe{QÍ^.BdX]!t׮US?T/ HAdp!‡>tQDZ,bD).Q†?nTiReɔvD2e}Àagz,g$)ҥÛ55Xr)ȐKC&iT&ѫVlԯXfeXH65{^P͊t7Y]nKXlQJD|r`!m ѰcZ"upʏ^Iݝ=سK~WICi^⢌'&)rm/Gɶ =mkp:;^4Q^ώr_;ϧ_} v<ӫ~+%SO<} Ca>1c qC4QDOEK`V ˱B14B 1 )zi>ͺ Mnܲ˘cIK/K042$L6TM7g%˨0<󲵞îxA,\,*;G,ǘ_>tSM;M34_8TS5 u_ɮGNXi{ JK4-Z"4IqP–t3R[PCLYTQQtZiUkmUJ?KֵENA kf[/4"*6Dwξ$"U4Q`k$IEx`nX`6|׳ ͕йeTPszv.yav͕ls +OYX e(s9Tl(_X^3Lf/yl 5;ig@kx,3"6Β}τns=V`b G ,z#8XxխN5P\WǺ֬5Pzj\ָuU=qVekb[nv}`۞vli7n}Y~6s}ngϚf lwcw]lUۛvmp୸ `|\𱚋g|5w" Kl9Nr\|`]r+׸Sn󚳼2/nqx<%{vs|0'lQyo?͕ᐺ˹^vw=ܱ:>nt]4w~j9o^Kj?x#V8&I=Χ'=Ky>7=]տGg_}w|~{`G@;itksnap/ProgramData/HTMLHelp/Artwork/ttViewImageIntensityDialogWindowing.gif0000644000076500000240000007753610534177573026642 0ustar paulystaffGIF89a&v## *+)-4:00/887///;=F2?];FFg 'j;KA@>IIGCIVIVVPOMSSNYYY[I[EW~WWk]]uMQfVff\ay_rqa[N`^\{GJcYke\sed[hhhff}gwypohsrmzyutd~=T>Z5L?]FZC]A^NaJcWgVjIgNlSmQoYrYuhigvqqxzdrezd}5_ty|mjwt}~ov{|֏$).:I=KTZ_`}l~zd}b{JT34 #&92;++55,7m:EV`_nwEWHHXXLNUiVkiJkV{{igxxhrZrq}{nyxFKT[_dkzrƞǎ˗ʜӡɭثƫմʴӺ迿ݪƮίӻ˼ٻ˲ԽږϏϪ뎎萐谱Đ՛פ̭ҺüνսŮ!,&vP *LH@#JX!".FQAԸ"D!;T2H.c\ҁK";XS6}fl9fQCi*EGE:5)ƫ &P B ׮hц[넉)PLBn\*wҭ+/߽/ṃ+^XXKLeń/gyyo3ݹ>#zgq 0E8]sֵډ&x;EP~zp#=}ۿϟ'`}|w ~w zʷuWwu|15v)n><Ϗ?o:4q<@ܑ0_\L; !sLY{l]q\Bȃ%tte9g砌ChzEÜ^i&hn I'j`vNj j%[9j8O=J8N0^<@ Pæ6C p6zgIC@;9[9+n搯y 0*p6O̰ g|07Ek/xko%(2j/p.$gKY[ L0C˭:(#Ɔ39NcK8J<.6^3V@vغ(# D۝CxX1 k<. R y\ς8 GLyMPC y^4]1̃>NL u[Yn2WθO0-ܮ wr൒њ nǫ >8 .Jã.sP2.;>Bkf[Һ ʻ?;`W'3lŌ_u@t4G5Q2.f"H.TA,#Ǭ'h1 )@ rP v8?,p'B >G8Pp]O&ȇ&&GZ> Q!;фXDh!@aA. ۝H6?*O]3(YK^?{Ճi/Yxa<+#QKl!(!?fp/`#Ei)o E]vC&z&xB8j\҂'V Da8Zae14 v b'Z ~1bÂk>Mdy@ v`&6A V"4;bM8Ѕ vCx q[-s@E˖ Kp*$}@(!!.T>+Zң ]yL$ Z+_."[Aqn# x'ؑ] P&рk|mzE2SWQ6KpwcLYp܀-ֺ v@კp '84!~pQoV @xal*EpQYc*].1GCp֑&qwkw Ln.p \@A`E)P5U:xL>1h5MS%Z>1j<5G< Nv2D84 n4y02;p< C4!iBЀ&SpCo0)<ǁ㉠Etpa7 O&@8ƟOE%h2xyL~?愵߼~xD:wWAqLwBMj %QC @@599 >5Lw:M:ՁCW%'X+*XRPEX2(8w eV9X6B{DhAGx6 y517BLH7HRw!nh_("wqw= 5 qpp |؇ÇNy5pxx8(870x؈x68 gy؊2`'x8؋(( ,h税؎X؎8Hh]+p,+YH  )䘑ixP( Ѝڨ 920 :9i@ABYDYHHGiJPiGIɔJMWZ)X Tٕ^9dY MOy<ٖ=p@0 pЗ}ɗ~z ɗ@ɘ~iYi{)9YYY陂i)隥I~I@vyI9)i ‰y))Iٝy)9ٗỈٜ~y ٞ䙞yyYꙟչ)멞ۉ4y0Pyy::$'%):&: ڢ* .<= .:jH&M4H:/JQ:LY\ZʤGʡbPJڥ_j:bjg mVX Jj`ZKzmʦyJXy:8`Pz[*TW/ Jpڤjکʥ* ʩuzꩣZ:$:I:jD w*:êhj کzt*ڥZz:j{jJ"Лۚʫ:J *ʯ*}ڧ* ˯jJu { ˪ڪ[jۯ0k*jʭŊ;zĚřбE*ˬ%[ 4N+lzAk'ˬ>IzڵK:`:H+7˭YKZxf몴z [l [ʴ:c+VkAr ʡp0~ pzk{ /Xڜ@p MOzS\;| ˰W芼pFʶ 3 P0 @ ;p;;  ' &о; z@&z @ ; ۿ { @˪r*ǻn۶j +k IP '00I 0 0 @̐ +, p#j  P0p۰0pƨ0# @@ &CL @  *@ P 0 S9:¸"y>).N .9Ocʓ* ?$o##o(&-_)21./*;?4_+o';QBE.38Bo0Ʃ ӎeLkk+b7cQ{ukv;a?ƻ(̶^o>Ҏ<{=z˷/9_JV<]J7*9M_>=׮"+^ڰz*˴|ˤq= {6Mz..oꪜ]*_ $`? .tH #JdD*cǑ"3&$)J 'x!ʐ*1. ݓgӤ1"Ȓ4C>Z)ґ)RZӍGr4eR1+MS? U@`.ę/XnܻckD+Ç SܔpVQpZIvV{Ra-2v QLS/PHi뢚[6YfU7~:yqMVZ.hďWgni)oSus'$jUsͼ^TlMlmW~u{`e+X*,1d5 t5H@60P4K̏ 800_"T@xaa_~Pb D(2$L[Ȇ};WG"]KV:BFCfGML"wl܎*/_Q K"((*` l_ǰJx,(8w0L"&t Z'iZUZoĶM2$6 T.XP9V[2? a;K׏WCPGO~̃6PA0!bLqUlbXԹùdsw#\Xka-N`NS+w: =;iSR>:%bK/Z#lt|_#әֆ:bg6yTD5Č?Q%U5]5橔VxrGV jw#8{y/ >Xw|ض fc&?02Ęl.YZ,i7D쮗dc£yd֘MmўVUj{YwHcoT;my +=AɪǯuKw]NUeU1BlN#+`kRV@VKNâT)MaJöѿՊY^+3Ba2*)iI` {6x%5l+DN,8VL,3Zmq_͏cռ5-oz7~+-p'|>qnpn<85[*0U\PzOrFUi[sJK.=)lvaV[HU!FTZQWwZ}e"w8}Ns{ߝUk7ͯEtHG9X)D{3,["=9$dA3b;1kK>k+ȇ<鋦{S*ã$>w㙦rQG;y8U`":PJ XIO٠:ܫsپ 3Rʻ:*;x< 0U~0ɇq ?w~lx pqXm؆m$_X9 p'!mq(HaqEQ WrxXyXyQpmX8p}rwd' @1s6Bdc::{K@v XrZ,B 8J`Jq ( D@pƁ 8 8XKP q8@rx PXRJȑq@X%W8xJȞ(\(˽Akq.úSCB~8T!p0af W xJ$WK$2ʑJ| I U  q̺/0HpXG/I8pxTXq@T8t<,1ē2*4;{:p0$X|pF˔DWX  K JdE Xp̓ MD/0X8J@K\H NI0I<$APa€Jx `'9A$k-k?۳ea9K$vKTV9 bB(<_r! J X@IAyO<؇f(U2@4FA@zbuP"/xk}V{VgV3KFe]V+97VYyWlŬyz.}ͬ-~)z}}|؄UXUX|e؄ŭzm9xRkR؍؏Y!rY ٓX Y!EْMY%YٌmٙuْYYYEڎY=ZX؏-ڞ٣UڢZY],s=v+TY-WԺ-ڭ2}<2[-E-b[:۲[.G-2- --ŷ2\zeJ\ۺ۹%9 C-|;4xŸ J(ӊ<3Cw2(#߽r?(5S\<}0,c2%@۽4L/ïBeҐQ5(-xzݺA9f^ѿ;>LݪJ_۰#* Ci}9n֡R:^H#"E^lG9ߝֲݲ0+51~ƈaz͝!W!#F$$0}yo 'vba)b0vb.V*F%~b0y !~/v5~ 4.10=BRV 9Ѱ2Nb0}`F$*d5N}b9IaF^'b4䁠d0vdNfzEoJd=bHbKveEfHWeN]dYVv5ea.=fHbM" +]~bҝ}@`op>|it>z|~Hg~0wgxFvgs{vgxv^g{hNyfwgwfg}gxzh֬ths~V}~U Xv ۿàpA+HojMiR Ki " 霞 jMj顦ބN@ꚾEn6x6jkj&.k67Xk뺦>9G% ˇn 9f ПNlWPU`l~ŖlnʎllǮllf?H>gpg0VmFg0mn؎mfնmӦmm~mmVmV?-6ޥ+mf /.f8EyЋ: @okp~on>o(#oVo>oy8)r5yf lnn888v67G' v`qwXqw7pGq9.~)Sm xpbqXG@8hqFrr'q/0Gswq!!4 J׻aRlQ^H)יiH@ ,O.ssOsCq@gEG,wp~P#u@% ~0w}r=G~68D?uFWtEOVS9V+fR@K{r.M?<<uv3(BGA?WwuT_-gu/qTgtHBZ^1 iAcqP`zhߌ"71#z@>s D~pu7@HiVwfwo,'UdWwXrccD 2< .H}(b3f7oz/77XeʃvX`rưP'r@~7?zjgXtzn9w&WvasvsgvDgq?CXuWzq5yșgDG>pqX#N6ȃ-x7roW~H:HM<9۞W4iM/ Xv FɃ/qWGz/4oPn Mw@(XI&MZկ>ʭ!Ĉ'Rqv&Y(L,ɔ*K3rʗ0KLr&y8wTYgIp'AyH-5S|À!߻{>}ȕ҇ڴyS5I4r[/zٗ%u NI0b>嚶R\%SfI$" V,3BpWH)#oVgʟD<on.q(*珻ⓗj>'"J.ַE)^<coO$߷}bSa5OumwK]֙?۰8, Ŝjyh%B[q=s~R=ՇafqSBB aZ:w$TגfYRF8st![јMG*ɘaToJTMg98-dС؟5fm$L=%X*TN ]Z%s"[ET" ߙ<:pӝ'RjTGf`tI,Rd9'\z){V u4ROf窪uA$?Y ;hkZBh,$6ޱ]}[+SpGXpƘ-I,w>:G!TbgϯZQCPAEcޖz(W;imV`<",m$>ABړInVq9$*CjٖfJEќA[|bko(hIyf9YVcƊ,,0  UC~E)&k`C;}sU8+X@$FPɆ:VӬ@]meJʣOU =DB *Blɮ%)DӀڎcV %cMm|(#*UvF(\&]@ \A fjK#~epQS`#UiCK<(fd"P?Gt  gd:^"AÄ9gkQ?Sj2LFu&$ QⲌvE%HJrXKk!T [^s>,zKhQn'7Y"P(mr ֱ%ɅI0Ϝ.AW;8Zeta8;)/hDC.Mhf,Əhq R%'|$$ч۔ ?{ p7x-gPY'H=^k&T|`l0pP1^FM"uWL"Hdvdh|9a`NGĐ>^R&7YVG(8։1lO>clDsu>&MKX"+)${hZq~P ~ݵ"Q:3K NhY:![W&!VTo3*̜ѷc( K}lhT6ֲtk*=k-",˱hۆN耈~>C[%/b+Mx8A%?[j}#?ɞK48 (8F;$# dxaDVZ5SAVs%aQm49.p%3!$ tOkzHt`8 ̍#WX|hRy"1\ohݘuShr 9X UT`NtC*V-pAReb0 eZħdظ 5xB*%hT^̠c4K);//mp0Mh쏣T9]C$?k4̞v xAd8 m`b-%leU1ꢏPme;;\> NA _P:rGgU5'P{Fy$iѣXK-EIІߦKZ6ΠP6Hl0+nm?y>ȈUHd Wpu p0{.EލN' ZH>fPа7E9 Rm$a8!+:6{56}0Ubfu>Ѩ٭BW*X YI tjt( w@-z HCёn1y/#4l⭐ 䲞X<'b0(6(CU6x\iS }_M`K\NbT)C |9C y$޶ dTC9R[bXd aW =SPW8Ν]z\ l̸Mo1@umaAFXq3X% U}mL}!DX KՖyP[IEe8C9 >U>-A:aQ~`A Ź!]`iKܠŐl. "MP1i] d6PF_qq\ [.yo5Ŭ3z=6bS++`?C \Aĕzbq`Zxta"OaK^CB< 0qR>LA@@Ae# b5PԃC<0TWQW) DA`)C;l-5lL ]BK!fi`D J"A\ )BTbm/ B/Lu 4`- r9dZΌy!tRƅYe)#, <sQ>pi5dC}e{^;Y9N`h \YF:޽!D +HtɔC &A?b@,}cͼI4dŖl LRZi^|IdY?!LB>lZhgC 6dA &T- $Dx=c'-9(I.UqSP*N1BAa&M`$MeZݺEN9!]8/`QUKAWz IsLa͐S9C+*?h?C>*8:#x.l:¹b Q!}ەea!-c#e +X!5ā#x#)p:ބdz@|ZzrU( tA,)?F*?=%@6(\C-䨲+y.l҇O7 Phl݊9" Q=|(?܃=_V+`: D@/)ub:iNl rp rJQR|ѧQF)Ƃ&0 ̯^K`rmgX*qn1Q d+s:9< CC Aln f1+-kTo'g"8OqZWthݚD]Q~ZhI<+d-Whƹ!lڄ nÐk^TT` ]0+B,N3Р2+U-k)Y]HkIMLe P!f-0ʪrq;HAj/諲Bw&6-m-Xo}]p!&Jѝ=@|T9oA6𾖔z$ɝnu Dݛ[.9\ RVCA!Ø&kV̑~.zc%2PUXL=̃IaaHCh! co.vC/ok(Z^OjfZtFZUXVUM&nxI & ,jnRӕhV@'E)a'W443˦:x嘮132#cX=n,É>{Y[m޸]ȖC̾ǯr*g"T1L@ĦA,:?0; i6Y"E0 5.i)V AT4p0 K򩠟>p?[ P/6#ׁ B|B)) j~-MfCC;8hU,@k-,[)ouj.r\M Hq283o@=uRg-gA+xIj(겷 rIȝBTq4-&@vPn3U:qjYMl8bh席omXv# ꥞3 (k C B;`BYð)1h@\`12a$ĠpMCNR_A 7&`ECsjT[85b37I} ]|8[s'dTbn^ ?xgd3:v6h@6ftC}RG$xa\QrYBԑS,xxn p/pqܳpcxN&l,CuQqQAU#CKsvX$`B zBR߸dKs d r+ogYe\q5l0F3AgxCxGo+\d)pxIX#-1P9//1@q?T3np 'b69:hcJ(y6t/Dp^z uS/OŮDb?C'AKaq'S`fCIƮWOKĂC[4*KLޞ:X?$߼6#ǜ_]>]TvEɋ?|DkIĿ7;YC3.jЮ8A-O1m$d,i˥#aަ, z>8NDmWFzju324U~ws6=7mۃ=bn'e*Q!H4+23Is dT&2fʇ} uV@`V1ء N kA Ԑw h9ˇ_ua/]ۺ2# { JTC l(!dz4$j4.+(K=b1qgq/82R#`mHEvH9N<,1 Ky|t2~mX P)fR2".u @C cA2psxʃiaOI@`r, (uPBk4ɋ%X*4# ЇlDTL#ٜ(;eRWqd[P1~t7NiLyO5!HF^(!Pj]Bv,bG:ItHҪ 4p!$ ,ЎsP+!6 t6"?o%HR5X.CUj Xq@$eYHs6aB\PT^@Xc4N6TqJcY #-dSQk,)˔!0qنxI2_gɃQ,aWt0,H Xp"tgB ,9:θ*Vĝ*_DʐDd%? 79q$`R''f60J@-nLB AujC1qiwm'Jhrwt7p::G7@XH"Ӊ"g)ݵJ:,P<((:׎آ+_Iɣ^]Xha:9rKB \r8f]w6l1e?ZLHImAؽ9SQ&Kcc$!r ג)XUl"fwraZɐ7/'QZ3L>6rN8iA!U4lɲwzÞM[Nn-B?.!$B̤:3VJ 7 ShZL<'HɁ[Uh;IWz(%fML0&i.F|EMbͥ f!jNVCq褫RU<@ǸglGDfpps:d6_;QNӷ&qo<炃 d1x?.斊^n\j{tF"vd@3c6*v6bLGh?~&̣,dp HR0m%\J -|D@B:0@ۜ 6 i+0PPo C KUCp-n mH## E6)JQZK QqhŌc'' J8o>\t`&mRB܄&i"A B0alAP:@*4!x0!g~Hh~jԁQPСJ(,[ءaq1a!B$"7r#yB[Hr$OrF#=#"&7t8r%/r P60%5Rk$Cr)_@(2ܠ. c #"A`6`(:`6(..7 6H&rH`/RH`-3s$@-'03..1 .'6@,N 4O`B4I65MDH@H 6I 4OS-O<4e5cL6kS\37qS7k4=6m9837ws4s7sS5E@6O/o37[56s68=:[<1 ;sVNfŬΪ{"(wh!" % @@)яAPAd@I@w-R:Cd(4CפD5E y[FA"T *%&-0".? Bff\ oK9 K9LHllKK&MTLT0¸NgO+Lg2"P1c~Èt$4{jsc@ IHimLP=PM5O J+:И THtJYSԕF%2•PPtJTtWLuM ndaHtx,H{8uO;^œKɠe(\]Z]T]U&]-c1r*5Nh*fulLTt>` Ha"@%.Hb&(VaYD L !ЪNPBV@+g5bzvd]#A$'~#:Lg82(Ɓ%1Ydmt+i~-ѠNfHdfa6'P`,VPanPaWq(H,:aVA,AqQ`(T`Ɩc\IQ\+gafdhrV/[Y6d) QbTsWA*a^AlTHVaVaLpM(ʷN.D0&a avRwNXmfҭ†[x38ݖV gcX`"X@{wVT܁PYN`0`r?/Nu_,U`*a&h*awѤv>6n1Ox6b`s(CzUX`L؆g84Y60T`.7uZW yP&jDIuva6CKdK8U6m'6R*y=\gwfCX2``s-Aa\Whw>a!`aWpy\oQkt{RlY'5f-bYwPkGx|(ʡ2( Ѝg @>9^8_]\HpnUzU`k\mvw0mm9^Za#Š4ЪcYWf',[gw{z9i:Ȍ_:[EXbLEu"82(fry$>E[EZy mwVyg 喥1xIد^ۆ~+m^AQ{iObIhmA٪}:t٦ynl5Xkdu֭Pd5 Nx8>E@8Sst>(j;siV!LV9nJ*aIu^z)y}7{*XWm9Bn]EԤ֘?g7'X`'AAMz'A,ʡ|Y&tV A?B`)D N`$ aCfۿYgѵQK?8wZ Oʘ В41x6~}]bPw#Wܡ!Q*DX(ˁOH}}/\|\ZQ϶7[,eᘋa<@n^bv&!s}M|3AV@w/@;\)z<'\S#ȁ~'A]AA!\a?ݾ;ʟe!ݡtҽ{]ս}tߩ{5V!R<|u9'aB\WƵw(AY "i! 0@q«@\|xuVDݡ>^陞Mؤ;NE>]&#d;&ԛ# atjSܩsD% sڨ^/!_'ꉺ#?۝;L:?A?9zev0ftrwm~)Ⴤ]/a) T!FaN?FxOy?ƿD]9;;N" "J$Q*|’+WZ͝]VՆO\]Dw+w1_s#7fLcƯ $ʶ+AU9s&LYioY7j۶g6=ڻqwݽ﹣==H~+˘ ۿ<ԣ|i]V=ȃʄ ؠ<%uYvMdcwhO}c_?iSFq8-$~8ZR.R)P u#A*@N((6ۘe> ]/՝㜹\F= E(hݡ&hGV>tLyg* b&d֕BIS֕jmyO7݈x+& ȃ C *2c=]‚ ]xaO"Ѕ xiIgGZUىhm;S_9S_?KGyFieJNٰi8(>`MtcAvc`Р**·S L0 Xnߪs>9ڝ:ҽ3Bޓ/گ;K ‚q!pC L6wrA*86r'9Z ,slek.j$}Sc:VH5zi}2# 9ڕKWN9q m*t l J"g*䘰Zp p-ΉkR ]y$VKO9$|*T 0UlV:xYD` 7 wA8Nr'hqG a{8y3*d "`Z-1@ 4?X ְ4U[a<$h-x eV U$zAe옘7b.(*>U葎+ </"T:P3/h੄E_  0A86|xajjwJYiWY?xKW>LC|-iL ġnK|_$v/tZUEe+q`,h'*M%+dey6n./{)$f%&%CKjSfEP鴔\׿fXT{x]0߬G[] p2;!o36E7bM_rǒjukrG+owEJ]lwS'_!ٻ7ͱc1h1p+~??rw0vCݗϋܸ}Z*pC,[X7-`C\̃o^[79H{\s2qUOȬ p-uT9]vL]qy.z{콋~-}+·ؾe>w¯]^}cݬ;b43hM F#9o;=`S>~6w۫n((.K9H{|,"`y+(4 N'p *0 ` ''P 0 *` *=sH* 0@,0Iɲ0 h G    ¢bp,=n8 & P,&*  ,p 'B> Cp`D %y7n2VtJGr€Ց(p'Ѓp `,@  ``X ` P@1@H ȉh& )`0 pp  , @,00  ^ '#)i``'p0 tZaawW yOw[Dh^@  '  ~8 8 }Ypp`ƈ3`$p}Y S` @ a`xy {I  pPH@ ')` sY-P ` UI{nCvx֖-u|0NtzU_mdq` ^ڀy* ك`,@`  '-H &0- x,`3(p ^p*P`؂s"'  *@ @  ^8+`pp^`@93" 1)'ug]&|7u\j[LX' 2 W ~08/ P Ц "*eb +pЗ+,bx@ + @  pZB\m<#|h\T m3 TҫI!w#AbݖJ0c=֮z !l8" n ,8  '0M/,԰+]nVUu☷ p+@T@ƩE @H=VO{) u&-jؓ` -xJY uȢ'@B-0/*ٌ Ժ М$T(m- { ~˾:z0!^ E? ~`(Ȩ2ڰ ,@ r/Г-(s *9@]Lj )0m8HX4*+t4CӤ/:S)!2'P,7Gu؉%NͧX";a *ǜFʋ~ `0 . AI0 Np2՗gf>6!H ɂ77 M(ʇU0~]}8]&_b݌Q07d1 0⪹͠,`Gd 0p$0, JW1sx#^߸{s [3 H mI (1Ut+ Mp&@ q>,@D+&3 /? :-3OmN i*@J'U]/Lw']Է+/I A7$~?/mְ@J2 qt @  PϨ~!CڐX^ pJj^%\V ,T' 8)FHKR2LZw7oa2vU:WVQtVy_u8+IzQe&k T yJvK#ȏCkZ1ބՔ$Ց.lc0yZ9w2A_@ֱ $C m`/7 _NSӠ3 gܼ+6dQۍp::K%ޢiDxLݳ g+0Sٜݶ95nFEݳ2)ȲyNMKD"فf,duʖuH,U سŒ Z8=6VRŹNk'lJ75w\.Ofjqz>Vڛ`߰HV0U7<>FDbת3KQ&tjIϏ)$#|AyRO^"|q~wэt_=z ]q8?TqX4^^bbքv l3ہv}hp"x﮸8)b`4 o~=pTs#KY|%OW(7?~|9և8ge{^}Mo|ߣ>g/]s؀O xz߾x|Pάqrg~?YmK&ai;]1@*yIb#?;01s y[3C9q a:SEQu8ܒ5 {k+kS2#SC3Jú$D[{!@3(B qx<4dB#6.KfwEYEZE[E\EZl15T:Z10 0s1+s8Y3к)2b`kFk nFlklFk n4mDro$YsA#8AS(""Lٱ>F1W>Ǎ(c0dPHTȅLȄDȇȆHtHȍtHH_@1qEpK@<ѳ Yc(]Bs zq-+6d[0F_Ɵ J_J,J4Jʣ\ʢdʧDJ|ʩԨ76L3#u" Dd9 KID8( rK\J J,G$o˥ SETF);@*?QsaTK7sb8Gvdp4`HHn,JcF\\M\M_/2z9DyB;ti ND\B 4kL̈́DHn4M,%HX`4T\̈́hTx 9El+KHh[ )7gS<_SIڿ+_ۜ΍]y4^?숤@f{ (v(F8-bb?5*>?`cC11 +?K?K X@ Xy^ 0dD6BV(dG^DdHNd~ FE&CIdG>dMNdIkFƌN^dS6eIPMveYMMfUOVe\Ѩd^6e]nd`^eEdFFWWfeP h^ Ha yj,nfy(ynrwfo&t&ovsfu^nogpvgrno|g{g|vz.|&hrgog~fy^hm拖~fw(vvm~gvg6&~^.蕦gz&痾i瑶XWI%^NMj.^jn!u##A@>HIGCIVIVVPOM]J]SSNYYYNNaKTjEW~WWk]]uVffWb|_rqlFIa[N`_\ed[hhhff}iuwpohsrmzyu5L=T>Z?]FZC]A^NaJcWgVjIgNlSmQoYrYuhiaogtgvqqxzezd}5_sy|mjwt}~ov{|֖# __}l~zrrWn 2+*&0> 56:H5BK4X;g8x>eeoo~~OKGGXFWWhKfYjhexzzgerhxx}lyxFLzR]dkr{ƞǎ˖ʜӶơȬثƫմʴӺ迿ݪƮίӻ˼ٻ˲Խ뎎萐봴ۣׯŮüνս!,&v` *THЂ "FT@a-bECfhAbō'5zTH"3|Iс$*tg̝]QODTʔ#ҢBF,i2҉ LxpA BVhVkM )pLB.ݺx֍.)zoÈ'ǐ#K;ٱ߽=T+m-ZgJ``jb˖i'5o?}<tCpMP`p%o=i6[lR ;$Ή'"/Cx5Wo:7hc8/X݋8nۋ8];C w$/Zdx;ܘx]e\xby7fnreb_9Y7\x8%y GIdZ6[ C:C L@)74)B 5&5$x"RP58N%VK5Ne * x803jl< + m8,Xm@F[m@Rb~KePޖ.k.[kV[7mf˩j m6KtK%Ƙ C2,Å:6\+Bb76SM)(!$+N[?G_ L+C8@M%қ/PP,P,B/Oxp+ m/ J![wR웃+6o81,5]7OTH%3T^SV;Fu WW v/rBެ CO=-w&( RB*Kl޴3MC+0,{: .u>ǢuPgn;tN th47:(5h*xD8JkB(¼pVLAR+£ V$T`sVPnPQ<ol u*TA% Ճn\ю |T*BOXVSbB+HG(@`G%>W}탟B.ůiѺ.[, !eBfF"XA N<%tnjEfTN'JڙK~3սcVXJ=8djP/4qX( Aȭf7h/1Y<&V\z B7Zэ$1:W ZC F7t0^p$* 0& D ^E pkhAg Q #% Z8`-&tC(;h1.A Z|r|fTN|@ Y)_p*-.P)9AHъ "HTl$yF8%|E׹n}O),*ixB! o{D5q\ pЂ K)!TC7,R<बX6)!7#WD*PiʨZwZu ;*+:tz "[Jj Z/ڨ+˩ªʱz{ڲj걞ڭJz8QYڪ:Z4jj%+Rj_۳9JjzJj KJpJ^됟@  ڀa  'ꮔ[@˸Pگuڳ ۯʮQ e{i2࠰X p P F ؐ`[  +   z @ ` @ pۭI{-[ ˮ7[v Y[SI P+  '0   @H K ++@ @@ T ŋ K @p p +)  e  I kܱ' *l Z+36,P' *p'@@*|@'p p yPK 'B   @@ k @p|\𔗛 @ *`pL  p \JW *g ;}0˴   @HL  ,O}{|đp [L ,\K'\`\pԋΑڀ %@ &a N 0 T ' \  黻˪<묚˴*Kj :}}<̩   [ aM ة@\ ی  ؑf \p'0 ڐ -ǩ ׏@  ֳ̍M `, 5.W{[r<ڷmj]@.@ 4 ߭ M ϐ  @ π @ I p p Ϝ ﰹ p - @ L ذ&` O<3`^n +)uSkڮ|ZꪎꦮꭾꊟWg C ۣB;<;M<\ w ȹZI)o ھ׮y[Y swm2ٖm  O o o/? ?+-o%%ޤ0=2ƫ!{~?;ӟ-:@;{޽JOofɬ p|JOɁXj(*X룜{+ "D_6{cCZj:o'δӺ `;6^Jzj;P5ʡ,\2mMogoZkS>?iJIO}˫{_UK1- k_頚*] .&ڶ(k lojn*2 O,A< <8Р@ "$P-BÅJ1dCAlqaƋ-+H1bɑ ߥsh%kL Q9M7)*47[)GYNBFDOMO13tPk6EwlKK@.'(7 8V%vR%µu21)6O:c]1/RlΜ0B癇06Zw#k't U#uT6auX+P!dMVd=va!JDKX `M߭UY$l4fHHQ7g46v*=IgK2Ga!RYU*Vm,-ox J'>IHw!;OxTelVfo~3_ɓ$RYg PN'GPIg\ݗvHYwLT^_"ټj})؆ʆY[k |Ǒ9 96YTB"!%H  RTTT@T&dTXOhSqvLX%+-`A 1&Uӌ V#C[ KEf2 (1)W Pދ;JB8;TpE,(GDu?.)-)rU>)J!%!)RPHHAH/+X5ض:tVӜ#ȒqC﹣#pl I;Qq!,69@9 ytQ MŸRqG( uBy$ŴAP\#]H R a^ETdÓĘ&6 Gw$x42r̀'~yC*)N9^3L_Y] ptvIZn+GJU : i9Wh)2 .wZ^mMitIvd_QSȋvjiu+a ҍB( l`*usU&-lo#|&X녔DOXNˊK&brmlNW,EXc=u l'sy)iZD_:ұl)aQH::oE4te2Vm2ؕu !?ny8Z5mKoәtAOԛ6iM}jVcՠuQUgձ5q kUӝu=QԮ6!K賎xVe =mjWvmn[[=nr1mZu;ݘ {oMڗNjԛ?9,A{Tgo.R+-4  ,ᙋНs!#;=  1G6m8ٷ Pl$pxŒaqHH`Z\xEHY嫌l+1*[-3.r =ˆ 0&lm@wlІImx  lptxh 3DwȆg<: 0j OPmx-'l@HI6hCmpɀ8T(RR()tjpgTPjHh0l e T4K+/Ƌ:s K˪H@1 xTX   p,l H d p!HFJb .L 8 H HGH Hjʁ tȀT8 8NfGȀPHtX PH`Lg@ @I8`"?K?;[»3 $.vHփG@Lj`@Hh @HwFG T gp9mwhE GTXOXmFR"`P H Ioa pKlHHX Xh;*@3?2_1xTuH@ ʳH aX (pHHTHE`@y (f\j lHDHp\ xlQJ@R;$uX.Hu0Fwu8H0Rݠ2R@?C\rp(13DH$ ^Dx((@ tG\Xm 8R0#ESPwXa2T(pX#zqTu9O OXj0\O$HjH#.RuPtrtP.`S`\yă/|=گE E IT (PMAQEUOԔwx O?V%-+ U@U/q.2SfUUҬf֔VjUmVPA[uprUsuViVt-uemM)wmVy-WbE" ~Wr ~WtX~U؄E؃m؅-؁ElX X؁5؊X؄5Xe؀׋XEِY؆XWؘ؇]ؑ YYYC>[ҫ=,?ZZr嬤+bZie7ŲrkZk,{ڮ]z,Ɗڴ5ۣ[UקŒ[]QZǢ۲]7[i[[BaZК[9k.k 8|(ˌjѐ>@,Y|c>]ڲkMzS \Z]8a-.ӕ5=\|\+dz][Ց׃IFqB_S9:]Ԭ>* /Y9&üE:{05U)|<k8F:kC}^=`c}_1ҨAz亻r=whvT6VU@ase>B~aaAar ih&aRbaFba@hab%v$%fab"VbՊ f :e16c6c$!Vc3.3vN6fc7=>;;86 XeBCt8u8w8_ee_F2]2 dNfwH^fa宅浺fu8]ƿ:movff`FeFs0_DeRaYB{ |%c SuO1O&N(vh6&hLHhK@O@hhhn胞h9EPi0202pi~i阖iiFii.i6i.jFnii6^ji=a eKu3ۍ| 0S0RR`붆kkn^~k뽶kk6H;0^b(Ɔnl~lcplƮllƞlʞlȾlжl.̦6lf6b-(d3ysM%-LJe@1hD>0uɟ`n^nn nI^qnn꾆jBk|Ֆg:4Rc4PcXN7oWwJ870ACb oi1::80n.gp]Gq*]IpkX/ecIoq_p(q'p@Wjm@O磙v%O>4X`n/<r5k-rl! / ǿ[2aHLr9=(XsC?)>WpU_QcFX"X"3FrLe018=w?P7AqV7t`uhVuƻuw(D@Kvp;fpOvwqyurXwJ ޸1vȆ-P6a?8:=ɠwj7xp j C4N"hvX$2'am@ &/d0'tzRgxJT1"Q}ݞ8rp@)fwsG :Gs8,y7pؠhqn}v蝷7'ssvkhq36yn/_u._|8c y _PV !XboxXa/v7ivr}OwIxAo md'UX`@fw tpv|^vOqC}ŇWEG.I&~j.8J{9_Khw=z?u<u ,h „ ,MÅh;x.nxGu yQ]6 4L$I^.HHkݵ-j(ҤF]jFssh%l(qr\ǒE,ڄgRCmǒA,j5;wj`)%Oэ #T'.NЫ'Of Tee(TZ΁ veOc6Al';$^S/uѷUxV,[m(`gV7bZe%Z%X#Fy]xֵ_o)ӗ@HȉJ LzNקpP)y%@YC9%&D)~xGJ*::p)fXNxLi4ӊJGe^UMEo$\q6[S(K4Srq)hTc[;u'߬HH_l 뺲Nzi_; +y՝3!~dG K2-I%e~ 78pHجpA'8|M9j#h[NM%X`ak|_][[pI2)tV;E'ܓ-FhMNAQVhm~96[X&L`c*Ip)I@I9RȌ7K^i,Տ2N͕)&~F nJj\*n`;԰ ~xrC_w2D nCBt,_j y Xp3,wrG< +`A$e(ك;bi%]j]!X,"9V> L9ڿd F:pk# "B*Ga7$/5cYXa'j3bF>h/``b-'Rk؃qBA#Y\罄 ZQؖ؆63]:@$s̞D: )yLPQ8!Z(+̚229E 9ZH1XMWB190$BE,<`$YMX{N253EXF*3e*w :uL:[82E;8)ԔU(c| $(E2#<8$7F H93PEN#P"\ZKGE._ed|"#YWt$xERPUMAGN MBc>F0ˏ7,"2D]TތS @W&dSJ$ "%8bOJ= X3g,J}]m<Ƭ4[d#<H տ t@CDˆIL׫-@@ S;/CH6-odQ/F`w\i"GxB\p~'P!ЇɠDs i{"{4 i C'ymf;Q8AL@ ҡr"1 C!W9-_bTB$@Q2FVmL&Bm@ xChC"BJA6T|Ց3%_&dv9_,zNqj v]e*kH2(2Eq 6jgI{(˧+ X2Ǚ4Yg,(*1B>ia8C|B˕hNjcG|/cR8a#3); J_"Uf~3٢yޖTzWӳF0 :>ȑfQB5R[$iLCaŝRv/Zu| hN` qQ֥M {`kΡ9 ]24r Ym ʤ h#.Y8*8[')C Ȱ!Et:s.tL3ɔ-bpuPg`,O(ѭMn7?|Z'xP;S# ӭ.ŝmస׽KTJzėkkV{:YRԛE[vѤ`hgoIi6h $$M;H ۵\腆H ̕V՚KحIđ]HUuD* @ 12QG6H]^uCPE0ՁOB[ٰ^ 5i 6l!zeyFMU ,l-݊ :uI1_։}YH'Iَi`lTNƽux X`FU =bpĚI͔͏C*m6 uHX%)B*Ђ)~7TQ֕E ӣ0AavUaPT)*d EpXM;4]Q ( x8ɭؿIGBԘ<@4Om9x9X:l )HM ֒R7|%|#J`5u$uC_@YN%3z"MI8D'`@9!^Ewa}q9O>籡dԣ˕F c=y\"qAe\j9K U]Rd-: ԭRCηO%=eCB,1U bV9ڰ@ +N2d\E%qEAhv$O9)D 0I9%efgg#mɧHb]ЧTZTa5˄Q3?b"C*8n-`++:XB$8B:%gQ]G ZC,' lJq؜ԅ6PP 8xnz7QP\pZ$.e]zJ痑]ә(>ٵubܿdC*H%u))87؂⑶VgR#~sR:('LJqvZ)P@G])'q\Z;fIRPEDx S%Si (^ 9dtL)7' \rʷ=#G.W#Ba"5Tq"*(FGJ9C*E* gqd~0֩:ytVRݲhߖvM3 `\Cn۳Qw$J7%{+s9+S'6e Z8_wq0&@E6 +m6ŠVjUFi|"aǖHsr2U]>Yy _E)hcg4$e$L k-j$uh#0~YzKؤБ~|3N^E*-Ͼ7iz¡i8,i0m,5X UlQۤ -dz)"r-}"hU&Z1Q=]!T^ ; 35ڝgd&.: %FVT|-HJk N ~:]xE`&E),zۆjes"AH됌u@FFѵ.NvNz;Z.:Ya, Ǽ".pDghnÕ~QQ=Nr@ ^2J&tfŮᒦ0Pq$HJ끁`FdVvBm]T_zFRk/x<҇ph.% XfeT7^(G?p~t-eUGm "*u ;)BIh~^!hNKFy.F` .+,x@WY Uz/D,LUƄ3*NXeg%nB↯#ڗ`xpN cIInM>pPI `g,-בZjQz@0pFX UDP' QZ4t%H3)²,{F 7LVk3zEeG}?kkB8BU-CBdtQ^7HB-kn9$e4~\ۏD#8rc8F8 uV)$S\~ \C Sա'ӡDQix):~XجA[5VKBLEz5rCs;+'M҆Z)Uh3f(s"dBTUKKj$,ݢtu09vuԐ223ٽǵ7)0u\7P#i,M A Qv' ԣ8؜> ?_qC\?ӚTB Ũ9.uio[O`~6xvRB" ߧkIb]dE"T3`u pe}M;u'Kl*11VYjJx]HuRhKk5ӷ-XX[gm]H8&;W@S.,)2u m#޲`XazFXzFnFkTgoSk*[bg+EJ{:&- C۩ZCkw֣OunyCc5s6q#9x7||Ӯ7t7TkQ%t{cKM'VA4\7R\&yt?b,Z3mǰBL)p^IܜT@BK= EGeցHHs3δ[+o}fmȣxRUH8>;SOz9"z_#1j-MEy8+Uköq*a*]vT )FGG_!ksv1pV7u6xSQҼRF|nv.`܊.;dDbځ ]eβgD#<ig ~y|П=w7ߌMgj:@#޻ ,;u֭Ӡ!b:tLƃ5k荅B}Ɯ3 VUmo>pV߸yMȥo||7ZF]jUWzz֪Z;*uUMK-Dc;r 3jaĉ;fh#^yv;4uF>LIC3 BE)7"GkjW~ \X۸  r`EAq: ]!܆`, -1V=thѣI{gɲ.{?*@r-DkB(܁H" YǢ{92 vn%̩ovro&J0I|zі#M'1 #үZ\?j- jH%r+1Lbh.K$++#- ;H.ci9tR<$#8[4FϩT#zЫ| PAbr-j|K8,00PK ?NV2g>TNl^`4Dqm'|\Q*\hMdHrbL㰴k& DBfejIT&>S3WaJ-( #-mu˝,,ҿ,ںÐCÖ:H<cqߴ\xُ*qϊy6_F9:"ʼY6(Y +قAp1դHńTA-SqBN͂ ViFnmf(;T_z愸Dh8|tͦ28ᐮ$mU`@!.JX2lUn3>y~qCvnNJ3|`~^hٴ~6eVP4X%'FVJU6U5=V1eZ6q]wӆ sӌan.dl {X ֤$ytB݄NfC[H3V)Ǔ$nWlQ_¨i!.99"HaW$ Q%P)13'Xfl?)9w9*jMxΫ^A6Y gN/ӟs``SAPb~rvPHQRV4Ij`S,U"xs;a"LatJma'ik:ɢOv2Y$4)<`hʄ~׉cTџ,K(+< vPWhPj-D_K+6˘?jW8 a\]R"D V/Ret c`V״PfWL.a&L%-8kȸF.?@,l1T\osϗNMvƔDGKhzo&E JI,2ĶI% #Lq5+S~!D Sm[S#jIqVi*gĠ|1 qax^icfy"r\Dlsl0YTʴP%cбE}~I  :DXmW$:SpGh6㚸uŌzt3/N)uuIgfӞvȅȽw0 ȲVbQv' `l!Te~5sfم9u8x\@so94Thfzם/$~XP?T`F~^g}#7d8#Xܞ6kx5&;𓻆_e{8 YRLo񥋈r#{2\E"$ XD*Ά:Ӭ-P_n,Nfr6Ի (}ZG;2(P9>mL~F_+mk%ӯ~n n!$6Co 4T>ba (F@.0@<$(<(`%*8p'`"p= 3pS*<(pS@rPKqJJ<d0 0N P pp P 0 e  0 OVA.#$rHxf0nbH΁ Ab#-"bB3+8*b,VkԈ?6zj@Ђ+wEK쯶,lQ$E! K ΄d#eb 광{&Qѽ1:E((fQ&n7~C*:!VGMG܄QD/h`qr#1Q#?dC#?A!N2$l`8rQ&5$}gZhqlx$yjϞ!y$K+OA:Am炅eI/~ѨKCN$r- F &,ɍxIUU1.BR!"!H<Ha?I<R?ˁ BaA=( !N4!2@N!G,S5-*¶Ab\3=/2l60s˩,ԣ~<, T(TVTN``CHAB4N N>4L4޴R!" AANX*(R ,l3=D^sHSp#JU`1AtM "LO`NRRh7,X;YpR#c@:B"a˹DAC=a#˿|˿<\Aܼ̻|\<9!1Wx0ՐRW@$a-+]EAF!'/]CKM]Q}xzRbZ6F!st}|]׳!={׃٣Aٍ]ם=C05m1nHN_ATFD2`PÝ]=]}}A]}w5;[0"8~㝃[?A^[<~ɻ;!@a!1'{ +b QL`k:r!A>t~%@v=^*59; q>!A A {~A~%5FNn*(*B̴_!8Sr9s!Ea@H b.,/?T Ё;#C-7xqMMHUr^C;'WX23~-"4AX8a T@"!bX:TЩKnt,`] Q\x0tȉ;xF|Nɑ+K,ܺu4(ҥKR<ӄ6Dj[4(wTbIZ5V^z5YK:s[v'P-ώ%Ι BbBt2 T.2ș 4dT7JEVD"gԊPt#t@! vdObͶ~SMp.mkxbMŢRJ5m̻͹{ϕ-ɦu+Zp ~TR͑ geaPаZ©FAe 0D3:ytKԽCt?:TM>RnK;YՎ7;E5U5HpuiYgVjcvVu70|J' 4,H66(٘ P:%*JGzݩG;y˦:lU뮮"S?va+VuolZ-3Xzror l`啾g}V8!X#2$,;(BoyÂrhF8` dnQຒAkH賐i BQ]IpЀ :D;ChΌV|U msw45scƨ7Hc%EQfFx1}W+Oo`Qm,ވ"o}K9doj)ki)ׅeɗAl\6G}Ւ 5iozhC &7홍TټvZy׋';_7=[ ܧ;UhO3,Oӡe'Fу<-aLZ'jiKҘr$4\ SvD5)9ә P3iQyԤJU*9rZ(65RL SA:$`ָI^>s $PC2g$iGK^+8-&Ю|V_=gT-G(g|x_U]F:FT8GTD9thU5 ,hs@ $bu Oh  @wm p  0~P@& u*4qq+,PoDekIG4`rlV5g6 \`~0'i%\`,0{l  wy~m*xG`$*0qFw'P0nʈ Om\G&ڐ\( `xA \[d?rGU4ERɁ W\19,+tЅĠ ϠsT ذ@+ z 0k '" P0`5  74{"p x Ԁ , L H0I9 O N LviԀv$vj[ٓU ـ \m$J k=feq&]0IyXoˡ ,qD tt4 @!6pDgtMcHiX0t3$bA B'(DB",YBljBY!4#tB9ɉ$P)AߙBi͉)BHW'(^'d;I+yuq+9r+)J$  9 j jJ"fd6d4em{'TF 9JHL'IVmg`L /Id&V(xZUFpf[.c9aW#0cdGfpdg+1l#Ii VyvdXVY#]3qCFgf5} M`\vgDc~դPfi%p*&E([ TW8yLtezHc+qi`U7DD&L3y+x,F`d^dg2$ŢƧ8%8uzå44}je^FhL2b^cKycf2*!w3jokZ eJ^9%aX4ee_U[OZJY1f^P*#[4Lfc}bu2Z\euWv~:E b8!(]"XeCy1 PKUkU+W bZMߥPL\OMŮi;%^M$g/VL+ze$;~+~ ~vٴZfA ӧӗFeZhh&a(zN3F]ZfJTEjYA [XCP I23$A Z~/90yP E_ !f;zfS:+d@b\yTq$ a6vSSc.uC):Ty7'ԳUGdGqd&ypcj䣳ɻ@t`xc[8DA +Gv' Pz`u'@ `O T’ww7,@njN=X _2*Y֔:8fu ٰ-z:m OweG@LX `yv ȞPPq Z , 6Ԑp]C Q" '@F曵(k P-y$A ہG: % $l9p 7 A @8Z(@nK0\1xWȊr&@ ' OuxͶ7!tp@}^iZ1(&Jse͊Dޛ4@֭$ݠa}$aa0.[:psk2Ax$W9$ZKAɛFVŌZ y Nu4^vVnFKPTOO5(OQW l5[Tz;A CՎZae`*JC469ddۢ_աw,~CE0_zMHk-g fleq`h9DxIs{_zW7/^2kUVx"( Vgn NDKo8I?f%!q4FϢ [HV,?ݿJfgO/JF5VXRN_qW>[`-P LEU\РAgགྷWqE/nԘG'~XIA8r;a RHt6].ۺu 4[.gIdTʔ-isL1>ĺaŎ%+jK)iZPZQzL*F@5h.Z0щEyujbYa:ަv9zVge'׎8+k$_ ᎓#':+;zj;Cl7$eϾΫ7΅ysE,>9M呝Oɾn0K["y'l:-u T(Jw흜I')Y9xu:Jv{BXO(K6$c+/*.xR'>Pd@IywTr'uDJdÙ9:s'jHaxNAT"ͫ W GXXlX͊d;[)Aw9IO6ӒQ4mJRQըB]b߄DsԞRiJP;jPk]v$PVR+>kYuԭvNJʑH(^㞲FU% &IE#I_]d4{}zȣ"qGy_GNi1&F3%IƖa~dHs6Bbӳ޺EiZ_T&7(*!\kٓFw$ZI N=Ubf[NDH+#ؐe̫%) oR '~#+Y؁} S;RMZ-=t/"m HQ,RdaP2^зؙ g #J MS0KVtDɆ2d,SYZe.Qd'[[rf2YpQ|,yc2g8ǹ<rwR:G0q:W:J!}TNcZӡMMiTҒuOjWO:ҤH;X]/G7O*/gN9U˴mw7e)*yalJ;ڭ duTsypxbzUoj,%&Evd*0ž73]k3ʱ-@bζlg+bL]7\,Tދf支׀;-6|i{rt Q`үwNAGal؟Cj)w{ yӮƷSޞR@!׌3yU}^Ȅs{6Nw]{Gw6C_;%kHo'f];/t B.F/zЛ'?ЧW= vY~TCqD}׈6 =>9I0/ [ڷ~ڿ>}샿EdB[Y>oL߲..*fRJ=\=\h@]X|@׋@d@׳@ T=E= ;;ۺ 6h82J͋7zڊ ڋ@]ATӃ A "k]X%Q<B&L&m7r"Ȇ|&M2ΐۧb+ NN(p8R- Uu!3 *T?#SP;IQTJɰG˯bDo:m 䛥/L.}?ML̶!ś#bCb* UKu{` DhZקZViCV˼SPX#Q 7JUCa?u.#9,jbKNJB~50#X5(ِZJ#Z4Ւ5Y(ᒖE5Y4=YWCٚ4 4YutHkȆ62"*Zm#ګZE#)[=#ڱuh[5[AU-[[%[ZeۭUܸڽZ[گ5[e\%E۰[Z}ܶ[U(S u` r@b]r l@u]ם]ֽ]ٕ]eu]ލ]޽]]^5 -^}U^] ץm^%p]=]5^e^_~55_MTȀEHD T  NW `^`a VN aVaU^aaFb bO]`j` wbV -a-.a-c/.c&c&4>c3nc,a00f8bOc b==b_ (VHdE^dEFEFdGDF>dFLGdL~ENOHMKdF&eDNeLdE&eSd[NeUe^ee\R P(;itksnap/ProgramData/HTMLHelp/Artwork/ttViewImageSliceWindowClick.gif0000644000076500000240000003146710534177573025034 0ustar paulystaffGIF89a  ...,,,***(((111666888:::44499E22599*FF,44*===!!!%%%QQFF)ff$@@,ZZ(EEu11=VV(xx!cc&..H..1((*pp#..F!RR$oo"BBt!!.II""011Q"")33Y$$-)//>@@r--900I))1**A))-,,.))8QQQqqqzzziiiUUUⲲ騨]]]ԤYYYMMM22Vaaauuu<>UNN66AقNN66DKKz>>Y==QPPUU99HEEf55=JJo99A447YY==IQQ}FFa99=PPbHHM]]LLY``MM]UUvXXHHQEEIAAU@@J88:HHf==BUURRUU}559\\>>LMMyEEVYYMMj==E^^BBPFFXZZ\\jjDDrrMMTT..33;;[[II]@@ee QQnAAMFFQIIULLVEEMJJXRRqXXddTTr\\PPeRRvaaZZLLd``PPkMMaOOt^^IIaJJqGGhVV..5DDr44IWWOOsCC^[[VV55%>>#99$>>$BB$VVyy22#SS ~~hhff@@!ii%%> ,HXh@@JhbCd\8\H@$=40`-c ^Q@Ï6IjRF/$i5V<իXd B&P+AB &LaB]8x@n ƪ߿c)T  @nI?pЀ[ְaMҦO@͚9ʦPBo+ʻo >PpM8ܠY X y +F};ONȑb_޴{S_Jp֬xlX~ν@tXkV7vBq%=u0yy] 4^}_g!kyQc@z\۩gN؃{~P>y(YS=5d)ah5i}lF('A~]evv$tOR (O<$`'&]bc{}fsqهwݟJ|9u(ZwV;;b*Fڭ>[㚳U#fkuyzXRxl4Xh^PB , :騃:t;cbJԺƩqkJZN樣ӖK*ȄR[+cs/gg K04y|eb%օg!"/v޲jl45,h~-ja[37%gnx1i2czaZW{2sG~Md ǡyqvz*jYr)ꫲ˒lWwgfV5|~YɅ; ׊gH7@d`RN2qCi+8yr+o兟n/Z2'AYpp0uT7يݾ[b1Zr^cƢW8ʞTWםY~0*-+868ʑI6Vۣ[ȸ"+ ZƩnukľxI0[dFiG4fǤ oM@B*1[SW"4Ml#-D@y*6*8M:1uT*Q8p5"<⁐8F?!f48jK rud a(ŸZzP#<7+81h'HIpj9ZL3Y,0lrN sd!d[ւ2+8`*'@ųx Ubel f-^њ0Ucʉqd{Fh2,N죨`-T07g; ǒMȀES2m^?\2v%eM 5),qi$YdX2c`L5DZ[@؀9+ns :E0=b=IT NXtsreN\Z,s3iT+BRPzjJMrdmUOjnR1Le4Ts҉r"; 38B N`"1oSnB2թX~@̑i8W q v>YnStƤjF-pkn < 5,O)?T.g1.Iisej *-peeuc_B67+]|,:zRCX/4N7`w='!]N WD+cqwCXY% C˓RZɦҲH0w>#pPX2y%=(.8t!ޠr߻䷖6s)^et 0ܴch$wVrk쇻L/:I'C^Z&\pxrvQ83.tV)h`:;#DqajW;E5tKk#y\6,6|y6c,A]躤ϧX:M uUn6Nh?^:72vxqŷ UA]sh8=׎l;B"J#G-vsAmCvȬyDu5Hhi|:?OvFUqX+ /π'rĮ8@д^97/v֤wpɺu7][es^\k!Ʋz#~—~nL) &qQffZGcB}k5@ q"AH_ী1!4%qcn ~0:71KQFt~Ԁ'zKY6]qz0Wh1W`=x45gyLr o!! h~^"5!DXWkwrH[$q$BqW8Z U`Ss?L1AXNQ3XWTW|r0 g15u )G#~EH{W]c#Ȉ\%6膺#BTb {6vc"q"i`0sV$fx|E5v) ]"VPkA%$~،(D }#*Al؃?$յl` }1'w_ cFx09R]|2?5Y#cHXu5]aȅyWא s%~0e}]4 n'WtC[&)+I`v6#a@CPP}e`fa)Q$(mfM#A(+ٖUYjΐ x^;%} @x'4+GA `tix["+LCF4 hM/'Ԙh[i+LKQty3nuaPS(V4Y1l 18!/ xHB)Y$a7BCKstt{GMr"@rq 6'f$㡝=EvWBF=uU PDGCs]K~]'ۉ=oi-9ֈ hdr&+.Z hX6651N 9b-RR)c/G#TmW` Bn^Z ~ؒ9cNH2jq_=)H\~E} ()57k 쑊)`)B D906Ii-X7$]w-(5VTsc3 iBnjkP0"ʨ:0G*xa5T=qqZdNtyD6sJv%Fk`yh93F28P[%D{Ý[I Ϡ JL5pHAy'S0BO23dw2c@j/>aVŘb(꥿r:w"|ɬ Юf.A,Hʱ7V0l*GBNvKi9|.{ڌ0P[9<@;-sMlYXeI(;bg" .{) V;j ه)Z53>׏uldqnPk@Iɑ.xɷvddBJҐ`(IckЊ[fK*xzHp'k,8_{LQ8,#"ܹD;:H7+:<RAIiU{xn;&>M}-zN^)S볓aܞ{ Aݨm]qxBln{6$wnert% {infcܾ)\)8`N R=  ϰ i  Ґ s 0 ` ` p sp 0 Ԑؐ q Ӑ 04p 0ݠ ˀ  ` @ Ԁ p sp ` Ԑ o ` ӟ @ ip  @53 "k (Q:O$cɒ'e-)KSe&Q6gٲ -9l1K uQ@Q8 C= i1ʕcb9Iads|2 ZU)S\-T(?E8hAHqΥ[.[vE `w+͚,^fΚ9#GNSri"C,0cl8uZܡ("TXMai(<DZd;C7lLWy ?t$X7Ni uЄ':BYg3>=U7$#&rMѐɈim*(.1e(l ߳1JU4RCV.PAEP@.)jij,bV%H{rR:P`H{[#hǻ \6 xs's^v3I DyuT4 R#45ԑ:805pDs"Pͷ]` &`CE -@:i>.ݰk| !a9s"F~yH{/K$5 nJLi0`1'Ӑuʬ3JB &Tj(($(- Bs`z+C[:H s:#JUȮwU^;sΩB>@5=`PYV5# ".eJ(Dn)ڣPjPp!) 8J}z[&4LxZ:֔a_g4cK;NB^%b*\L>+bXVmB,wILSWLf(;B@ Sկ.Ff?N+< `=dr9:6[M3 ȹKܛq2遈1*L8{nJu7 {Ml:V~$%c8QsԣjP/jV.w JYJj}"YAUB!% =2[Ўx :k1q_ A`[M(D ET1@q]6y+Mk`@lЄn8Dwζ~]!OB]Cbl$i p@P 4:|^f$ri(=bDAC &ʒ!bB8 @l޶)px`M\u@Œ=iFhHj vq`{%9>bq 9p OjʬC^0yERSW^Y^|Y$b(ZjR;<R|!KGc 0Jׁ=Oq`\{654eo{[ӱ 2L?9 (c$/;s N{M*SC?©?$j@뿁2|I[ǰ;ѹP(:r$f3Ӹ!0+K/~# !K5-is:1ˠúC`srCq:9B) #ǸнBrs40(tR0?8G۾< %՚[ .A胨:= :z9 -L/ xw[8K4yCb dH:2I+-?p7Ȃ P= l O= (VeћE?h+N:؂7g޾u 78qtś%fNc!)gseG"5:*+#pF7xUu3*a 67pl0!#I23rṪ9gf 9;;'*Z#+Iu/벼:ϹRZ*^_8j I"BkjP{7رev"B*eNNH׊$2m)m\n>PnZ)?Ђ4(+(y-i$*0@ z41H@4(R ͮlttqBjX!=Y,<0:0D)0--)>.؃{ x9I$$0f`\kD >E9[.^5G:,k@.)3#GMݧc,:':$10!Ђ(@1%- .x( 2Rvs30K*ڀwz(GCOxD9zKVzeew(&v/Zu)ʠւ-s.=qޫ H9xz7LXNry|Ǧ#( d$@$\s,P%o{L&pۺ* &@>('pU~o8 N AB2l!Ĉ'R\'@Ǐ z"Dhf&H`f8hP.@.5j!AŒ@%-@вd ;9j#,xPŠrҕNcCmA"oS*~5F-.\`dYz ^0@PqgI'(IJ;H@sT>TNMlK2,h ̦Q=AA^ |-l? ,H6)@4tNaZX7 &oi]Ԛr J0 jI(H V8YoHp?p%lO2f|qQYFP Xzs(c[qf&0@LN I7*`}[ }h!~z8 $OuqL헚L0ܐrjby` -`g|] #`di`Mc0dԪ[)"d ?KȪ A`?Ed 5'FLRS4)UTAS{U SjRFcS 0X%0v}fN - ;itksnap/ProgramData/HTMLHelp/Artwork/ttViewImageSliceWindowZoom.gif0000644000076500000240000003534010534177573024725 0ustar paulystaffGIF89aŮǸʾƧʩŬó͢¶ܹ̳رӍգHVXRjr{fewϴ}ٜ˜ف{ʤsiV]n\[k˶خʬ[ZUqnL]QjowzlNbrG]Qıoxԝcuvy}u[dj¾ڳzhԠgtؗ{֭n]دָZds[v]q`h|{_sh~Wu}Xgsy~w[eemƽbiuĺt|d|NoĚ}͸vvAVvÛÑ˿]{̢Ԩf@XŸλnq{ѨrŬba[ֻѨ-34O\^Ⱥgth444(((eecDDDED<&ƽ10.¬ÚVUM^]d[ZYTT:XWQwwvTTTHHT@?9;;;::6TTz;;D44; ..1]])ee5JJ.튊ii'aayǹXX´PPi׺\\Ե}jggJJe``KKMMt==q88Wts' xx0zz-((A,H*\ȰÇ#JH1a3jȱǏ CIɓ(SnY0cʜI󥬚8sꄹezBeMh(C*]ʴӧJJJթP:tWTM%)zʔSv۶pߺmNnܻvƷ/V|"U+^̸ǐ#Cl bbWMެs1S1OsӨM%T1P-[[._~"qcG^~t-k6fؓf:R6%^_۪~~pmI=*_r'b)Fg `b9]Qj]EW|ז= b}罇__jg#X}f bwv]t )K "}%׊=톢n\|\Z0)dϑ1<.hs@d3^}H߉m'W}Y_h>n seApt-kEX{xfq 3 44}Ș Ǽ탆 V%cƑ^+ H ru00bAOLÈ2.)7 ~óPbbpTV$a$cùV Db`ZGF` QRS)bE+HLd,"QV*\G jD#C(# N_N(FL8fu,NZf40~VNc,1E*j#j(3 STZ 8HN4Ce#bhf }~\rĵQOm",֩~ 1 +v K򡬋n\ WNʍh4\\4"S,e[d5GN̠j r%)uq{Yo.z 45iۚ6iDʬY{A ՝zE/sb]-1|Z1CkE"ƯBL*&dA5McC~0C0Q|s v)[9K f!:qaw6Ȉ bOڶN>߱t:jb邴˳GitwyL th57Fɬ&>7},5#u-مhI1#4l4LUUas+Zpš ?q^qiQo(3>)N 7SNjG^9b_N bx,ɫIqmsO1՘5)DL wվU[6FAm]`cV.vsE9%Gu'kd̼ԩcў^9 URO>j1\ÈufIV<\*۲rbuQkrs/QspoH9,q0& dݎRĨuҟ>`=q;e@*P3Bٕ3V'i2T&(wF<:)bt(VɖicQO{M:j;$Coi^G8&lYAb8sAU?^|XxN5j$.k5'3Oaّ+:^L!7ZvYEgu{vz_f[]vn7T&lR\X^WQAֆF9j]~TN4&!(wRW S$W170ei税&׀-Im= h(&qjKMis7 @[G!YF渀ah tc]|U T>)5))^Lng>~Zw#f訋h;Wj藑AE TT2xO)iAd? [L8Q{83u 6Ivuuq,u0CyGZ8VL g0܉Hf16yuN>(oi8>#|?'*(I$8En,h0i&ńh1' hl: z1F yi`ǑFo7y }m_nyu ^UY4eiɠI;{1i؅;$ypȏG2q77tTyS2hqkN_ =Y&l3: sw摾ŢJ(ЏcUh{ hZIY&<8a:Nc?)iWlkiD)K m:ˢ5Ƣ#*X)(3oqcYo7ob sGR)?*Xy*iG˩&*))X7nW re# bz:Xo ]x]ڑII3RRmy!@#Xb "eHCY=ĦhDrU.WɊ,G!0iIT؟1[ahH=z1~ZG(|ɩJ9Z`󷁙tq>baTwQ;|'ViR0[q&wm15J$|f ǀ$k&V 2_-k+XuvmgAš};JXU Ǖr2s'Kl*w kpo"i7[u5$hv)FygǪSB_6L pכ+nc1oJ^xc+%uZiɕLRn.Zn;_:7e $YfnۛK^娉Ay+"kSdn;4ivYkpъ2)JC9 `;O1{Nx4ˑVE4[d @;ޛGFbP"Ys:lJs+Qg ׫ʑ?LAtWQEܸ*U̹RyNٚ &j>8&_\bƹoi=[7g6kmlvL!g Jq+hkL y;298)%7$&y`r<I,ʜX^W,G.LXtGrGS3y{;#0hBBQ |&)?EhUX J#EiEU"ܟM[>וIt b+TslǬ~;bseƀʂu 7FGIg5/]+i4<ǞarOnaA;'l[f(uSxq|nb>w & [M̉#5E^mϤ[U~([Uq xT+Ȼ*J /Չk˜~?5–ʺZӋGlx5Ng!%<,4 9Ҁ$kJ}#.ŐX]~]bWDxN"<Ը$wԽA&VO$%qǯXrW2MǤ6%\Va9>e $ʪfw|)toʢ,$q=c-ݿǡuG8ky}YLůԼ=e3|-DXs-,=ЙYDH&Љ4W2ϸՔ,6&ͲQKFuLj}i:akyėֵK$6ҼʞC6 l/I%}:X,Z3KnIDԌ ߻%ݭUFȊ zM/{v;; ؁qt8Ãϩ֕|3`鞞B 7-ñt.`v@2;$}[m5pG>>ǹ!IÒ+"\6$7\6>CH^^y1\95,Fy (s.O lo4=^1.~teh#e!foN+ߞܟRDz087/TjئG%#8>BGE\.n+wrM/O7x>Ś쑔"\m)Bݦޭũ\wfqj#@l{eXCpǺ3ULJ Lx/ B 'M 5]W/G#<5cQ$6$nby& g}a/& |Nlt蟩s'j?9?$(d0c"5)\2f) ;%2ɑ: ٱ$ȗ"WL&<ٳ㪞 %:Y)dB4=A]kWS u6gϚv!…!D,cӗZ$-ro" @ rHZ%cY2bjC-l*3`l_3VK9F";N`K1N,&G:w z9 JƀMP@$IDzF1xpKA#ﳐ>)ɄhH&[,R_+nS ^AJ-"sx|*5ehpBI”!dJhRlUe|2]Y+^pF!͊ 0{e ل,5m)_RT[xFss񢵯5vЙ!cI0)y¢0x|{&IlDbZ1ZpVW^cw\N.-_yӐ lydbh$<aĐj{kWSUSPޯXwR% C}LHGכ#!8oړj7e+_-xPG~VP$\6=>FQ^f 43 :t(y<jF)=!+{RH&j1;TP(W4-fqzӂăi|4ӰH3Cgؽ&xWT˪6v%Nќ!9B!pA%2Xbֱ#!rJ瀈uKPTӖ2Ѧf&葉vL]qqzI9j1aL6o:eSFLO|XZegF#\m^ >8;flx.zl#2rId"P2'2i>Lcu9hN'mT}\`'q&Đ!ouBTv՝bSPL5ie*S)QHfΨ+/zr)ڢc@-b|1"?p=@ :.K'YN1]` (۰X0l8|s0{~j"BR,(" ?ݐ F@FR!j=03 3g2E0/Bs@̣AXٔ=+*V;;:%1/?׳@ LB f t^r'^)C|0_(C3"Ó KbA=үʗ᾿CS<$LB ޙ!*[$82㰙 J< kzّȸWA@Q̢.<@Fð䚏%< Q.S/+][&Brd$b0PŠ1 @h#}0r.q̎|a!bp9ȾBF9TL2QpF}1`Ļ;lH <|Btl%(P4FHGcG$@Dz`ƛJ&ʤɅ \IJԩ²Ȣ<'bAelaI<,Id1D8=dڰgCDJdϳ/¾(隧 Sgy./_iSB{04? $+ǔX&<`T 4'JûR@?`$~$JbFbH|t\G¢D'|Jh= aB LYLM#+K1f*DŽr -AKH̗3@أ"cu$aCDcrOk .ӌDAM4>شNǒZDE +'j*»ET K\L3 txdyL.=UP^=E Y<>"(h;{v#7i a gi9B -9X=ڡeDM4DVI&%D 5VMG1x=.h]{.@PXPg? dꔍxM -QvÞ$[%G65L0'1cNR;HDMy` U`$3E`(i 4;$ReFaaD^sO`g\PjӜ0\Z"7 me^ޕi`(}]f`PiT c C.ʚ-kgv7+%1ƭJ4X-^S. 2j^hb}EhYH> KDz1=&i^0h*s;[U* U\23¹xefe%eFbTQ"-_afTCӣ'G HZ\ئإNm(/x8hfOme10[VȆ\pU`kq%_ Le j+=dGr|&9}Tj-r``bijXhfqpVpVXd `Z`Sp[jUԱ IQ20fdFfEvHrIZ42!!D j=摅`Y7Y(i(pVo Te`i`c ]rYi )1+E9Eh%D` lSRphbnp#VZt.VT}&^^άa >DX`f'A6k&F#idftPtV?/"YY+ ӶajO,zuO5,ŹRg/<fWmF7--9V&epfnvop \J~jp>dC*e-O?,0O/`G"~/s_x!!f.8N",ixБVm!Ofoxjbqi0R(UIG;N.lGMו1 czmGrZz7) _kc&tWe`o_ y4H^#G<|m^ VXh *gZn}=jvDn'Bg"Zp'8 M`lp&_8mY R#X/jRm`WVk+g=erk{Db|il[!jbU \E)Zhzj0R-cՀZH 8pT X,R XY ̠[ 3V,OHG2̩b"shʪB KVbPjojתuLZX…wa p &6XV~yS, fS6ύG)VJ7\cjeRk] (6Ę1iҤ>ZcE,XgϊMZpnj&,^dh y?.6͛m~ͼXL= w)ɦmen=ٶQIZqO}5TZ4`\}]Ta%bqavq2/xWxw\VW{T̏BTHU#_ 8\OM:hn2FP"kՕ$UY^sS[* v'.YX.TRX^1K)QJ CHL2Nj* >*n" ;06,iL,Zqxƕh=C G,tRdmmd#8Vj$+ n Kk)PH 梔&3b*^tWCz4hl)dV07E44KC7޷ oNȡcNU/)$}Tɻe%0I5~҅srgD{][Ň<˺0lh$ FÌj#P=(MI\+ҍgP[^Ti]QYb} mF;XZsCU2w#z"i1ݖB4E7\d C|X $&X4-li"tŗFѱ^Wad鲷3-:hPZ Tp4.s޹hFr\"ov)m<uiJU9:Ul{{,ľn{1 jl)_ e6>r*ojH .am/3z!Ϣ H唘QLĹQdfsS?x+Z|ljK!`.П7jE"#Y,apD ?UBWbbȢݬ&Cx|TyYQiNkBGv$!EGY<L_,,rgJPA ݻ3Jk>X-[җ42T?p<F4N'ƃUK3o nh*yI$I_P4HLViyM`iL1^\3L5Z@i a^q8kFٿ`֙0tD |^,deu.4á9.(4KjDVP!hBx!!!&h/&xj!@@/*!*&/B0T!b!!$"! j/ . .GY@()@&/E"-բ-f-b-"/,#-,0&,-.+p(B0>AZ562 7"7cmG7#:b:#;0‚Ą(0,R-#>c19?#/#@#B 4ƙ++4C>$DF$Dd;#E^$Ff$.@(}))$IIIFvEK>@'p x'8$OOO"($/$Q%R #4DSr8TN%UVU*$,VvV.$@XXX Y&@%XYeAVҢ %& "&$BdB!&&$#Wza$XBAtcB h eA!! B" B\΢""04X4B8# &E$L%6*capfRB ̀lt|܀ A@t6Aq%.`X#ā4ՁAf2p'@. H@ @A@@D @jf $,kf@AHPA|dA#RJ@SNT\X@hpt|@ 8h-% @ @^@ @  Ā ,(RFԩ)橞)V8bdƩ*R$P*%d* X*^*fn.@F΢ *@:ꛎ*E&檮**+;itksnap/ProgramData/HTMLHelp/Artwork/ttVolumeStatsDialog.gif0000644000076500000240000002035710534177573023443 0ustar paulystaffGIF89ak*,,%(0/05465.7H=HHED?FKIKNUKXYSRLXXWJRcIWuVZbP`_Xhh^po`^Wdc\hhgfzxpogutlzyu?W?XH\E^B_MaJcSdUj]qJgMkRmQnYsYucqfvq}g{d}kouyx|ojzr}ltzÀ{y~Ďʙ˝ѣŬɫҲ˵ҬƹʾĭЮͯҳż׳̲ռœźκ׽!,k*0ࠡÇ#:X ŋ3jtbǍ C(!(@ $_bz :`DqG>{٣GA zԧϦPJ)iSU"ݡׯ`Ê6*VFs訪m֦;t}{'O^n҅f$&Aa; w'^@~qb*TD92/>}t )~9ְYfvl۸s{jܘ_^yd/4'?aC ̡7<:UF b:{& A4S?"C|x8Q& #@f%@ X A` b! ` ((!"r "b 'X$!t"x#fJxB  t RX'p$ K0PKhai!g"yLA!||`pV.i vJy%` *d%*袆6zR*e z 7)'hP 4*$X0 iZjBYip+gB_`ͷzĘ!!\"2u@l?⊧ dڹ.J鼗Jt4%$$ gy  0') zt%x XAk7ȭ[n~)lK/ .30SqyE H|4y@(Ļ_0pmB_w]~@J=f.xA\EhZ6p| xZh %` Yش* 8lAGX HJ :G)D !mP.Ҝ5}.%+!Vn~cS{V{]WM|g/d*,8 |p%fC p5ԎLǂB`\fZ0&@k]6xRjW9G^($=: F0` W X)@d'*qv/gHF$ S !5(aSA^P?b$%YY8,/E&3 n _/8&2 $ 4J'\P!`D崐kxtwn YA^D,% l/(%πg>z~>4F;ъ~'h1!w|;P`_,P?y~?3@Ԁ=IyCϼ#{}s{ۻG~}wzz7~׀|w}7}y|W~G~}ǧ~WrWuxw} h|w % h|=({w=x{GzWuxǃ:(7H~w7@81G?y'ǂ"(Gxy(~|~W0H (^h~}ȁ{z~Qtѷ؅xW|"؁X7gh(bXXz-}M{w7jpEXb8;H\]Ȉ؋aLj})XyPA (yyPC7tNj 82}4)|ƈ8NyQP ppps0WpU0Wxsny`wRPRp*9p3JH|Gx6KeȎ(7AD@>pRA0@>?s AEPDp p R-PsP~fxa8vyZz7N؏ǖ}E@pm.n`1R@pAyP@/ٍx(}Huؐ|ٍXK~ȌȑxE0DbYW0w@ Вs0gCJ 萍8zXAX8iu鎨xP׸vWpYx%y$yw@y0,9$y(FJ'J*KzN:x}N\ڥȍyb^ZYC@C@qZ@@q:vxr}Z}{w꧄rvڨ* ꧐ʧ)pЎX>*ڪڪUV:jJjZګ**:zZ͊JWP|Сx?eڭ:݇}Ќ}Jw :Zgw_P@Z{O8u`VWHIzd"ywxPx 誮zx@ W~p`npE?w @!L;(vpy?s DPp>@v@xRkI W J۴t[:DŽXp_Pn9-ptЖ>ɜl AptEyi.o>Pv۹{-P? P ۖe QЖQE@yEPRR Esțcȡp|zPWl"xy@P0| `Qu-۾țȷP@l-* ܹ#k{Qlk ܴIcG lgܹWJ|+sgtIs gp4' suM79s1Ls%t''vt5r,:<<,5,J\7"sY& |XMo|+爇~helxdmrxF))=Qp>@@PVjo0Pli|klI}(P?Vpp  @p@0Q nŦ։). p3PsNP-ݩFkΓlhjiʙ9 wkw}`o=鄰ݨvxМZ@kqj>Q=\BȂ؇7~y h-lLt z>ѷh ,H@*d@=|80+B,Ѣ@;1bF;^1%J,U%ȏ+c49e̖8umZ#Ϧ<)E%uQ-KUӾ x+ ]'L~j8%㼉ksy^Z6udz c;b_7)AQ]mOg[<^>L?So_o~o{Ϯ94/;(n93&+M(͔rH04P`D-t?#M۰/ o3밸 F qB 76cm+,+sL ˫R2 +5u|s#+eSƶˇ $1ܢ.e쯸D>b "oLζ4/"Tt N#SgNE(Q/Գ:!۫;?5:DIUKbԵx ķC:1ABQ>E]yewC^}p{wtޅ^hgMH!+ kuUDmM1N.㌓Q1(#0ZuyC$B9Wvy˸ye]ngv:hN{$DSOAѐ| DIC V ߆mٖm`6noov[ Eu~-@DLSl3*R5syޜsS!XJp8ц4DzBrȆYdcsWd%/)&g[>:AjS[*1`O)8QS2d UilRz*rRZSE6կl`;XZ5#IaXu j>!c,AJ8gWюvcHE]k;@@,S&@4HLDq !pQWNrs6:Mnsgݾeorm$(AM42DŤA TC}_  `F jI;A/ߛ,gr ڧYdɖ(#S&鰸.މp^D;悚r^$J%h*U(FPUJR=Qi-gcF%ȹrj,& ,$>q"(5͊Pqǭ9l3L=qxs> t|<әф4|gF']^/=Fϙt洢fCԣ&'m;Sσs2CX2@(5}=lbFv^ ق@lj;®}ll[;Ֆ=nh7-m[׼6]w'ƀP C+ 8B^p_0|/8 1B|PxqO 7xU>A(B8C"(<Y.wk~sZ/rR P>9zngG{ɞvo{.wR(4@!|?xG|x7|uF;itksnap/ProgramData/HTMLHelp/Artwork/ttVolumeStatsMesh.gif0000644000076500000240000026341210534177573023141 0ustar paulystaffGIF89a + (-(('43/775+25. R o&s$,V5[Vk8HM^lT N8p V.xXiMYYWcHHHXXXLUUJRcJYyX[cRRtWghWmsec\sq\ihgjjtexywulxxxqpla`a&.=)+5'6(5*&2$;.(smu9K6E,t$z(mX0J3g3NBQO0I$R0L"Z"LTOTJcUjPoIgRmQnXrZuMnxxlqd}nsiTVTol_bSi brsUdxGW*2&2tsjrtTt{DB -jz"Sjxybh $"-CJLRRZ=F 1rkpUl&"4:[SbpdFGgVˣouwCUY%IPY EY`̬E˗ɵɬ֙͡ҜĹҰ踷㮝¥ůγû״˳׭ԹðʋƳ¬ƶֹ̬̹ѻ޶!,9@谰Ç;|Hŋ3jȱǏ CtA$%+ɲ˗0c@b7LԉĞ9 .dH%C,Yjɨ sGW޹+W=vts\j5kW\mK*VZfz7kYun V^u nǐ#KL˘+bϠ޼6/V;EnkӈZU+[x}Վ)UkiՄ+-irK&Ņ` kώZ2(YXe,џ7_~.᛿2}+Ogx^W`х `|V_xMj$(/"&h#8/@#WH7y=8?8b*)WJc}cjŗ lz(ߝgE ߟ6((bASmS vlϦIEULEYh!Ej*坺jyN G{Z!gUTXxl '}Z-N{E]YEz njEV)^UhKoKڛ"LpJ\p0nbmoj92&|2V -͖[,̧Vl̬ZE>[;lRxa &+2XR*uUSܧri:8*v؝Lwvi?jWSwZ߀*߶++W߉.D;s;n:n-Gqo`aVTeUEzt]l]|.z\kS]lEv衮oWtѕ^TSuv`wVWq k㏰EqdYX:-Dofˡ/_ԅ9^ZC󤐌cX.s`E' ΂{Wr8 fonOThJ:v6w#8pplKG21N]IG0<$#C<єb!gFK #C;#BAJu|D"X9E^htjH1S8 G`"C!AoU1w$swp! LxЗ㈠ <x1cdڰF2rV` D10cwìF+i6dcB@寃L@{!w.V,c $$a )F0!x"Yṗx0!Ĥ# RAE+mCD4G DKC ?i!sGp3G:T!F] PQxFݨ TuKpQ33*a$d +T kN֡ i0+)R |md*q)D][ *"ְ#X9.0p[0#w  RXہjHL($ $@]2p( 2t \ S4qQ]tx&\Ȑ.)\!\f2¢Ec@Ezآ+QmHa.a +@S T쐎# $d.݅Aw؅Q=@%w0F<(,@XHJҜX%.⡍jT(<V;*1iWF53+lHkvk]2^j2жHR Nc)jfR 78oP}ǧ y8#8կ[kH;BLJ֎Q D&,LGR7EHⶌG;;$Y2tpP$F .ְĭLvpx  w.``@KvKde5;QK (e5ڑRdp )T"p"u?.ǀU WJK A q4c0ڐFTSOT]T rRXzt q֠tFR`TKp xavqw_06\W1dS,D49/U*VCy=zCɢyeTEyT TcQ GBBPLfvxjf1UX)wwNF ga)8Qa"5 B5T,q5a*{4"ex JSr%&+,%QSǢ23#,)ceD@O'J`@ǀnbEwH7LoOZpK$]R@`/iT $v XP@kwƕT ^JqAt'^ pK\WOeLw0Lub` B!gɠ P ƄP@ 72=È stZ!pX[T^Ȱ[6/qK^`NTmX GkB^-"H3B+R2'4%h2S3S3, gl P irI()PUKTy8k`\d1րƁ m;H!H٘I< U^)1A9!) f;"? ҙ^QN=x7 ]^w?(ovT9}oyz0PfiPWҁxUS ]w)]]uXSІIBe YМ!qfo)yڀ֩QYk|tGV ^P 0q'A!3}vt7HwIVZHũQ^PwUx{0Exp\0Zw@QY#阗!IR!@&@:ҤUj( (\4"dj c *!pPbK1\4w uҙw|}ѣz=w>{èuJ}{;Z=u >zZUZa`=]`::JƐt󫯚;J}aJ zTy qqPr irЁ: ؠ@B;@ D;F{HL+I PpO۴T;Q;UJQ+]DV d[f[d W  B+gKXD۵ p  еPsKN[;+JD9pѳ=񸕫+@ @ 萺к ;{k@  K  [͋ + ΰ˼ [[ ٻkĀ kۼۻp[;Āλڋⰾ0L@{ܫKKk;˺+-\ ›9 ̼,LkkE  [k̺&;0ŤŤ*%`np o|pqlq`uuzLmvl|wl}ǁw\džLȆxll ǂy,ɕ,ʓDž}ƈ\ɫlȜȰ\ɸ{ʀdzɺllˉȅǾ̹Ǘ,ˈɫ| <ɔlɪlɣl ɰ؜ٜlͷ ͱ̭lެ̩m|ל̷zl\<˕ P ȗ` uSiP-M! (,#.12M;+:@D-1=:9-EUmSWPH- WMZm9-CFSmNX=!] İ]l-dr]zO Pby fp}ՈMIؒ}փlؗ}Yց={3jj&MרMөڬ@p0``۸}ۺ۾]ۿ]==ܷm ɽ]Ѝ̝Mڍ-ݹ ǽ @ Ѧ @#m-=]Mߍ > .~] m = MCMײm-}ڨ-% ӌڙ`@B>DH:NI~RK~L^+\KeNTHe!)PS.\^!a>a^x^& +!d^jr^Ap ~ ِ阎P>^~ꨞꪾ` 0(^$-P 0 4P m 6/]= Pà$-.mހ<~@ )@ n p p  t f _NN (P%(pqĐ#`p )N  K + H 0 ?^t@  %PE0 F y L `PT_VXAP H Fk 0 À Qo? P @ ΰ P0 0Ӏo?s (66n=Xo0p @>6oN~5_**^\m" '&fp / -?Đ)?/3 _ﳟ? wM Ai9_ ՃhRĕB  ~Y/}6P0 PxʛZ#đ0E.d{1”aQr6rB;?gU^ŚU~UB)ܲU-mݾ˖/f+o^l!0D G"1b34BE) *,0a sh{mW[#}5:xͤq&0ōG͝?5@vm4g2T ȕqDSN?'o/F #Al$-s#qjIA'{TJZAF=.׺2:h++a2r/ʫeR<0 !a/%-Z"#L0LHAτ ͊*̳ꥴ3 }'>:fkx gbu#yYk{Yy!{{7;$Wgz՞pH? @) P-BNCDZcL$SYV@-1^%bt^UNcAu};ql,ya6"xG,NQ$6199S ^CQ >0E.vыЁqJ*F%fQ`aTE/2э0lE!$rqI6BZ1hH|m }AKO"4mX@|G6|9ѕZsJZidQ 1!X?ј2fH4vtx9萐j3PJA怐7&r웓w[~8$m \DM1fаf:/:Ԭ7OHh cH>7!,DidQpmnoLE#&]i k6=nyK/dV-pW0v`U_J2b#-b|y)ˎ "SG=178FyʵB؍|.?vtм1?D#f0dZHG'ms7o\ 6t/8 K ̒.c)%,E3W /0!h`z4\uꐈxƓk[U>`|A څ6mC_>+c imwPB-tFź*+]="Pݻvw٣u"o3 @p b i- 5o ӥ)oLC'yg@*ɣqȹЇpH6ۉs}P000wc]a`0uxw|HAv0P0vw`xzHd9{0@ uC8 t;2滑n*H8^ :A;(FZx'C 'a^ 22/A ,r `C$zrHz0h_: l?m L1@1@J@ez0wKL000h10TrؿDk",CzЀ(6: o1EXEF0)c SpCЀ+N5v " JrC+XK@_(_C_nBH#,^ǵjc{x= q@c֣zcXEcE0XEebcrdP0D6lG( F}0 r"i'<ʋ0gƥTC™ 6p,nᅤ4#^[!*-03+0<BdE.Ed>&:+# 4M5OZHXh(FN :vXZ C_(_]^   Zc:F)9{}g~zc:;6C;mnopfvfqPs~""!ʭ &-U]J, l^x. .ZEHPLZ:7lƼLZiNzDmch~?Uf0|9vjki隮mf雦iiźi ujg6jjxmI‘ 00e\aJ |bS%@}+ gfeo rRj\4Z@fFj%Ɔ鸼i_~@lv(~]̖t80j6Ɇtf8*2m.mf_d)N[K!*{&].ULhh\\~H[0M(b+]l9-,1k\P7b0ÞEhfvlϵ,_kFHf!׾چ&pԖoUϖ]PjElQފmb MX/-Jj9K[5h4H`Tw[2e5FnSN#Me*;øLc~~hF/$0reN=,6a:ff}!5*h7lN7mׅ]؅v]jo'mC_napk&|K:؛X]+eWd)qR]uR\u26n\2&*_f@E'vhfrhl&uxv5?i\iwvvn8]v=_fiH0wCw#~%Pk:w[ o8,-gkRbL`8xx!u_/6o`4yo5džQ̶Wi8:f:07z ?gH p![5 ex;_#,;H~۱5] ֋c Bd7\e/|_9Sls̾4߅k\5i֭kvڵi ]%b:9!mZ&Txεk("rZ1ѫ>{i&Μ:m0"Р[nѪu-_2mJ-ZNu RUq+Ek֬!\h(g˒edaĆƕ-6J+WRJA-iiiW3+L_=Wn5زgӦ\=ϡm>|JCW^=rKKV9lx ':y&EӰaSH]aHm5 Pp] 3._99(#jTOAeJTS1 aK!eTQI-TehUQr_,2\rR e$8ֈ1"/ys#,e/alX mVNoՃZ)3Sx%ie@c h" 'r&lj'ԉ'~ziuv)p%Y@$Z(fYf )$fRN =\ ġ=R;H dMx˲ 7-׌B %4!@ CEʹCTLPu'۟8M$PK5.JEHTUPU.ŕ0 /a!l[I\1. /s#27c0`K !SqeTnaJPNn݊3JNB +M Õ0)sPQ)d95P,F-L&P6ҎO O\J2(Y?EAiB wGeBEw)džp>%4!@i hpp@>Z=+>JHs6N7xPKsA^бK." `~y܄PܜT^MK&o @O_/[9UYՂUAULl!"-s1$R %Fbݸ\IGDb-@@gf83ߔFh0@֫`Af2-A{d45lt-Lq$XHkr3̐5H0iqs)n \sX$ k)0 |X+`p8[&4 nx7@3̔JLoԗިH `j*:BzcvJA=衜x^y;ZCQA8J]p|R.`H$<2;D@T BN*6T6l*; c2XqQN5(h`& H %/'S:. ,)_¨(&\_\7%գ0i|,þmpQdV*MAQј!I`Tt&7f6H[׎)^kAAxD#@z:G+2n( YC?2ͨmħD)9r#P\WBrAvw6v]Yfwԁ'ev]Y)w=Z.pu9zBM2Ry>CؔP3Fl@˔  W*V@"cȹt(Z#[6nt#e(X=|B~7dkά0WF`H+S{e͡(riEŦļYQQ">Dfmnv3*r(FJdԁLBPTѬHFYvLG"xOi*" aEBتpV*;ڡ`נ(;izde2Nipc"WlpPِL"!#H}Bc1+% g1&ǰ4~&8&±I%05-o}; zi.2H; 93^M[kʵȴ/]v 51oRZd$Hpa1.bMUʘL'\$pC ,a"؎{T)Eq{h[-7^-7dxfv 3 h5Ҏf"> 7,12ց!'\@e_"3iƵUx8Aسb P^}0hB4s0h_3@ RpzM8sodCpˌ *$סMǴ0iKeHQ0eTcxLmڦLz@EE QCQɥ U$U9UaLTW&ùN6dCU1RsZDDH$F@5 aN4arG1L<mͅ$-xFhVLĠiHEūN! U(PlʈCy\3Tй`hF=4jIQ1vMBݍA]# e˼!$Pde u1ѝOEؑ9QUT9ciUA`J9, ] XX<ۻ7t%!ydCH:&!:^6#~L:#-:Ouar@eMTlM ./ƍn ˌHԐchHV:lzDW@HR܂ę$T i@ @ӄQM ōYm'%~-D$DR.%$LBR&RB#L%5ZU-(M0,U EmPnt"v TNBaN3@$^ ~ۯd8X'`;5:4(&!`e*f6`5H&f 6育Xf:c=4 =2IG9FR/ď0PB*$)0._CryP(E^Q,ř8h2HC1  b> Ωd-x d))8B- k"'&p&ܧ~''(&W͆+ZEM$yp13E06e|m M՛ 1ptlsQ MNN F{\Cc:vc>ިth*le^&ge^Ö$brqz֦UmA--(/pPoC 0ERMHaRud7 Î0o~ Ù,$YoUbJQБg ] 6X@Z[$h$g}qo'1q&m''O SEljOwS[m^e[ׁ)#@yܲ0++pr.8hprfmMdƩA)ɇN'"o}l 2p܂3C1SGͩ _V_tJ0L-dy!I@FW=8<0ίFqqкqB1/4(t'(I] "ԑ9iqiQ)PN!41b[kW$@krmUo %юRC3H5PûkQ;3+?*S<ԃ8>̃N3k!u0AĤ78XǂCBpHQsa\asFgĜY$>ǥ3Xd4F 4oW_NI$IPG׹@7B`azś7,S|7 K@iǟ^PEa o2,h_G*3\ V3,EΌd zYc y 31~v׸h{}~z@D4˗&KB !Ѕ PDXO>vӦJr5iKRPaBKZhri):9 iJP:.UXbC!6lp@TnXʔU5kըfn!zŕ+z(/zȭ]3lצM!8 qfgּsgYzP{@kׯaǖK۷ڲn߾|f Xq`Ĉa #f舡ǜ{1a6xуH#CL\nG8dȺqlsb҃8`|10|+@[|Ya )|-ukꪧnV@'JЊI@D7iM^EeFN$%d!" 2"\`H")Q "'E2$J/x‰jBjJ'ZČrhĩ*8j*ΰ:+O--rG ˩.V0tR SLc>8a]tFiS)6WUġ4*UhYlm `CNj=f|[ȇُh.nE~Q(B3[IMBKƹ) ⴭ+aKR@i%+`ޢ6E/ԥ]C!h! pЀ S V~!쁚\JyS w[ qHn TjraN~.?*/l * a:.vQ?)쵬INj"*ZbfЀU0F'o&^}12̀ zA"$^t%H"I"+Jp@>EBrNz˞bHłJ7HBr^9!/PǪİP-<.b ]4fb.vU~fDOϨVPGjtuANj Al\tQ COC-f>ϳ1kz`F\8y1Etdˍ7mδ H^2nbI P!߲6.= W9!q _Fq1qoE>R}k%H%HxbF#II:1 @I$@4dhrP!ac Tjb%kM4ĕzА4?ƗrS0Ș5! | 3*omX KQScP8%Lh/vY@EDz:#3[ApԴZ9y*RxǹBCT:ق)HNB:dqR#  eu]pT(h} ǂœTpJOڄ'(ˏ`1F[b4@"` \NrL`ŕ`2DC B*lCASTv@ ́O5l Gl a Ajդ]9Cta܃;ýo8j0M窥vZF/@]xny5г gv.Ib`b4HQGf l8htvƯbVH%t0%&!+P Ll&hNNNߺ 5,.+ h̎0Pp)1ƅ̀^[<@JvY6ZxZ./P(,ʑh&PREtq~"AGL@HFB m}'-Ǧ԰Ctܒ9zxRLFs1TEM&iT PD ap&B+֢kF(B4cTNE 2ENUU\!Y h5jal\l!dYRMpE>7J7R  jh>a;//9:J1\6@2D%LFħe`D~Rj@:0"r}@IHfdf ` @RB fT^~r0/~Hb@D6\& jS-C&-BQ).j&nO0!NGA 2vA|H4C0O3 5UcV1j&rsrY-k>1~3*@ hj&e Mtc*Y)b(NFk.h1r~-Wxs0!:!Du fJ;98 S`@EtVτL YBk* 1 a uPkFeop04a \}T\5SRpbs7 KCD7D@l?L]2x! P@YERAitn&vd>`L@<<LzjRĒ.T&x HfI,g ~ec'7'x ejȌȃȨ`(@WCkJ$oV+Xt UzL/ʦBfOT((+*:Zˮ<󑜶l\ 7\\pGA5IT4"q%)L@%7A:6;J皮81bq6G:v>;7)YRRUTgʪhd A&8GH,NFB5 6*؃uy&0b}YǻECu/b &Kf‚LiVj4bq`46dt lJ(35p==8;XFrp4,(Rm7At(F]v8UsL1x@V j/]Dl'9XYܫpTXvFE(p NdP{(ЫL~1{! ` ҥ:A@|aC9PC WkV62@ (!G@|) UԕnIXLn,RP3!>x9sx5ᵶX䂨^]xi抣74UTr>3kZw&hrC::D2ܲh ;g T N)]fyWoEUd6h=5?u%$' DB 9H-F`Z D}]j~.9X8LDKpGY}, y n06`A˩elVpozoUxeŶV󪥚͚z5rW9e2vJ9`8wp2?A˜:$d9!C]N %E%%˜wy.j4Ϙy)9 hGʒ qM>p , @i#` R̒3kwlEmFhfBzuQ+oP{l ۶ԛ!{曽қۛV'jx~)G4ʏ.Ca:#[c_z `A Ը[]Ht9Lu%Mx汩x-3Ga(yf-J6E a>HD)}|dV)h xta4@\ WO$kD@okC(bBWZl51!SiB DzyeiQF @  =Ab /a= ']A=E=I}"q7nXNvuHw֑c; j< m9N;H .~])MxLyId"Dx~ A{wD5Q' ;*qf1PBPOȃ`ң`>( #!^!= W"P@)lQ- 9EQNEF9eM3GYF#Dj)ܲ CdK0G$DM7S3ĀsF)U LDN E!ȦW`3~ei1cɥ :‰'0` 'ؒ@Xb=&@D%:QD[ ‘vKvb&|FѶ|6[oC$4q)ݲoaݪ۱Gx*xrD^z ɧyp]'i7>pgo=o/a> Om^x^!F2VTI9yc7IR22t^EEy#ĄDH"etS13- DMtU?9𒥟Y޳> $z[-:V@$n&1׶EAiY3:/Rky kj(nK-n!:[D6m&B })r;ǜ(voUm\vmG.Bߕڭ^A#YU7/%K?}OO}OO u@@BGi 468$lr8[Sr00 l~'h PE.ÐxM`KT NxҀƣ|`3Y?֔ _obID( TBZ8e(0ګR,.q i"UX*hC#Fsz@:|t+c_""UqÁiObG=ٛvp@8@t#yvHF=~b$c iZRִp}9 FjԲieA/nR6mHC:fdE$(n#aiF1kZD ShlOw{cmJAbg%:v:-8!\ӧ@4 a0Fb,wr[(NO\\hSgA86!#ʸQD"E0YA%l9-rsu"Z+v@`%-yFy䆺GS>a4<Ŕv4"$M!.X l! [)&bh4_kđ4I4ZT^U:@̪*HTd ZGj?`b)߬ȥ@u-; J'a 7tug T1xOavkUP4I34 QLlrg#b?/a @.V ÐWa~~$*Qb<-BixbgMt<T'SҲ q%Mz` PPT% E4Bz#! "G> PMX& \|X ߐ|̡ p (@R}RYa~޲6Es^S.Q fZ*@_A<<̃tCa 0Ǡ `  a0 b@ ɰXap 0a LvEk%0&n#/dYn)`~/u+[B+*WP (p#p"+yQ7P-WyUOO Ѷpx &&|QyܕR+""U+>8uGsTTdUbF#xrFUNE`PS'!z T!i1u@!0g)Ih1@`7* 3$2>!жK CmJC5)T=`GR B);?!q X@9q pՉ#p\ #Π*_-u_]L*h }\Pes`C-+`Cc0 j|"4R$Jtw\Ȇ }-=} (W`J:P޻{ǰݲ  -Ɛ }oٽݗm̍Mٖк]z8}vpж@ ÍЕۻvܠkt&={xMM` 0 .钶A P6%@ UTY3#0Ã'> !`a{ ʠ#Է>},R1$ޠ_Pūh#`So+PA>0󵧬 ` /O /@ ! ]!Rާ\ K$# pN%f&{44NqJ9tPACCr3_w7x26RD@KzPd11 +`ܻ_cI0 {Rd):) `3ޒZ e B)`E?]+힊p]4g]($n]*wR8 Kp)RyWBN~:`oW ` `Oː_د?@= ؾ[*.%@"W>%# Â#,*-_$6vkȐ=d\SVZHĄ(l%1b`C6f7oAS(->޲QI.  0go\9zYfw)bА CeL %JHAC[!D?v PbŊ#BT(Țmm!$5ygAk>2sU'a L̦:ۨQ^ ZiJ8Q"Eh,t!6խZבW>ҡWOܺuͧ_}g?9¡uPǞz`jA^i&dA Pp.l Zh%[hQA4ҌBjI$v_v1aFȎITI+A*Ϊ}2KrSʀR .zɸB`k|~2̹Ja(ZlaHL3LDsT3I ³5L֔HM7``͉dkX V]-hSn9"Aw].A0 r),Ւ;/=tPrQg>hy[p6\r5qUweYGzv}QJ|@`f7D&t1 _<tH&#*ȬȃJqljMHC m*ILp'Zlh|s>;e)&J!{@L'?`Sa S[J@ >I.bʖ:ȣ0Q.4dt2DMUoPYc‰%HE57W ja Yb[a `{*É֞y3tRu{fvkv۽}uwh}=r ^w  YBqTڅl%I=(4G[VeEN,&a Iɡ[8  k1J|u Z{BLbJ\t&qFv(;Ď!@> :)@ޖARsǑ!`fkR#nokժ%<'" \ a /[[>@j,\s635ЁySֱp2|;<.ﺅG hؠ yGBa%i;I& ^E)!h6!@TL I7$ #$6 BxH*QǀA< -!2;saB`OF-R+s@*@$'735 ̃ /QU|[c8|€ _B YE'O8!~u&'IX电^= j[#h;f tPgDcsHEg"tW8} ȐtǿHa7ab$%q4Xf=8BDY! 0^՛TINZ VR=q rHP4  ;sB23MF;k 89fnXFejF.68t2JJT= %@)L5 EpfdK9% Ai1Jw4!v"28];bxg<!/}`k@G/w֤ &`}!jC 뿘/TcE),J)ӄ7*귐HG7 =GCє3*hL+_o 5'MS j~#ǎlQhrَ]XO弡䶑Q&[)&VNd= خ6KB̠&335@ő)h|_#i~4ΐԼ5R ˈa\pt)j >D >Ǐ㽸3q#-D CjRmSLGQ% 9TBIXR!.YKFʰ΄6ba+*V)Ga{KF9ɥ9P tAܢ F V+Al [4#pcf0`!!HDڡhxIy`׺36ԚUi"ސ 'pҭk:i֡m;5g:Ta rxgH;o .R#50X$-2G7vA!!h=T/c0p=q!0=42C 中Yj aa`hCꓔ# ̲9aI!@[ѡA(ذP[pM;"&h432E [E[5@)C:XvF=*K$K< j)}AՋz6Y IG)Bٳ0Bz"!k0T<<֛KщSHu(@{:Cn 2>#+`nRx28,-KIQ1LڄK`3:৤ĤPY-tX3X魡pE;O耒@[d3[xhO#FBz0x1bchh8ͳoÿ   ZKrSGBb(#ZLCM 2)МDͩ PEyv( ); ZhDHI!Dt' !u>,`HhXD" ݠ4$ȍٰ ҳJxJEt#FzftYFF=He?ZqTpaaK/q D6XI:W QC!<[: 5HH٪rL @QpddqȰH1 QM`_2b)ӬӔ-SI!*H:bc" \D0 .JRq3z'N\-4(Le}j5T8HufXHٖ]Y`p/,UNa$LLQȝvCфЉOCjŊHZ!p =i8(x30wղ .eLD}9P\0M6H/N}:"SaQA$B0)t cz" '9r̭\48j4y4Ps(^s:u-^]^t0^EUr =:^s#]^ux^wYA`Kg۟t4e*$:Hǃ ]>GV1)PM1"+|~ [( au`Z؂"x !:Uh`dL"B<3]ЍR 7 ,HH9+5A:B0(E2E|h^^`yd@dC>dDNdTyaEN,JO}c'23AZdX! !!`>`"&Z3&%-63jcKX h[rhO=  1V̀( [X| W`pf8W'L9g!@>rᠥcE6(7/LV M"R-حHգܛ'j%,3 .ZйŨOFK>~C.dBhGi.iy Bd\V-I耞3$<~Q1a V- lk~Htp't`hҶ0PDZQX a`NĦ<y]8(9T ݢ94N&觞,1#8"\4d==9cn>vXs~q/anvy)n@P9IT\> e-J4]OoNoMGbPכi H`1\;hBܩ /ab/ 0\h''M\q V[р0pAbcX ʎxzQi &2n&?id[Jh^(?n5i?xnTzauHǠ&(>?t[G꩞*60㈀Ho5s&pJOOQQ8zQN ʼn@B%.XyQIMkapu,q[)b_!-d |~ @<(|,sX!8([Er_Qc ".'JGS`DH}:{רrH8(` qP*@ s5t4^7wdoq6%KsPy~n Ci'fޚ.2pCʠ-H/zwz߄/zO0`*@p1  Me 0bĆhQE|Q,`$ɒ&O$@q {w ~sCϝ [D?|l) AUKΖD)Z[ V@}r(Y \1ƿQek1._&.^L $LX̤ %8q[i-QEcI{nk Zp,I-QbߥPK>|CQu޿xӭ#o~{W9;r?_ L3$ 2pqK`PH`GRZf &GO?QgL|Q B7|҉3nRc''t#-&x0B:0Xy QJ4\tSJ3cOA %Ԉ f@u 0fBE  -%b=/)b @ +tB ]h]dEX1 SQA`hcD-pqYfLj_pA$Ԃ!-P$keHK]ªV)B@bZV Qc2$#Y:GS=]N!px׎᥃w쨛yN8wNUQJ e(C IdBEQ7BEeK-tإaO7if8iiiLc] iWO7k8S>w4Q: `s 2'5.XxF !PK4Ű)F+ [܂4KXB /3`,)!`DR!qy.fRPVH!fPXz )0!_bk[ˠBHSD'6jnP M|Lg˰rLh2wZѥ؀ Jkir͠VBhjd N FC H Y$B;\Iw1iu|t?YzUwd# K*Um&4YT ITٚbSAqLnyATV@7}(~-,sK vʥIy몚dV |`9%:SbJtꔅ 0Z*@ AZY մ"?z.>ءVd(`~{! >Se,73($(t &a SΈ$LVRTc"E:5b-(pV`h3@&Csi"Q[C@Q"[pLAAr2)K/t3$gU+2$NrMCGjTTt,%P:];F 8̅6\MyA詀!jMbrF\J fP8\4hRjXDNm`4SL$l:Ё@/0EIg1gW`v6&&̏mtϫi9\:иy ߪIt kh$j&X_#N;CLb>R@5 e Hz 0nު5OJӛ6cN$Pw]-(U՝]ǓMC:*l(ˌ D f0 r ZDZ LtGVW%ikLߵȉUi'Qy*^cߙ0jy㵼oaik{aV]BnII}i'Nv^5ID oܢ qٝ,,!N#F@"ZɵY'.dc;\$e3@_DqUԡHy`bxߒtJA,/B lT@@K-|$R ,[ VؐŰl K!AO`şd E#NEx0@:t0`!0A08a/xG78Ԃ 9tC C aRxÇ}i`܂^\ ID/X.hOT-Puܞ1W!!“=|VXA,!AÙBxwԘ Hǡ_)Np-m5tmB; qEWr 博DIT0E yhxwp(N@A!mO$i<lQԆ/}}PW@B |D젴t^ E*Ji KD˱}Gq@bՂ G-@b ԂP C!x/!G0  B dWCU-8XOY߃PGQHW24t< S@'6/"2t @A\!ATf5 x_( ?9%}Ppq)YM] X5QqQp f_HTEEE!-F=JeA1POW54j؊] 2Ţ$ؑFB`% ]EpFZS)iT@BU7~)i\iYge< >]6&`(TTG?Bo J1PDFDb@ $d MDOf  T ?b!sΑyR ̇=TGCwFՂ7 9:9t787$V7T :.87 ֡JSHq߿T ƞ Ahx"^E'- 0c8` x ^ 9 phMa"lk,i\f Iʑ %4fi:#i =E4@L?/1BFE/\P9LnnDJ B؂b*JɤdT$ mܴ+ڰ< Dˉ9(!;|$:vV0*!.bV1kNϾ8nGP`Gc,"B n72+Dg pA@pAŞ4GvZAB=,a:ehd]f? !1]mϾPu}E-QNW`-XL8axElR~@` =$OxbvVBJq TY%{`:(pFzG9K81#81'KtFzvK<3[K46o8DK0k^5?u__ü^u`a#>6t! ע8ג*U-x!3a'6`awCbbc/n(5dwAC 0XUDJ68i/X $`j7,SlcP]V% ԁ$ :w',9&&Esk tcsRO vkMtYxrn儠C7P÷n9·M1>-:lX0 !*VL\E~,>*SP Zjn$S?\G;~GF83vӸ_:=t3wxb5x8@v(I6 FC1(f"qpGU F_lkӳfu-TeyN, ZEÔy/=؄7̖~softvC77pSb`pENDݔFXT'B[ll du1+40A2rP" )H4QBCuƍ+zWt(#:uKa_a5ui}P zU9VU ujSazPWsֵ{w`{X`_d0|EiѢcNBt@l1ZUVϠk5.T"Dի$Rf |[܁ηh vVko޷{;],ɋ sf=S+}z_=z aI3B$R4 0`6hbM7dtd`1Cb/ @0Re@0[il|1[%6H|!!|֩6F؀p2AP$p !uq;ɦL|Š})K>l*  ѧ TzʎһfĽDŒA蠥JX@bh)$(Ta՚X„d͖;7 SL fpaZ)t,^uֶ*V;u;nǀ;xKw/#1-LU`lD Vs‡7bjKbaYZ\Ѐ8@ѿZ-B2aEZ"1hAtvZTPHepa1ubZ&|TSHZ*uFtlJO3Q4ZC*ﲮgGGRp Sf# > a *L.qjp@leL62#i$x5 &xH&@!tZ;6cf" a Yk-:|J!fIcO^^컧2jՒL VmJL`TX9d 5Dz-"0a!z $f#B8a aBG̢E <TK L0$-I ѓ%+H;ԡ3xHOIYVx [X╷)*WIOPXOy9h G 62}@XF>̈VZBkLǚ$8 he+Y댳jj%_,b b D(ԓ{+>^pH?`-Xu _;P6` ~HmEQSТJ`!`BZl/IhF6HRP[0PPTKZ:Zژj,Ҧ7#ls P*^{JՎP|sYux' 3 {qb [V%hh [֭WLx Za7İt:L!kȸU|0URd\ϻ[@II͋{<{ܕ `IRP$6ҁKZ8D` ;*.Hs)cԋ[ h$@?D #&;"…*@Gp̟ M9AGAu@r7@lY@uÇ+WJRYQ0̰r `5:f [X',a {/!yec X}Wl.^I=S o'.D)t{0@ dsڢtyG,q~qyQ` !Dg{0Hi1Rf㲭>+$6 % M)(梊 s*MAyPs`ye ճЈzfG6?+q~泞ҵh SxG'ۄ.4b3/٘Bf{jp8:8ۖtһ-Vw$2U5Zl@wcrsB][)D{k JV0!1"Rjj`B:G(RfTDgÉ}U!G%{SJ4 |)Ȫ$#dWF8MS/gqnGXeX1`tG貮dm&LPJn|2T o #; 謇CNP2 r̪+-|˛j_b#}jCBWLW\g 8GJ#&= FB'" Ɋẓ-{P)ښmzE K )2"llCS!9F&I(oZ/Kgj4Hlkj4 Kbj>Kq #0B;(l΄KNЁ< 3*R!P"P!HZѻRw(0d6VHTN, fJ6V23/LL @ @&?+gdj7#I`Bk*R ,nDZ } <>R> s)y|E8Q0ȔzDH0"DĪ9PY e0PJHRf!7ia"SiOd(B#dp1$0ˡ <!(Nn0lb (AjaF?sкaPḬBFrn<R`O/zlA\@hFԎ4D$Hղ.d?d꼀KUn V1I?lh'h/8G6* ;SjO4]'>l=p.*EQ229krCƎeLjlYxm/HhX#bnVHs$U ( W &Pc#PBz8M|bB|([š|Ab"H|Aa|AN|cDxNvjA>*B3Pȡ4R`fI[H#46HpE#3f` P@@Jl _J As$vzD芁t[c/Lr%;B*RiA,Rig mрz/ 5X#3f2@ $$h=O =XD@XL#pKD@.*  _ V5/CeJ^1M&u!Zbz}[ǁ۷!Zx-p(N‡vAAq%fΘdW?T /4Ro$0:oD~E1~q'yҴ\F¸:#\ 8&$ոÛ nk8=JWaG.gɻŝA͛j@.{jX+4vs&#t$$[\dx0a³'\˷\9j$N mƂϖk, 塚£ApPŹ˻&xFg\jgl-/xfis;1@17#^$}FF('ޚ!VĊk¹\W[%jo'+aADAaaO &(\ܫ4 8 q[F;:]\G&ћsz7iHAn$pv9cӉ9%Z.~Od@ux;ġY=:LOC0;OG‰%=se=[B>E (osOXWU>PO=:#DcLcS3ðDc0?1t ;UIەVf%m9([p#PM35 rVFiS$h|ɧDт 5Jb}&m.Qq L޴)ә!RɕbV[Vd(무jkP"V|^1Cf7Hi,Z2,!%n[u3na kLT -lTPF)l~}nvnZE\|Bth (;KtF%Ҥr0Vb9_j7_}k(2TWVZ^*`YZ:3=4  Ѵ-8TMhu{#p` gH%AQlEYӦrZMrR1S s^u'α:Zx=v"_3`&N]_[PAtH< 0m (tD!lD !g+i񨅘'1dt]4tt"(qA39);܁^p;8 6iӛV0_@^Ъ4'\gR%*y0'ܽD#IP3I(Um^:@ÐSa$,ï콤w۫ EG׌j'WepLljf,fJ7YB.p:R:"/iMy|u-^b+[i[t[Vt /Z>7"d:|:dl{ߤH0O!,=&}JBl5)ց,sAVJG$J Faڣgdg`J)Wp!.Q[츰8 X^GE6.<:LMX03ZW?iIh-"o-(<% CF0z+(!@6=`o0A_s/QÍJfw>~&ܗC81qx`hE#x4q#ѐFQo Ǥ3MiEo$+( spD-cnB<:$+y  .fPrD.;-!(z^~_!&nζ.! d:zη~;߁\pp_(~[̰KMD˴ϕ5`!LQ[U`-pNH`Wip鹿6 %tS-=0%d"C?E8tLxoHѓo{#LcvRv*mCf$%8#/M9@ܽ[A-}2/sr\roQY Ŭ+Qq΄Q͘^{KGzu~;'Q r@s]uF"1&S*!u3('w;qtn)|"w{4TN LϾL/)ʍ:Pwyw"e{} l /17܁=G|÷m]d%Pe!nDLR,#Y7lYpap*wWyWoGH`!+Pw7X\K[$f2I/+1()z&l7({w%.sQJ6.^#yR{' %}SP%A!PR Fx2rs}S35hyyho`ޢϧ=[>ٖ=,OGIlyE{^JAZz,!Sy׃Bxw]CPV(`8dT ްؗ8+RvH0asogtWDoSE o pPlqP3. Ȁ7|h{qC UZ҄NhgG0 X*=ٖXh|(=SI"AwHXE8 h0p˘@ lq?T ᐒ a.?Dwob@ a$xZxᔭ @ ` 93b "Ub` [QP` B2ho$&דVmX6J$#`'1!@ (0 S0o{I>GEp]6fHmcw J6(*dI  @ l'ؗH7V|!n$QJrH3AKnt@0"b!o `(Gb b`H`  a Z nJ) w[ z@ W EW4t)X'Qر!jX+zM!PǏkS{EP9ZH] hCEO%'nڱQ䰖0FzgC)+*_(o0o`a`ް`dÀbdE ݐ ``@  E ` ^@@ Y$""YJ Xy=q^GCx2/+Iv јl^Prꅘi.!nlm'0KF!{oj}v(cQ:k֎Hj:#a cȀ @ @ Jp `^е^ { @ RQ{Ji @ 0~z#bQ(@_ 6J/QrՄ!&m)` e )W7 q&.ZgIW'𨶰)F'R6ZF^!=@ 3ٻ8dgk7˖͊o'h)"`Y (L s٫-R@ћp1fx!2֎\|N&0JfKm+ʳ6$A^'DW ar+f*`P2;;; c])alN$/z5eYa1@ 0 /< p )8i0,/ I5#+R 0+2TVb62TSKaʹK<T,ŀQo(Hnp0u|tLvzw<|u,}z,ȁ ȆȊȃǂ|0R\ʦ|ʨlʪʮʰSP?t g4c})Gˁ1L\9ii ͑i&9ͰV؜Լ `Μ͎f,L\\jiF\:Yj:hRϜ`\_gҘ!4\Ѣf h L|Ό3iL.oDS"7.,5.~oD[D^F~J|8"5JNN@GHt1`G ppf. Ԑj. nnhN n~la妀b3_ovp x~rN zq3Mom#w~0y ug5n ؐ ~ > 0>o {oo~=y.^fy0 곾 ^N F5ntA~̩Sb!Rw삾o^o0 r~.. _ o[~H/? y3ڔ+L9d^<pN]Na 7ˮG ??/>/ ͠ Fo͐&  q@44o 9_c{>kLE IDo _oM܁04[GC!p}#pGvd {oGA 4 G.^eܐt IK_"% (?; 0  _yX_>oov/ f K Oo" 0~1 GFoAn:tjnu(nv >8 BSN:[ֵ[7rX ]t9^=u(髗SN=}iPAyR:MvM"N2C: 4ffW:VETn|Cэ^R3}2 E-{~CQi{)1ʼn?sdT:A^I[ vZv<93=55lZ_7%|C—BR4, ^i0l-(h[Ŋ|*J`*h0V-H&q};s02 (jFN n83Ek#[ :! :hiZx0Zz_`p)SgB)ɦrJ&G]>\B:P4DzȂDv4%yj'qDRfLa0K#vRg^fTFfj1kg%~@o< J(FEIyVXUe6-a@a]:0Hn1+ Edsʚ:E]bnNKi :N%kᑧur1%p8eaL1_ ˡJҨg0៘pa0v[:P<83OlڊZ_r}+ZiXz ruR`SJ9_J8E_x)Abf`6O> Pe^W5eB^VfsY 1,Jo{UGjWq ofᑞz-lt,g<ś( 6vuXWZTGñKya'z\'%{@FBf=bPH1x-}iffb2'ѣ%ׄ`ǰB,JJîԓe B <51llÂQA nP! $p18ߘbɎ0 |dH CI0 Qi rRa HnM|]<p&(a 1 x8 nĠѮ 0)pF0LacиF Pp\5FHG m)Nqҡ ␗0)qx'3nxc7 q)Q)JTÓܤ&Ay@>82cxk4CF3iJӚѐ&7if`sTGN0 $9cЌ3grSش'4Yn6:BH&g`:N5Y5$p LTV%EQщZTV&PAE9N#;P2B[ b,\ HLz`Χ)xuF<<20NJ 44*EI;MgVQTs!V%>$^C#L ! 8AWշ5a]#9n][zG|}lW[׶~_!VvwMmfZȎ:ȑZ{ ȵ+텨cijyGw)6X:.]o|3&ȿnCw6 mr u;v"xG^򓷃h!I|G|gU=EBA)R)SԢaxӍulZ0~8~=v}>r=>qw;w|?x~zw{y}ygzWNw73w/N׻O/}wo['g~>~ɳ@Q9TB-T25TBȀEHő \x#gD1EJ=PPRa.UTHUU1UUH:BQXm8g7 ZzL;F) Pd-QUVe-f]Vh} KKU[V }xm-HBpug(LOlOO{|W|)}MT5$Ns%o-XՄ5ȏuoj%r","EXEY7XDِ"YJ%%e؝mJ٠}ȏ̉mD(ڨکZ/BXZ ȭۃ|L x-ڶ}ڷ. ʉl ۻEN[[GU\Y% U\m-Ee\ȍ\\[έ\\\\]\ӽ:%.r]@?a-b["ڕګ,UXa'؀؀ WYIZ^:(꽅[[^:^H0H_Z[5_^[@^m_x^@ONE_ZH U^ ^ 6 .-` Qq(ra`M"Ga ݸ]GXesU)H^`[b``a3c/__a(3b^20>cbb`9c3=&an/` Rd@=6c4.S3Cc3?5;3TA;Vc{em-" j+Xvs5v8p&:b""%&f1='2Xa{ ^_aTFUF/]_0c`vWd26`ca>b(d=cd /K^aU@`-6_3&hvF_b`xcv]Pe46>Whƅ0xFi^@]^{ n(àLu(XASi#)/n1q4^c]KV-pV- 6sVF닖gxeFk|fgk`8X^eCkb膃Fnh VaMa~>Ncyg:葮g/nvv]Z5b]6@rzʜ fZJR[`uHFOURO)ks|IߵjE(A_b_8^0i6?g^e~6WQ&&[؂n5 _Mߊ_}n^W.iF;v40-0在8 ( V@uP_W"KQb8ZЀpnetbjm /L(_ox>n(gFi7oXF|c3cC6d㽎-Lo=1c22nVgh\vg(eaZvi Z[ZpLEY"(Zbq ,>PTQ/؋(!qy&TP0F%Gi̶g9^klc,ggBeBc d0k`a$/V_6O0i/`@oNZ>Nv:hnYrѾveQ,B Ѐ (Hlj,r"!v :b/n"X)]u_~nganmeWyg2vo0sjo dH_UvƦv`2a VFvsxg5 ]ΪGtb.j5%RϤO%'%{OR%yqX*qPQGbiBڋcwsfeVc^r4ne7oB~c3fqc|6e[,@HPWn1&oovcf/zsiwpznt⒟eUVe?ow-q pP MؔtS") )"(Y(gڰz/)/+mbĚ8Skha[ٲ/[lDa R810bÂ#L`È$9a ւN Z)R2E-ZMb "0\|ZP@ "WU J`[rB3Duu)(Pܳ_ЩGܺuo߾ۇ1b}SG2̔'k|gF lcr(իgزgӮm6ܺw7agb ڰTF]aSJ +D'VV,bHs6icjpa9&ey&ow>}Аw :PU5]W@5'YR#FHICaDݐL|-^$M݂GS #ࠠJQuA( Tt`"VN1B3N9S=e=e^(&Bah0*:)C:¤@L=تN NK* !v/b 9P.0 Sh|0 +,fqӶiWS=gS{uS?} Ce^%I4BpqLւ( *QWQtIYRyznP25Y8u^|3όSa`ʠ*m2rǤ0(` ;>O" >^Ic8W K>9啗&1R*'W}"]SVQm\Dݤ PSD P;]JV-Qр&@y9XUGP[O2ɷTBꌸ[5^zc=es6B `> SK  0 ! 3 aH{BG9Fh5="g r=etARE@V  +24[b &0A p) 2"E ')$@pPA pg AWu(H'BOPUB=WZF|Qgcn|PPZ)(!BWd@(F H$&3iaYV*^yp"Ƞ2xA+eQO(B բa [ A%$B;|Hw'b:oUzX'XK#0`ցui9-b1G;AD 1 eޕ1?$D#*Qެ9H!U":Za .8)JY"JF6r]dEfGC`%%@ \>Lv$V4.,:~F!KY/B sZӪ$h+ 64`&׽sR:rsB5Ȕ hL)Z@ 3@)di]ʇ/I3A[݂jOyDbR<Tv*J{5WuQa1"mY`CE#bI[+ڃEGr\d-1>*.v8!pW/.5|}/|;0fSI,hjJ>繈0] eJ(LK8-pa&3Ov/2! dԅl?SBP?"%hq*A:!~':bE `muXSjEKY`LZ4$1c&'Or'b1ִLB'0ƷnN_5Xho3|F48) Tz`HF&0JY?hjް@Đ!3E L@UGiՠ +Ҋ@^l{f>#@Gz1p/ƒ_??e6h+,e~]L4xK 4 SS 5UZ}lM $>\-B/H&0&UDXJ @^؂GǃPU0G ѝqϥب<2T_7Cxq8՞]! =F_b)!C \itLk_5 rЃς|,.K}ġŀܝyQtB]'1yH!P @4 $~-Dj/T" J5Qe}5D(՘UګAΑW݄8bI8P":U3&# #I_Qc2Rt[]㛡ü ԙ1RRYR R") X0B'"!~&DABn~  G%ENp' L |@~b tx֬KZH;ρդM$NdMr:A8%5L8$ I=DLDWx 2N= Ȓh''eXNY@d |Y~@eEnB' /؍+d J Cm-.ꅯLXf~\f @mf@f^&fZ\dbf}gf$p#Q&_lάxDYQLPG}ļADN IF |^Ng!r(<lh`-D!4fnty@!@]V7-d%d|JԴcUDv@DV0 XQ0<fD^_xv*C;.W88h @PjXlj| LDM~T*|BrB(i}>Ru>(#r"?R'!>{#=J !S~&! - W)ꡆFK+&*BC!YJ\j~jmTj^\jl&Iq TR(RӽLA奔_B$j!q#L##,b`j)2 @@AJX͜鬚214fVd:kJ+_:*j ս+PƦ m@blڒ܃øF`d5ay & Y!H.جAp$LB#!rYjf.(,c'7e/@x(I tQGYtV3$΁`@9*R«!mlm@/,Zj|= af!ya-ߚj*n^H?N>~~R.֬7zxA>|N檼Hpyw8kyC88`0 Km~IZoX @ƿ+Tq@Ue[Nނr/άwB'PoBЯǪ ubv+RJVk=:/+b 9 ^@T j4o G۲&vo&Ɔ-َręhcE, )لp-pJ5 lwb1~{1^ cEE1@Bܐx*.e-Ȁ 9E /8:S-7t7C7 y: :1x t07̓0{a^)ro'onppj~,w  {f- MH%s0G=N"%0ʎ'<54dR3b(pf1|kVm0HICL*(Ѓ@= <1B!B·@} 0KЩ/h@BB!l@-Dt*P?&F0ۚ4~'ɿv2_!+uLXbNAhdNMhHL|mwʱ;~lu$3>/5n 4/ /Xyha0@-k:F6mtflooFۂ2eF|} +cL%igC."&~$O풽D\N@'bq2)'|&7lkÁKC9cqE8bHF</7@7z{ӂ@00@7p G Q‡G88 eddI*Fkإ҂AkxNԵk;, H Mlcݽ-b2SoovXrۣdqԄ!9e EҎKRfPvMFxB]:B2B`/8:B{20R-0>tI94@4e?8pZk2O5C;COaL6YzONtKLߋ|:"P/3SgrB]d=hѶ{~H65{Lz&= WDp#oGK ƃßOm$8ȃ]ENxo;_I:͘CL\DjLR +!3%oɀ.%d=gXH͆(!l#zX\|]Xs^D׾k{}=G߰dj4ۆ){o(I ?E%E 1-}@ DuhbbM#9P]KxA+=V= 5'RnU6`+I@rV-[l% XC p"D 6,#0_i eEuƕSr%=}+Q[N? ڮPG}+48zȡgWfպkW_n @e[Z@vܴp U@Zrr 6pWgؽzTAÆ|`n"tVX0b8,0ձ._Jab ǑPN#H3Cwo:mM߱ qѧW~sUvEjۯȖ-h1%L Ay Lzaj)p(;c*zQ}*`10."@88-B2.BRFÞ(R!C,+AJH 6Nj#$/ZhL҈+/`9ahON>n^z^XO=AuJHš6QuV;rͶ-H-\p{RZI\g™jBE:1(9ЧB)uJ_K @@+% wLI$,JzJb^:L@`LKa78Hhq(Tۨ;M7h{V\((L *De.Z .tH d pi<= x/!^ڔȾzԊ믉![<eArP rXlZYgtx򩛸o) S,s `Lrc+}+2 J0ZR. )b3vMY[ [#\64-7F4PPAyMHRPin 4ZEs9(.U!^b+ M8: I!mm*ܒ"r('GVĭ!Irbr.0jt¬|G{L%^ @W!i] 6 lL!XldnSkX)VGFBi PAq KLwpD q p\nj6@x|%[ˎ+FhI\Ʌ `,e,zOᖏ ʺL/4zB`껪,H(s .>&dFla4rn56 ,fl$`\\Ն`M7ѼUxm!vkb|'Fv8F$L&,YY|Aφp KҭVJ4hF F@! i6Fᔤ@p[C!i$("A08R,n.@,LrZl %Y0ke } sJ%o6C| .6Ω5l4Fd>Ƴ~ a׀OP ,^)'?.>J>^`Lf. ͫ8A!RBPDfJ`pT`ԡj1#o&ska10z8 "z|A G6q'N!G]h.ƂH/.@ 92A,bjb`S%u ;s;[%O(-+>kAUq jو4"s ?drn?,<|Ncq@zP-N>o15V!>c`CפHOZI5>B+4J%^"&(a'g#a@GH'xtG}T"JO#oā#*oatN!` od @-lGk9S*s/NZ;u`;w@; UQs%@ t~7e$&+G=t `j7HvnA]N,Nܤ>!z@凐|.,o ؀+r@b" "@!(76gV&\,x470rTB>GhfEXEAx'au^M3:c^_'t_A`x!pm_A_j !<g]ksI.0#8C4!T$SQuQ3QiPgPsVR ,N0v/os=WZ*ar[i\bh"uTc08` XAIBv(d*ZV-~w:B*¢5'NT(Uv^-a\݆TX2iQ6uL"Wr) %"x!r=.s t=wrKt+7tSt7wC XP^ dnHR8$SΊsaO}+xPm;ywy;5RAEx3V脃>c'L~E)*-Vd""l xHTkB? :8bЪ!v2wv c&E"vdp/9ضhy0qtɭ*87̡0LPECPњEa](4HMO"%*OC:*xoPzY''$ Z%XQ <0`{-A.@ScDfohs'/>'t4Vˣm!ܗN"2pZ~|VA7,D@긯W75x?CDkVQ[S-4Ty\?ɮ(JQt˸"LQdLAܭ:BXF Z\`6;5J :-$M sPy&.@$`\8Y2%m@8ָc@ TIsc @B8^J뜰'_`# #"nU[^ @O>K!/k Ӣ5?9 9Nhbs[&pNv$h"sJT H@ڿAAݭ'aYRE$h`⢫n{ P'"Z"9s%O|[j<ٓUr%y u%ɘȵW{=#q Z90p[Dz}GcjCq#|1TUC' +O׀ IW.1>vVU%kO~.>D*-wҨeNN,Y5n B`PN0*\p' !P0Qj!]$ \ezO6ǹ'\Fް9=d'.sRKxR%gؑؓŋ)y5Qw `QǘSaI:g@}8 n)԰i{[&"?,J4nAW/` lb80 NNknl>Uz/ S*'j)_"Ӹ߬Qdh߭u12A8©&`&u[rRřٿ)[9Ӿ; ZQB9Zlb :|PĉPp=S;P1`$IڲU-ZN(%[j#M$n9sĉ Pv40#K4*0[OKR=yV T֬Bī<,i:)մQDt䰢vR;wX`uI-HRʔIg W.fի%>t)#.ujZٞ٭gXO׮]ךyg-fz8zȡ}D;`AP%K2ȑ@,\?ޒK*h Z( L@H.x~GHVh,` :|Ha; %ԝwТxF4= 鵔U5 &dL) P>FLPBt`740E5eTN T&.'MX-)^1DkAL4]$ki锕V"\zTWc_71yR iKY*=VK)Bԃfu֜?j3ϩJꪨ*jNȊktcu (͇ygzYx^ziȡ-,0@KnRP/C?<@rB;{";x@0k, Q)xfNrY@DG!IUQ>uASNN":1nyTY%UvIf[ GClYZk]gTV3O(L3MKg\Ceƶдo sOɳ=Z jtvߍwzwx:(S0F\%<9Hwb0 P`&x.N8nCZȡC% 7`PPp{V}S9ke.8 @ U.P8o#d0Cu"$wPڣ 5F˃%JBycG*)y!KS1E$ceOx tI,4AN`/],*g^S̙A=xS h 1U%-$stP!Վ\OdSLH 8PLemmK`كPGނhĀ }#Aayt*J`h pEk\B! L;#4^>A_3Q~@d *N"L ć bݠX rbS>Gh-[&ݏ\b )i$ 5:a [@gj SZXnrI%dY fHtVJ@u\FS==%7P0:jaQ58Rb1XQC,Ml" dbЄ#&0.( P ӥJi2N 2KXP$w;A=HSbIX Sj'QSt,2 Ԃ^Vŕ͘,ê(l54, 0l!Uj^pz;5}Q]ҟ"{"Rg,<ׁ)Mmʞb&*(-"M7BFuq =ֆ<sqqN&V|oH~ղIiNt"q%K~),KN~--ij6lq6/ͬ$HR˲>OJcGn[3 ?ٸF?#W>|l4oM>xHF5sm%.0w2x6.EP7ArȴLoP-au3H&]"vQISIrvPk%$vn z3".4n`1,TA_s y@>@=@"@$Mc 3nFXna*c2,#X1*tCoZ=T8wf` 7 iȆkȆoqX |3l6,Rڑgg [qss5<"ph.h  j9E G}0GFGi S ;" 8d0o0D9` R#z : P<(K"2(-DTFnuMc vxLC+-bAJLK13uz,nKA>Cz24C"Ov)rO$8efqoXpp)q  qؐ b7H$,n=u{ R $I hR҈(< -u?>`9! F 0Ҁ0Tc#mvVEӋ k,k 8Ќ,Xs4à'a%(YpbD3 $0MYDxLMnAKS&r[@1Z6Y ͐(S?kް24,X 4=3%*$u ua"ZEqzPa!h?r>t @6d#C* %]HOoCe7 |!`x Y @Ku@"p{' nڦ+jp7՗r}xj,~ 2'-E9%-~ਉ:9iu,PqrE 5\s9%"HA0?Uh >+@\`%&( $$:~[lN2{򋄂duq֊$@y|xPB@}x5Vl%r٘DXW@K'z 2) B`# Zdt * 6:G+f! WWjkp+m |ɋy!m0~ c-RsBhبڝ'"p  G?Ng115A9OS%W`"ܓIKdɀ ` ` ƀ  p ` @ @` `  ƀ PQQʀ@A@L O| \ a? _ a a p|MlpD@Hx>Ya` p p,m^=Ll@ēGO\ŕM Ŗg \))P _`ap 6]+N#{7bnz PWJ0۱[p" Ѡ pͿmy7 ="[1 Fg:-P"BPB!փ8P3D@p "o|DiBP 5$\0O1 6^^6X0 0 03Jq%緇$@:A$C;aN/zV8ng%8yZ lLz,4l๶PhMφ$h䴺b$U+06[Q`&0` oLO7{7hI!pfP Tܠ]>x'@@' [X j ݼ7K8w4g7w-{"@GR! kD k@] B qU0nQOOL{~"QAk 5 :9ݠwx?;1ŶʚӸTn'K5ʡ] {CWt ,ZxnaTB;cKS3;NGP4faF*2pP7|'|7'ˋ| i7`,M0ʒҢE0G&ýE@ P_< !gRש/p4qKO MܼqG "jԸ#nܨ8q#O>nj:0?ܒtȥt<&U)xHJo|GvyԤX<1c XGDR[ctE40id' Xf]~ cIBWs\ZW 0@1e C+ 7h, lp{P'F1 T@D%QDd,Q;D%G RHT#Hz`m =-䑎؄KHSnH=BB:(ZL#)AT ZO1 r/0C|1W`a!<5#eR2%DoꐏzH$8)x _aJ,<Tr* rtO4%#axT!d (HĶ2jeL LS:"':-5(bBZl138rcdҡu UT%av\G<$ᡖlj=q8"6aFŸ)[u4+UV^=xo" !AhHy-R[!w@M,vձre'_f3/ShKKMW#Z `mJHae%"=fen.$7Ɉ#xXۃP 1}<Bl5qE.rYBN#0(URE*)Rp`# =E5i=aTاU n5TGO% UBBFjzuSyqQrxj=qv(@ H4R$ T N":^% "1ptL7JY3\#kޑ1D P;N ',-1}t(OAS3t"CfdO8:ѢS-z,g9 =ri`-8J oc7齑pF} qƁq`^/0Q81tF,ab.|=DD]tgD p`CxL~D ep@jٺZ1_ p9xfRgRX&#+^ ?r:j ZQ 1':!A;Oc!:0* Ј /Br4H! Z@)_391([{=C ))zy5LC4 hH@@ ?MDPi ȤR x+%%Ȁ x^%X3+'`d36s`&к3@^9 S {r4s:r/1;KY"4Aa*C0!.1ŸGx41 (†'  /,y(ϓ6GI/ #0 / IIhI\ɗI{H#~'48=Š CIK a9 .r/țrߜIQޔI1\y$&Qd x"X+%!R @`'(MJ RH=ېL'=/9L4);!ԡ@jS:@`HV;! ¡]TOk!`] 1,Od{&⓴U,HHP{ќHaM]֚=DiWBV RsrVVleɂ,~!2(ZTQ8kZb>Ɲ‹ Zpt4uyLyۜn\(Zuy[{(@: *QuKD6Ǯ0(Z ]{Jy0X^n(/vb/req1mlILDFE x4s;V~Xs?_,6t? @V F4{wW@paH30KtXر⺡0دʻ~ו-b( 00>n)r hA6`x0dod0`aHCdppu^at{rr8\7#Ҝ$J}]RR 2sahx%6PHzى_{sDsKwf$ I> q20HUPRtUޗYp퐠[( p hQ+"\GK*l BGX")'P.XdH؄Yнѫ6 |PBGO A;x‚ "9;`#ǎ?vr$ɒ<˖,I$E*C 8hVXbNL[d0+~zz+a:˯N?_: I X&EG0m̱K'DRJOqcOA1+D`uvTVeeST"uԐb9L=*mؕ,{W,̄80yK-PlB l>nC $tB-[njV0ch7kr\bAGcYu)]x^n孃^zѣ{WB :[sKEb! 5mH"xƉ$)5 aГmmF3fpmz;.`\}B՞؂Bd٬HDBr3@lw) Z,T  5@׳(mjqGF1lzmlL7"ozDo" Ѭ5MgK^M dRPI&3Ss &.%:^zL[iL4:= ]pӜFGTqS_#FQS7#@Uj[:aD+ȄӗM^#*H_!\2Vg.}us:;G:a7<$|nb#FY1ySC[?6Yf :44dT uK#]"A7c*.sHT}=ͷZlpB(,ZaaP v' *Ơ2*GtV n8PBPIa|7l:Ed%1L-&>cdhqYi_^sDž3@'txB]>3yS`HyTA&xXI`e*1s]}PTr@56}"K:C#j;l`܈4vu7CZZ5}mœ DNaOAQ) !TN@!kAXgA=,Ȃ*D$EңhqdH1 Owst|JgJips4FW-pH7E0Rl,E8/F !aI-{*E y@eCSM5x4@44b$:.Q$z7pC@aZJ^Slh! *B8A!8)yn4CZ8/J8-)A*tb9$XH-F uq 1$0(Y n↼}Ԟ1C-a!v!Y8Wg›,Za{L߮b6..4"!5U3hZ#^6p5Db4Da#vN@A:h_P(] #1IWQX*8p1^$hmGpTd-I_,TT8Fd` + 1}L,nOpx$u ½ 8ab@a0FMAKŕ4n4BP Lfb,Fq1\@C=ZלL DZe!7 @ 4ԁ.A"$6$uN50beOO$'pwJF=TS>E%I EHHAPW^Id!@Hha)%T7 dTЂN-tB-8,`L0oV-# - dbB lc7GbtFa"im 14h?VF p\FiCk@]n/nhBmAha &$@%Dᮤ \Uq_!2g  Je6IjYzb1*Pg@sB@}bS^SFrXVbŃN,P0֎ ӄNh4NmAB/0os,²CF=>ǚHB}I1gpJZI.p7x.Tydp-'6$yFU C[ؕDB% ˎF}J5jjVYBF88,`"CLق @h ǀՄqtnFC3#8X ^a" CI!r T,I#B+.ꦂvM ҆hą ƺ|}GG^Cυ@Ёzag.04I^$Y'"2Oj.tvZ4چU Y׎ՆD:VUj焭@-j/*Fơmڎ-O'78C 8 `*]l.:P8CQ\OmcކBp%fC0Hc!h.U.JhB궮a6F(ZΊƪtF2.Jl@@@@!R"z.l"MxNbvq#b|.pC{OR>rHXlEu<Ջ6N簝3FQڪ²4/ F74iɅYџEN;|+OϤtlOgly0 ȇ>Bj%_+.+n+@BJףRˬclɊ<_M=~߭ @@2\6'6\mp5l.4o"ng;AqvFՆMXjF*T $(ށݪ6cjhmtJm](#+s`-?JGT(wC dmCհ +J HchbK4C3*+T٤ =,U+u$Dtn!n.A7x5$jym.17L..%`R @Dcw{=4DЃ @ ;G6G ٪]H#dWK+lC0ta,FS.2(fQuJ*aP1Qw0p!ϡC0؉@>[jIU) |cOXXI `5ͥ,!Bu~3`Y.P&5,vo#zoAʂx&|EԧK ѴX YD˯ f_TǶJ-w6J< `DY~as!B7clYPm"ͫa,ueXYS^!莀 *A+y*v 0t3e3Hf6d ANU]jbsq]44D h⃇a/T"?DNeXTU"gG>jFOI0a05-Oi^2y#ӆb ƒm9\0nCpl̞5Y5w*)PC7a8o$}\60gJFtHvF*N&j+$J@IA{*B#%|68jiXSd`wg꿐cl&ݛ[e're U;$Fi->F s ͊RpKvsnr o|j(d7Y! )TuX{5/q,Su$5,g7]W9C1Ls*`,8zڥڪXīӋ{v9u|N YhA-0FvVC@e)kPm9[@=n#fp$5oGObn0\<ɳ?$c/`l얊nbbDV(V [vkWeJÇD |RԛGn]ΜҮmyfOi9vilfeöҤ^kn5cڵl:׉W ~po^{0xa @p0z%-Lxrf͛9wtaLGπ-)X/\hj5nٲ˶[hzK!3#81dĂhܶUH{ņjkNذasNlqa�/ ?17??j^h`Zr]Í[0TZaC&BeDP9#(ʩrQ zZzfBzQ-Βj9kH)ju&J%(2'|B 61 2 lN$;ܓ>lzJ4($:LX.$7Z[Bn1n9P%%RWXA >3/o, _+f`kSdtzŔVQZ`t lۭCQćHTXSqhi,}\I&txuЙ,:KkF'ᴺru(rtǸ׮?E/J .l]~yL!, RH#vMh7b6Bli1bBUtkkϹ>Fi`O [¥`koaV[A%6Zpm3ϣŶZrVBw/R7-\,iޔ7j+'WM7MG_gt)&fmwCwuyv])ԑ봐a~襟޳J!f59Do$gS$ RF1ݢgpI55; 0B`+9:bc36>%$<U4 dPA$$Դml-NrDAB#U{(_$~ 0@U44n%,_X o3ɁBlK+\8Eĕ#JJSOZd 0f$#Ú/@T@b2b蘩iGefď4mqQT 1b5(o٤ 6 Mo6AjR9mRK 4`:A 4?AhVЎ?.iu@ ^Ž|D!{d>VhV 8H|w#:BHwliv6+pu'T#CE*2j}LMTt;F wh3HaC[ Ǟagꖷ6J +Z׻ i|EFGmSZ/p+ =Ivf= BeIY V&*[7Vy=tyӜS_OB=C.Pɖ.-WD>@V]##Ԇ7l \ SǍnwe,/dgA_- KYjQwj ZJEҪVDƭF<:O,F8<όFTcIƮ(Wj [cԲ wSC54tQ@".~٘BBΟdS4#n6Ihl ߽kx&X*( U#N TP^ fl[W>>hd 9 F5P $k4 G=awxȃ!NA8r`N%5-c|2\мu:p@Rih W}NQs#%Q.p@"J@!혥S<͠GS2ÔFe0gg Q|zhM!dP##̜J(Kr̠pJi88&ңVI&)4LiHsp"TQ:j{fRRz&6RRpihAOSJEo^J:;G肆A; RBD  f2/a!co>d Q):tp `J̌(T m܆ϒ4&5S MJ$7ïT TTn(m:L袞$3:6MB\,wGFB @ltAJ7q ӳ/ 塩KȖ!RaCTlJNN0UhYt19SR=L/V=ȃC62#eJ@9R.QLvFjt`U2g`BGhHHB.pAD&FJ J}U/8P3e LÉ8ѧRK68ˬT<ڊHR[STaVE FyJ GGlt4,AA_K_sMv_o`㞺UܬU.8hh9(Ji$ޮwM!plCuwߚc< j6RAk?Q*3nw[@Q}6QUgJU0%ao_a(xB>W,pqqIl.Zs؋"U#6N)6`:Qމ6OF k9KB_]k7M~) gQZb#8@jڤlN|YTUa[Hua~O D`` /S=ɀ%Qx3+A%(dgWLv8zƦAJIBk&eO SăV>i /4jКQ ㏭M6eg77n#sNS7gcPELB,Wyc.@ DȢj/s"#<'Qe1 FcpLTFYcM)G,i 59o 9 8ֵ;,^%nh#jB@# RM[c5#.sjNUUGidFX%@aVnbLVs)ՙG/54%8L3RIN9 WS?]OYBSb29lix18oiІ2FV6ċ=F}SE}j9ZOez4W&0M`5tKK{uasmҜs۪*{:Uj[NӍ̬ 7˂TØ>I[XJS,TYk Ր3ALd N C Iэ[AgeR̒9j=Y!L9 keL= U]@~>Pi[KEoB:1yYc$Zj[5hw i%81Up}@4[&X2 f7mto6l֣^Ed>`B;Kht5V b 4h8P 7n\ֻW&KQ:9OI49""aT2d͘'W\I弝)GG^=r(wӧPJJիXjʵׯ`WؾzM X[n߮ 0W.x݋W߿t/aNװǐ#K~;q\kPWaĈ(,QB#ةǎuy[\Mkֲc=կqӞ=/%VR6QӇ6سkΝXSĖN0<]ƗtGr5 'N\873݈3N7׍~_߄%ؐw8"~ Vh=$eS饨,n7V)4hf yY9P7Zi%v=SOD&TgdɝffݣPg|'E{~Y| )hG({(i饘YfДDCQ>꫰jFjb檫y+US>&::_>Zk:- =HsxÆ+VSE\޸NgsO噭 *']ණhc8g%0Xۉ0sN8, ,-&Z$*10Z.xsW r3vŎ:jw¶}Jn-dv..: ָGU!U#(=zSYj:?hrTGS u< /_/Q*GxӔ3]_ H Po5ß(- |'HA3-jZbp>8LeԮWSCx !p2! sH1"(D%&щBHv4"D$*L evSDy&C帡hC2qS,uG"Dh ޱzuH0 {#9G@*>6)Mm}O^E=VKF?)uBE+Lϟ E)~L_ad03L!Qg[l,(Xl]1[G/kFZ}RfGL;Xr3`9oKɖJ+)ˀPf,)|m$&3:- +i~)3/e9ZT`א#Q'،UF)Iѓ~8 i8$J)=Q&I7MRHa)%"ͣ*E9/Ԧ:ZJ:MqJӎ 5TS ԙZ<$lz\V)ͩZx\iLx+\=Cx+`W4f0VXldь:vp,c#K #4 YjLJEPֶ@uEm50; V6,cuuԪk~<Ֆ[}UJB`5zvk F5MzYjt7܅5q nw{ jHɽod on*{{VWH֘w{ 7.5j}ʡ(x.u;ڣ E,9z\î|16aWخٽmcT;: @.G'RVelo,6<+h!NnF3FZ-fX%7Y*[T4dRָ1ҹQˡIX\AKn:!BnFLi7CPi]+}WNs:C*W,fQCvuxkeh_ -[C̥cGjUz=r s= CHnWٷ5Uκk:Ӌ4dlU~-[]$J4VlduȐ4A=dJN57t9b') ]ܑKsyx@1S$fm|8nFuKcuь6يRAS"G8QSbڧ/b;xČVr[ڑ{rf~^5]up{1ьwŸwwlݱy;^wq)G 0t!3`XvQ3? Nf?֓Paڿ-\H΀q3 p`~(C// Gђ_(4/ Q{ԂEB}|+UYULh%wynڵvwxn%oذWqWrW ذnvx@oFx8Q[Vd3 !pyts!`uyV!Pu"@"(@2[X}uir_VeS gamVcTPd $hu* "Pd. t!@fcaB}PPAc 4 12c%0e&je2{wwvy^egsXwYo]5xuG n d% 4x PtPqf " Pzu Sp_ kHrjr vVh\E[%$x ܰxyW}' 0X[ Hw@ Pm @ p됏m(gaܶ:}ugh3JђKVEa%_]x^^& 5a5R՗ ]_5aY_ CR} F_Er@3Hv`L+Y_-9FHy렄#"Q]'={Hs$&-Q 2FY+_& fDY` #UCIՒdiƖD _ %ְ & z zٗ|ٗ{ c}) d&R4ɓ6_{闄{}I Ffƚ;)ق]Ydcr%XyW)Ww*Q埁UXrt՟'EtWt'fJtx u($%f T0GYK9Ҡ*f ,q4!V%Ր >( Z#uʶRSCL/R:FU%Z$YSDMr$` Ur(q%]ZkIe`kiDZX2Kd*ajSF$kSkDm*?ՄWvD(:( 甩rI$BSzU+uUYURB%$HSEU$T%U$PSz%zcZPҫ)J$LHcZ@ʬꪪEêQ\ªZ:TŠS$l:U*jw( ) ۰   P* ˰$("[*P %@*@$4;$81{>2+84C;8[H=3۳@KA+S H;T˳POK+:*V[C{A˵QM{G˴Hh۳m{RS*;+5+CKOfK}KJFcMa{J+K4k۹Kn[ZR ˳.#`۹QaI˴7 ;«耼«›ċ;қϋK΋{ۼp˼[[諼㋽ԻNjܿkP{;̿ۿ,`E雽KË {ꐾ Ll2$ l: |k=\+8L׻ s apZZ\_^d|c\hl[i[|nqo\vL`y|e |,sl<Ȇ|ȈȊe\},Z, Maɚɘɛɛ|ɡɠ<ʢLʟlʣ\ʞʫʯʮ,L˵ʬ\˺˼˷<˾̦˴˸<̧\̶ʝ|e̘l\Ͳ ʥLͳ܌L\˾ ;itksnap/ProgramData/HTMLHelp/CMakeLists.txt0000644000076500000240000000012310727111331020052 0ustar paulystaffSUBDIRS(Artwork) INSTALL_FILES(${SNAP_DATA_INSTALL_DIR}/HTMLHelp "\\.(html|css)$") itksnap/ProgramData/HTMLHelp/ContextHelp_Freehand.html0000644000076500000240000000253510734222720022245 0ustar paulystaff

Polygon and Freehand Contour Drawing Tool

Use this tool to trace 2D contours on image slices.

Drawing polygonal contours

You can draw a polygon by making a sequence of left-clicks with the mouse over the image slice. When you want to close the polygon, right-click.

Drawing freehand contours

To draw a freehand segment, hold the left mouse button down and drag the mouse. To finish drawing the segment, release the left mouse button. You can combine freehand segments with polygon segments. To close the contour, right-click.

Controlling freehand behavior

In the 'Tool Options' panel, you can control how freehand drawing behaves. Select between continuous drawing, which gives you greatest control, but makes subsequent editing most difficult, and line segment mode, where the freehand contour is build up from straight line segments of specified length.

Editing contours

Once the contour has been closed, you can edit it by moving the vertices of the polygon. Drag a selection box over the vertices you want to edit, and then drag the selected vertices to move them.

Adding contours to the segmentation layer

A contour is not added to the segmentation layer until you press the accept button. When you accept the contour, its interior is filled with the currently selected drawing label. itksnap/ProgramData/HTMLHelp/ContextHelp_SegmentationOptions.html0000644000076500000240000000144110734222720024535 0ustar paulystaff

Segmentation Options

This panel controls how different segmentation tools behave.

Active Drawing Label

This is the label with which you draw when you use the polygon tool, the paintbrush tool and various other segmentation tools in SNAP. You can select from a list of labels, or you can create your own using the label editor (see below).

Draw Over

This dropdown gives you more control over drawing. It specifies which voxels may or may not be overridden by the active drawing labels during drawing operations. When it's set to All Labels, your segmentation operations will label all voxels with the active drawing label. When set to Clear Label, segmentation operations will override only those voxels that have not already been assigned another label. itksnap/ProgramData/HTMLHelp/Credits.html0000644000076500000240000000412111115142373017601 0ustar paulystaff ITK-SNAP Credits

ITK - SnAP Credits

ITK-SNAP Team (2004 - Present)

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 through grant 1 R03 EB008200-01

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

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

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/ProgramData/HTMLHelp/CVS/0000755000076500000240000000000011560342170015754 5ustar paulystaffitksnap/ProgramData/HTMLHelp/CVS/Entries0000644000076500000240000000164111560342170017312 0ustar paulystaff/CMakeLists.txt/1.2/Mon Dec 10 01:14:01 2007// /ContextHelp_Freehand.html/1.1/Tue Dec 25 15:46:24 2007// /ContextHelp_SegmentationOptions.html/1.1/Tue Dec 25 15:46:24 2007// /Credits.html/1.3/Tue Dec 2 05:17:47 2008// /Shortcuts.html/1.1/Sat Jan 31 06:45:20 2009// /Tutorial.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionEdgeBasedSegmentation.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionIntroduction.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionIntroductionToAutomatic.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionLoadingImages.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionManualSegmentation.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionRegionSegmentation.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionTipsAndTricks.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionViewingImages.html/1.1/Sat Dec 2 04:22:20 2006// /TutorialSectionVolumes.html/1.1/Sat Dec 2 04:22:20 2006// D itksnap/ProgramData/HTMLHelp/CVS/Entries.Log0000644000076500000240000000002011560342170020020 0ustar paulystaffA D/Artwork//// itksnap/ProgramData/HTMLHelp/CVS/Repository0000644000076500000240000000003511560342170020054 0ustar paulystaffitksnap/ProgramData/HTMLHelp itksnap/ProgramData/HTMLHelp/CVS/Root0000644000076500000240000000010011560342170016611 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/ProgramData/HTMLHelp/Shortcuts.html0000644000076500000240000000717211140771600020212 0ustar paulystaff ITK-SNAP Shortcuts

ITK-SNAP Shortcuts

Image Operations
Alt-IAdjust image contrast automatically
Ctrl-IShow image contrast window
SHide/reveal segmentation
AIncrease segmentation transparency
DDecrease segmentation transparency
Tool Selection Commands
1Crosshairs tool
2Zoom/pan tool
3Polygon tool
4Automatic segmentation (SnAP region) tool
5Paintbrush tool
Slice Window Commands
(cursor must be in a slice window)
PgUpShow next slice in current slice window (mouse must be inside)
PgDnShow previous slice in current slice window
Arrow keysMove cursor left, right, up, down by one voxel
Shift+Arrow keysMove cursor by five voxels
3D Window Commands
(cursor must be in the 3D window)
SStore current viewpoint
RRecall stored viewpoint
File Operations
Ctrl-GOpen gray image
Ctrl-1 ... Ctrl-5Open gray image from recently loaded list (1 = most recent)
Ctrl-DOpen segmentation image
Ctrl-SSave segmentation image
Ctrl-NCreate new segmentation image (clear segmentation)
Ctrl-QExit program
Paint Brush Mode
-(_)Decrease paint brush size
+(=)Increase paint brush size
Changing Active/DrawOver Label
,(<)Select the previous active label in the list
.(>)Select the next active label in the list
Ctrl-,(<)Select the previous drawover label in the list
Ctrl-.(>)Select the next drawover label in the list
itksnap/ProgramData/HTMLHelp/Tutorial.html0000644000076500000240000000261210534177574020031 0ustar paulystaff ITK-SnAP Tutorial

Tutorial: Getting Started with ITK-SnAP

Welcome to SnAP! This guide will introduce you to the most commonly used features of the software. The tutorial is organized into eight sections, and requires about 60 minutes to complete.

Click the first link in te list below to begin the tutorial. You may also click one of the other links to jump to a specific section.

itksnap/ProgramData/HTMLHelp/TutorialSectionEdgeBasedSegmentation.html0000644000076500000240000003006410534177574025462 0ustar paulystaff SNAP Tutorial. Section 7.

Section 7. Segmentation Using Edge Based Snakes

This section assumes that the reader has competed the region competition based segmentation tasks in the previous section. The instructions offered in this section are less detailed than previous sections, since the reader would have gained sufficient experience using the tool in the preceding sections.

Step 1. Prepare SNAP for Edge-Based Ventricle Segmentation

The first task in this section is to use edge-based snakes to segment the ventricles in our image. In this step we will perform the preliminary tasks: clearing the previous segmentation, selecting the appropriate label and entering SNAP mode.

Discard the previous segmentation. Refer to Step 1 in Section 6 for instructions.


Set 'Ventricles' as the active drawing label and set the Draw Over mode to 'All labels'. Refer to Step 2 in Section 6 for instructions.


Select the snake tool in the IRIS toolbox

Position the 3D selection box to include the ventricles

Press the Segment 3D to enter the automatic segmentation mode.

The above tasks are described in Step 3 in Section 6


Step 2. Computing the Edge-Based Feature Image

At this point, you should be in the automatic segmentation mode of SNAP, on step 1 of the segmentation pipeline

In this step we will compute the feature image that assigns near-zero values to the pixels close to intensity edges in the grey image and assigns values close to one to regions of uniform intensity

First let's tell SnAP which type of the feature image we will be using:

Select the option Image Edges in the section A of the Segmentation Pipeline Wizard.


Press the button labeled Preprocess Image.... The Image Edge Filter window will appear:

This window is used to specify how intensity edges are used in constructing the feature image (this construction was outlined in Section 5, Step 2). There are three parameters that you can set in this window: scale of Gaussian blurring, edge contrast and edge mapping exponent.

The scale of Gaussian blurring describes how much the greyscale image is blurred before the edges in the image are calculated. At small values of the blurring scale all of the edges in the image will be used in constructing the feature image, which can lead to a prevalence of noisy edges, i.e., intensity discontinuities that do not correspond to boundaries between anatomical structures. For the larger blurring scales, only the most prevalent intensity edges become visible in the feature image, but fine-scale qualities of edges become lost. The selection of the blurring scale is a careful process where a tradeoff is made between too much noise and too little detail. Luckily, the edges between the ventricles and the surrounding tissues are very conspicuous and 'survive' a significant amount of blurring.

The other two parameters, the edge contrast and edge mapping exponent, determine the shape of the curve shown in the above window. This curve is used to map edge stregth to feature image values. The steeper the curve, the greater the contrast between flat regions and edges. Remember that the speed of snake propagation at a point is proportional to the value of the feature image at that point.

On a fast computer it is advisable to check the Preview result checkbox in order to receive live feedback as you change the parameters in the Image Edge Filter window.


Set the 'Scale of Gaussian Blurring' to 0.6

Set the 'Edge Contrast' value to 0.030

Keep the 'Edge Mapping Exponent' value at 2.0

Press Okay to compute the feature image

After this step, the SNAP window should look like this:

Step 3. Snake Initialization

In this step we will initialize the snake using bubbles which will grow to assume the shape of the vertricles. When initializing edge based snakes, it is improtant to keep the bubbles entirely within the structure that we are interested in segmenting. Bubble placement for region-competition based feature images was described in Section 6, Step 4..

Press the Next buttor to proceed to the next stage of automatic segmentation: 'Step 2. Snake Initialization'


Set the bubble radius to 3

Place four bubbles inside of the lateral ventricles in the axial view, as illustrated below:


Step 4. Setting Snake Parameters and Segmentation

In this step we will experiment with the parameters used for edge-based segmentation.

Press the Next buttor to proceed to the next stage of automatic segmentation: 'Step 3. Segmentation'

Press the Set Parameters... button to bring up the parameter selection window.


We will begin by experimenting with snake evolution with the advection force turned off. You will see how without this force the snake is likely to 'leak' pas the boundaries of the ventricles

Set the 'Balloon Force' to 1.0

Set the 'Curvature Force' to 0.2

Set the 'Advection Force' to 0.0

Accept the new parameter settings.


Run the snake evolution using the button with the 'Play' (triangle) symbol

As the snake leaks past the boundaries of the ventricles, press the button with the 'Stop' (square) symbol


The leaking occurs because the balloon force acts outwards with a strength proportional to the value of the feature image. Since the feature image is positive, the balloon force never stops acting, so the snake never stops growing. Luckily, the advection force can be used to get the snake to stop. The advection force acts inwards when the snake crosses an edge and keeps the snake from expanding further

Rewind the segmentation using the button with the Rewind (pair of triangles facing left) symbol.

Bring up the parameter selection window

Set the advection force to 5.0 and press Accept

Run the snake evolution again, stopping it after a 1000 iterations or so.

This time the snake does not leak!

Step 5. Further Experiments

Experiment with different values of the ballon, curvature and advection forces. What are the values ideal for segmenting the ventricles?


Try recomputing the feature image using different parameters, such as a smaller scale of Gaussian blurring. You should be able to get a better segmentation than before.


By now you have mastered the most important features of SNAP!

Experiment with segmenting other structures in the brain. For example, it's possible to segment the caudates using edge-based snakes. Even the hippocampus can be partially segmented, but extensive manual post-processing will be necessary.


itksnap/ProgramData/HTMLHelp/TutorialSectionIntroduction.html0000644000076500000240000001665110534177574023770 0ustar paulystaff SNAP Tutorial. Section 1.

Section 1. Introduction.

This section explains the purpose of the SNAP tool, and describes the screen layout. This section is organized as a series of questions and answers about the general capabilities and structure of the SNAP tool. For brevity, only the most basic questions are answered here; more questions will be answered in later sections of the tutorial.

How is This Tutorial Organized?

This tutorial is a walk-through of a SNAP segmentation session. The information presented in this tutorial falls into the following categories:

  • General information.
    This is information about SNAP that is necessary in order to use the tool. Most of the text in the tutorial falls into this category.
  • Action items (denoted by icon ).
    These are actions that the user should perform in the course of the tutorial, such as clicking on buttons and entering text.
  • Tips and tricks (denoted by icon ).
    This is extra information that can increase your productivity when using the tool but is not essential.
  • Information for experts (denoted by icon ).
    This is additional, often mathematical information for advanced users.

How Do I Get and Run SNAP Under Microsoft Windows™?

On the Microsoft Windows™ platform, SNAP is distributed as a standalone program. It can be easily installed like any other program, and launched from the Microsoft Windows™ Start menu.

The SNAP self-installer is available for download at http://www.itksnap.org/download/snap

How Do I Get and Run SNAP as Part of ITK?

On all supported platforms, SNAP is distributed as a part if the Insight Toolkit InsightApplications directory. Obtaining, building and running SNAP as part of this distribution requires considerable knowledge of system administration and computer programming.

The directions on obtaining and installing SNAP are available at http://www.itksnap.org/download/snap

If SNAP has been installed on your system, consult your system administrator for instructions on starting it.

What Exactly Does SNAP Do?

SNAP provides a set of tools to make segmentation of volumetric data easier and faster. SNAP can be used in two different modes: manual segmetnation and semi-automatic segmentation. The manual mode is used for segmentation using hand contouring and for cleaning up the results of automatic segmentation. In the semi-automatic mode, a powerful level set segmentation algorithm is used to segment anatomical structures in three dimensions. This algorithm requires some guidance from the user, and SNAP provides an easy interface to provide such guidance.

What Kinds of Users is SNAP Designed for?

First and foremost, SNAP was designed for clinical users. A user who already uses a computer for image segmentation, and thus understands the fundamentals of three-dimensional medical imaging will be able to use SNAP after completing this tutorial. SNAP does not require a deep understanding of the underlying mathematics and computer science to use.

SNAP user interface also provides some controls designed specifically for expert users, who are familiar with the level set algorithms. In this tutorial, some sections are written for the expert users, and are optional.

What Kinds of Images Can SNAP Process?

SNAP can be used to segment a variety of three-dimensional images. The images have to be homogeneous, i.e., having a single intensity value per pixel. In other words, SNAP can be used with MRI, CT and PET images, but not with color cryosection or diffusion tensor images. SNAP reads a variety of image formats, including RAW, Analyze, GIPL and MetaImage.

How Does SNAP Represent Segmentation Results?

SNAP represents segmentation by assigning labels to pixels (voxels) in the input image. For instance, when segmenting a brain MRI, some of the pixels in the image may be assigned the label 'grey matter', others will be assigned the label 'lateral vetricle', etc. It is up to the user to come up with the list of labels to use in a particular segmentation task. Each voxel in the input image can only be assigned a single label. The output of SNAP is a volumetric image of labels.

In this tutorial, we will use the term grey image to refer to the three-dimensional input image, e.g., an MRI or a CT image. We will use the term label image to refer to the corresponding three-dimensional volume of labels.

What Are the Main Limitations of SNAP?

Since SNAP can only assign a single label to each pixel in the grey image, it can not be used for segmentation with sub-voxel accuracy.

What Are the Different Parts of the SNAP User Interface?

The picture below shows the SNAP user interface in manual segmentation mode.

Most of the SNAP window is occupied by four panels, three of which show orthogonal slices through an image, and the fourth, located at bottom left, shows the three-dimensional view of the segmentation. This tutorial will refer to these panels as slice panels and the 3D panel. Under each of these panels are located several blue buttons, which are used to interact with each panel individually.

The left portion of the SNAP window is occupied by a tall thin area called the control panel. At the top of the control panel is located a menu bar, which is used for saving and loading images, for setting options, and for accessing the help system. The rest of the control panel houses a variety of buttons, sliders and other controls, which appear and disappear depending on the current mode of operation. We will see how to use the control panel in the subsequent sections.

In addition to the main window shown above, several other windows will pop-up during the SNAP session. These windows are used to coordinate specific tasks, such as loading and saving images, or selecting parameters in the semi-automatic segmentation mode.

What Input Devices does SNAP Require?

SNAP requires the use of a mouse, trackball, or an equivalent input device. A three-button mouse with a scroll wheel is preferred, but is not necessary. If your input device does not have a right or a middle mouse button, use the following combinations keyboard mouse combinations:

  • Right Click: hold the Alt key and click the left mouse button.
  • Middle Click: hold the Ctrl key and click either the left or the right mouse button.

What Additional Documentation is Available?

See the list of publications at http://itksnap.org/manual.php

itksnap/ProgramData/HTMLHelp/TutorialSectionIntroductionToAutomatic.html0000644000076500000240000003737110534177574026144 0ustar paulystaff SNAP Tutorial. Section 5.

Section 5. Introduction to Automatic Segmentation.

Step 1. Snake Evolution and Velocities

This section of the tutorial introduces the basic concepts behind the automatic segmentation component of SNAP. There are no action items in this section, just an explanation of the theory and terminology that must be understood to use SNAP effectively. All examples in this section will be made using two-dimensional images. The concepts, however, extend to three dimensions in a straight forward way.

The methodology behind SNAP is called snake evolution. The term snake is used to refer to a closed curve (or surface in 3D) that represents a segmentation. In snake evolution methods, the snake evolves from a very rough estimate of the anatomical structure of interest to a very close approximation of the structure, as illustrated in the figure below.

The red curve is this figure is called the snake. It starts out initialized as a small circle inside of the ventricle, and over time it grows, or evolves, to take shape of the ventricle. So how does it do that?

The snake evolution is governed by a mathematical equation that describes the velocity of every point on the snake at any particular time. The velocity of each point depends (1) on the shape of the snake and (2) on the intensities of the image in the neighborhood of the point. Here is an illustration of some of the velocities that can act on the snake:

In this picture, velocities acting on some of the points are shown as blue and yellow arrows. The velocity vectors always point in the direction perpendicular to the snake. The yellow velocities depend on image properties: they are stronger in the regions of the image where the intensity is homogeneous and weaker where there are edges (discontinuities) in image intensity. By making the snake expands much slower at image edges than at homogeneous regions, we force the snake to assume to form of these regions. The blue velocities depend on the shape of the snake: they are longer at points where the snake is more curved and shorter where the snake is more straight. By applying higher inward velocities at places where the snake is sharp, we cause it to maintain a smooth shape.

The movement of a point on the snake is determined by the sum of the velocities at that point. The figure below shows how the snake evolves under the effect of velocities. The dashed outline represents the snake at the next time interval.

When interacting with the SNAP automatic segmentation mode, the user needs to specify three things:

  1. How will the image intensities affect the image-dependant velocities acting on the snake?
  2. Which types of velocities, and in which proportion, will drive snake evolution?
  3. What form will the initial snake take?

In the following three subsections, we will address these three questions correspondingly.


Step 2. Feature Images

As mentioned above, some of the velocities that drive snake evolution depend on the image intensities. In SNAP, this dependency is represented by the so-called feature image . The feature image is computed by applying a particular function uniformly to the entire image. Image dependent velocities are proportional to the value of the feature image.

Image-dependent snake propagation velocities are proportional to the value of the feature image.

SNAP defines two distinct ways to compute feature images. The first causes the snake to slow down near edges, or discontinuities, of intensity. The second causes the snake to attract to boundaries of regions of uniform intensity. It is very important to understand the effect of the feature image on snake evolution because the proper construction of the feature image is the one most important step in getting a good segmentation.

We will begin by examining the edge feature image, an example of which is shown below:

The edge feature function takes values between zero and one. It is close to zero at strong edges in the image, and it is close to one in regions of uniform intensity. The value of the feature image at a pixel is independent of the intensity level of the input image; it only depends on the differences in intensity between neighboring pixels.

The edge-based feature image is computed using the following pipeline:

  1. The image is smoothed, i.e., blurred using a Gaussian filter.
  2. The gradient magnitude of the image is computed. Gradient magnitude of an image describes how much neighboring pixels differ in intensity.
  3. The gradient magnitude is remapped to the range 0 to 1, with large values of gradient magnitude mapping to 0 and small values mapping to 1.
greylevel image
blurred image
grad. magnitude
edge feature image

The steps of the pipeline are illustrated by the images above. As a user, you have the option to change the amount of blurring that is done. The more blurring is performed, the less likely are the subtle edges in the image to affect the segmentation. You also have control over the function used to remap the gradient magnitude image into the feature image. This lets you increase or decrease the contrast of the feature image. You will learn how to construct the edge feature image in a later section of the tutorial.

Now, let's examine the so called region competition feature images. Region competition is based on thresholding the image intensities, i.e., classifying them into background and foreground intensities. Based on the user's specifications, a range of intensities is designated as foreground, or object intensities.The intensities outside of the range are defined as background. A feature image is constructed by assigning values near 1.0 to foreground intensities, assigning values near -1.0 to background intensities, and assigning values close to zero to intensitues that are on the borderline between foreground and background. This is illustrated below: the left picture shows the histogram of the intensities in the greylevel image. The intensities between 45 and 63 are chosen as foreground, the rest as background. The function plotted in picture on the right maps the intensities between 45 and 63 to positive values, and it maps the rest of the intensities to the negative values.

The feature image resulting from this operation is shown below. The caudate appears as a bright region with positive values of the feature, and the surrounding structures appear dark.

Unlike the edge feature image whose values are non-negative, the region competition feature image takes both positive and negative values. This means that the image-dependent velocities acting on the snake act outward in places where the feature image is positive (i.e., inside the structure of interest), and they act inward in places where the feature image is negative. These velocities are illustrated below.

These velocities force the snake to fit the boundary of the foreground region. Here is an example of snake evolution using the region competition feature function. The snake is initialized both inside and outside of the caudate nucleus. After a few iterations, however, it fits the boundary caudate by shrinking in the background region and growing in the foreground region.

Note that in this image we show the interior of the snake, rather than its boundary, as was shown in an earlier illustration. This is how the snake is actually visualized in SNAP.


Step 3. Types of Velocities for Snake Evolution

In this subsection we will discuss in more detail the different types of velocities for image propagation. There are three types of velocities that can be applied in SNAP to drive snake evolution.

  1. Propagation velocity
  2. Curvature velocity
  3. Advection velocity

Let us examine each velocity type in more detail.

Propagation Velocity

This is the most common velocity used in SNAP. It is proportional to the value of the feature image. The constant of proportionality can be set to be positive, in which case positive values of the feature image cause this velocity to point outwards, or negative, in which case positive values of the feature image cause this velocity to point inwards. In a homogeneous region of the feature image, the propagation velocity is constant and causes the snake to expand (or contract) at a unit speed.

Curvature Velocity

This velocity is used to control the shape of the evolving snake, and it can sometimes prevent the snake from leaking into adjacent structures. The curvature velocity acts inwards and is approximately proportional to the curvature of the snake at the point. Sharp corners in the snake's boundary have high curvature, while points where the snake is straight have low curvature. The effect of curvature velocity is to slow down the snake evolution at places of high curvature, effectively smoothing out the sharp corners that may otherwise be formed.

Advection Velocity

The advection velocity is the most difficult of the three velocities to visualize, but it is very important because it can have a dramatic effect on the quality of the segmentation. The advection velocity is used only in conjunstion with the edge feature image; it is not used with the region competition feature image. In qualitative terms, it causes the snake to slow down or stop as it approaches edges in the greyscale image. More precisely, the velocity is defined by the dot product of the unit vector perpendicular to the snake and the gradient vector of the feature image. That means that when the snake is parallel to an image edges and close to it, the advection force acting inwards on the snake is greatest. The illustration below shows the gradient vectors of the feature image and the corresponding advection velocities.


Step 4. Snake Initialization

As mentioned, the snake can be initialized very roughtly, using a circular bubble. More than one bubble can be used to initialize the snake. As the snake evolves, its disconnected components may merge together, as illustrated by the sequence of images below.

The following rule is improtant for proper snake initialization :

When using the edge feature image, make sure that the initialization is fully contained in the anatomical structure that you want to segment, since the snake propagates outwars only.

When using the region competition feature image, the initialization may lie outside of the structure of interest, since the snake propagates both inward and outward.

 


Step 5. Summary

One does not need to fully understand the mathematical theory behind snake evolution in order to use it in SNAP. Basically, a snake is a closed curve or collection of closed curves in 2D, and a closed surface or collection of closed surfaces in 3D. Snake evolvution is described by the sum of different types of velocities that act on each point of the snake in the direction perpendicular to the snake. Some of these velocities are image dependant, while others depend on the shape of the snake. The image dependant velocities are defined in terms of the feature image, to which they are proportional. SNAP can create two types of feature images, one based on edges in the input image, the other based on regions of uniform intensity. SNAP supports three types of velocities: the image dependent propagation velocity, the shape dependent curvature velocity and the image dependent advection velocity.

In order to perform an automatic segmentation in SNAP, the user needs to (1) choose the type, edge or region, of the feature image to be used, (2) select the parameters used to compute the feature image, (3), initialize the snake using spherical bubbles, and (4) choose the relative weights of the different types of velocities that drive snake evolution. The following two sections of the tutorial describe how these tasks are performed through the SNAP user interface.

itksnap/ProgramData/HTMLHelp/TutorialSectionLoadingImages.html0000644000076500000240000002113310534177574024001 0ustar paulystaff SNAP Tutorial. Section 2.

Section 2. Loading a 3D Image File

Reading a three dimensional image is not as simple as opening a document in a word processor. To facilitate this process, SNAP offers a special wizard for loading images. This section of the tutorial will guide you through the different pages of this wizard. It will take 5 to 15 minutes to complete.


Step 1. Download an Example Image [recommended]

We strongly recommend that you use a set of images that we have provided as an example during this tutorial. A compressed file containing these images can be downloaded using the link shown below. Please note that the image can not be downloaded using the SNAP help viewer. Copy the link and open it in an external web browser.

http://www.itksnap.org/download/snap/files/MRI-crop.zip

Save the file MRI-crop.zip in a convenient directory. This file is a compressed archive, and can be opened using a decompression utility such as WinZip® on Windows or unzip on Unix. The archive contains the following files:

MRIcrop-orig.gipl An MRI image of a volunteer's brain
MRIcrop-seg.gipl An example segmentation of the MRI image done using SNAP.
MRIcrop-seg.label A file containing label descriptions for the segmentation

Please consult your system administrator if you need help with downloading and extracting the example images.


Step 2. Open the 'Load Image' Wizard.

Launch SNAP if you have not done so already.

Select File | Load Data | Greyscale Image using the menu bar located at the top left corner of the SNAP window, as shown in the picture below:

The wizard will appear, as shown below:


Step 3. Select the Image File.

There are three ways to specify the filename of the greyscale image. First, the filename can be typed into or pasted into the text box at the top of the wizard. Second, the 'Browse...' button can be used to locate the image file in your computer's file system. Finally, the 'History' button can be used to select a file that has been recently opened.

Click the 'Browse...' button. The following window will appear:

Notice that the file browser includes a 'Favorites' menu, which can be used to remember and quickly access frequently used directories.

We suggest you that for this tutorial you use the file MRIcrop-orig.gipl, which can be downloaded using the instructions given at the beginning of this section.

Use the file list to find and select an image, and press OK.

Once you select an image, its compete filename and image format will be displayed in the wizard, as illustrated below. SNAP tries to guess the format of the image file based on the file's extension. For some files, SNAP may not guess correctly, and you will need to select the correct format from the drop box.

Press the 'Next' button to continue to the next page of the wizard.

If you have selected to read an image in RAW file format (this is an advanced feature), please follow this link for additional information, before proceeding to the next subsection.


Step 4. Specify Image Orientation.

A volumetric image is a three-dimensional array of intensities. Every pixel in the image is referenced by three coordinates, x, y, and z, with z indicating the number of the slice to which the pixel belongs, y indicating the row in the slice, and x indicating the column. In order to display images correctly, SNAP needs to know the mapping between this x, y, z image coordiate system and the patient coordinate system. In other words, SNAP needs to know the mapping between directions x, y, z and the right-left, anterior-posterior or inferior-superior directions in the human body.

The orientation page of our wizard is used to specify this mapping, and it is shown below.

This page is used to specify the mapping between the patient coordinate system and the coordinate axes of the three-dimensional image. The orientation can be specified by (a) selecting one of the preset modes, (b) selecting 'Custom Orientation' in the preset drop box and assigning a patient direction to each image axis, or (c) by specifying a three-letter 'RAI' code.

RAI codes let you specify image orientation quickly. Each of the three letters in the code is an abbreviation for the direction in the patient coordinate system. For example, the code ASR means that the X axis corresponds to the anterior-posterior direction, the Y corresponds to the superior-inferior direction, and the Z corresponds to the right-left direction. The origin of the image is in the anterior-superior-right corner of the body.

For the image MRIcrop-orig.gipl, use the default orientation setting.

For other images, specify the appropriate setting. If you don't know the correct setting for your image, just use the default. You will still be able to use most of SNAP functionality.

Press the 'Next' button to continue.


Step 5. Review Image Information.

The last page of the wizard shows summary information about the loaded image.

Press the 'Finish' button to continue.

The wizard will disappear and the SNAP main window will display the image:


 

itksnap/ProgramData/HTMLHelp/TutorialSectionManualSegmentation.html0000644000076500000240000004034310534177574025075 0ustar paulystaff SNAP Tutorial. Section 4.

Section 4. Manual Segmentation

This section describes how SNAP can be used for manual segmentation. You will learn about working with segmentation labels, painting regions ontwo-dimensional slices of the image, and saving and loading segmentation results. This section requires approximately 10 minutes to complete.

Step 1. About Segmentation and Segmentation Labels

To segment an anatomical structure in SNAP means to assign a label to each voxel in the structure. A label is a number between 0 and 255. Associated with each label is a name and a set of display settings, such as the color used to display the label. For example, we can associate the name 'cuadate' and the color red with the label 3. When you first load a grey image into SNAP, the special label 0 is assigned to each voxel in the image. This label is associated with the name 'Clear' and means that a pixel has not been segmented yet.


Step 2. Create a Few Labels

When you first load SNAP, it creates a default set of six labels, with names 'Label 1' through 'Label 6'. Before starting segmentation, we will edit these labels, assigning them meaningful names and colors.

The SNAP control panel contains a sub-panel that is used to interact with segmentation labels. It is shown below.

At the top of this sub-panel are two drop-down boxes. The first box is used to select the label that is currently used for manual and automatic segmentation. The second box is used to select the label or labels that are affected by the segmentation. We will see how to use these drop boxes a few steps below. Below the drop-boxes is located a button called 'Edit Labels...', which is used to associate names and colors with label numbers.

Press the 'Edit labels...' button. The following window will appear.

This label editor can be used to modify information associated with each label and to add new labels. The left pane of the editor lists the labels that are currently available. The right pane is used to modify the label currently selected in the list. You can change the color in which the label appears in SNAP, the name associated with the label, and the transparency of the label.

Change the description text box to 'Caudate'

Change the color associated with the label to light green

Press the 'Close Window' button to apply the changes and close the window

The following additional information is associated with each label:

Opacity: A label can be made to appear partially transparent when it is displayed in the 3D window. This is useful when one structure, such as the skull, obstructs the view of another.

Visibility: A label can be hidden from display, either in the 3D window or in all windows. Like transparency, this can be used to hide obstructing structures. Additionally, you can tell SNAP to apply manual and automatic segmentation only to voxels with labels that are not hidden. Thus, by hiding a label, it is possible to prevent various segmentation operations from affecting it.



Step 3. Load Labels from a File

If you have downloaded an image archive, as recommended in Section 2, Step 1, then you can load a set of segmentation labels from a file.

Select File | Load Data | Label Descriptions... using the menu bar. A dialog will appear.

Use the 'Browse...' button to locate and select the file MRIcrop-seg.label

Load this file by pressing the Ok button.

The file contains a dozen or so labels for describing brain anatomy. If you open the label editor, it should look like this:


You can create your own label files. Just use the label editor to change existing labels and to add new labels, and then select File | Save Data | Label Descriptions... from the menu bar.


Every time you work with an image, SNAP remembers all sorts of information about your session, and restores this information next time that you open the image. The list of labels is part of the information that SNAP remembers.



Step 4. Segment the Caudate Nucleus

In the step, we will manually segment the caudate nucleus. This step assumes that you are working with the image MRIcrop-orig.gipl, as recommended in Section 2, Step 1.

Make sure that the 'Segmentation Label' subpanel looks like this:

This means that we are going to apply the label called 'caduates' to the voxels that we segment. The 'draw over all labels' settings means that segmentation will override all labels that have already been assigned to voxels.

Enter the crosshairs mode.

Position the crosshairs in such a way that the right caudate nucleus (shown below) is visible in all three slices.

Now we are ready to begin manual slice-by-slice segmentation.

Select the polygon tool in the IRIS toolbox (shown below)

The polygon tool is used to paint closed polygons on top of the axial, sagittal and coronal slice windows. These polygons are then filled with the currently selected label. By painting polygons slice by slice, a whole 3D structure can be selected.

Click the left mouse button somewhere on the boundary of the right caudate.

Continue clicking the left button along a sequence of points along the boundary.

Click the right mouse button to close the polygon (i.e., to connect the first an the last vertices).

The green rectangle around the polygon indicates that all of the vertices are currently selected. Selected vertices can be moved by clicking and dragging the left mouse button. Let's select some vertices and move them:

Use the mouse to draw a box around some of the vertices in the polygon (to draw the box, click and hold the left mouse button near the upper left part of the slice window, drag the mouse towards the lower right corner and release the button once the box contains two or more vertices).

Click and hold the left mouse button inside the green box and drag it to move the selected vertices.

In addition to selecting vertices and moving them, you can use the buttons located under each slice window to manipulate the polygon. Use the 'delete' button to delete the currently selected vertices from the polygon. Use the 'insert' button to insert new vertices between adjacent pairs of selected vertices.

Edit the polygon by selecting, moving, deleting and inserting vertices until you are satisfied with it.

Use the 'accept' button to fill the polygon with the segmentation label 'caudates'.

Once you have clicked 'accept', the voxels inside of the polygon are assigned the currently selected segmentation label.

If you are not satisfied with your result and want to erase it, you can do so by changing the active drawing label to the 'Clear' label, and painting a polygon on top of the part of the segmentation that you want to erase. When you press 'accept', the voxels withing this polygon will be assigned the clear label.

SNAP remembers the last polygon that you used and lets you paste it. This makes segmenting a structure faster.

Move to the next slice in the slice window where you just painted (using the scroll bar or the mouse wheel).

Press the 'paste' button to recall the last accepted polygon.

Edit the pasted polygon by selecting and moving vertices, and accept it when satisfied.

So, by moving from slice to slice, pasting, editing and accepting polygons you can quickly segment the caudate. Notice that as you accept polygons in one of the slice windows, it becomes visible in the other slice windows as a line of voxels. SNAP is unique in letting you view and edit the segmentation in all three orthogonal slice windows at once.

[Optional] Continue segmenting the caudate in all three slice windows until you are satisfied with the result.

Press the 'update mesh' button located below the 3D view window to see the segmented structure in three dimensions.

You can learn a lot more about interacting with the 3D window in a later section.


Step 5. [Optional] Save your work

SNAP saves segmentation results as images. Each voxel in this image contains a number between 0 and 255, which indicates the segmentation label assigned to the voxel. Segmentation images can be saved in a variety of image formats and can be read by other programs.

Select File | Save Data | Segmentation Data... using the menu bar. A dialog will appear.

The dialog asks you for the file name of the image that will be saved. You can type in a filename, use the 'Browse' button to search for an appropriate location on your computer, or you can use the 'History' button to enter up a recently used file name.

Supply a filename to the wizard using the 'Browse' button.

Once you select an image, its compete filename and image format will be displayed in the wizard, as illustrated below. SNAP tries to guess the format of the image file based on the file's extension. For some files, SNAP may not guess correctly, and you will need to select the correct format from the drop box

.

Press 'Save' to save the segmentation.

 

itksnap/ProgramData/HTMLHelp/TutorialSectionRegionSegmentation.html0000644000076500000240000010564410534177574025111 0ustar paulystaff SNAP Tutorial. Section 6.

Section 6. Automatic Segmentation using Region Competition Snakes

This section gives step by step instructions on segmenting an image using the region competition snake (in last section's terminology, snake evolution that uses the region feature image). This section assumes that you are working with the image MRIcrop-orig.gipl, as recommended in Section 2, Step 1. We will segment the caudate nucleus and the ventricles in this image. This section also assumes that you are using the label file MRIcrop-seg.label.

You can, however, follow the general directions of this section using a different image, but you will have to use your own judgement in selecting various parameters.


Step 1. Discard the Previous Segmentation.

In Section 4, we have created a manual segmentation. In order to perform the segmentation automatically, we will discard the manual segmetnation. This involves reloading the greyscale image.

Select File | Load Data | Greyscale Image.

Use the History button to select the image loaded most recently.

Step through the rest of the wizard, as descibed in Section 2.



Step 2. Select the Label to Use for Automatic Segmentation

We will be segmenting the caudate nucleus. We have to make sure that the appropriate combination of the current drawing label and background (draw over) label is selected.

Make sure that the label "caudates" is selected as the current drawing label

Make sure that "All Labels" is selected in the Draw over drop-down box.

The draw over label functions the same way in automatic segmentation as in manual segmentation. It lets you apply the results of automatic segmentation to all labels, to all visible labels, to the clear label, or to a particular label. This gives you a lot of creative control when segmenting multiple structures. For instance, to segment a structure that is embedded inside another structure, you can first segment the outer structure with label A and then segment the inner structure with label B and with the draw-over label set to A.



Step 3. Select the Region of Interest using the Snake Interaction Mode.

The automatic segmentation component of SNAP requires a lot of computer resources. Both the amount of memory and the time required to compete a segmentation can be reduces by selecting a sub-region of the image on which to perform segmentation. In this step, we will select a subregion of the image that contains the caudate nucleus.

Make sure that the slice is fully visible in each the slice windows by pressing the Reset View buttons underneath the slice windows.


Select the snake tool in the IRIS toolbox (shown below)

As you select the snake tool, a pink-colored dashed selection box will appear at the border of the each slice in the slice windows, as shown below:

The selection box displays the region of interest that will be used in automatic segmentation. The use of word region here should not be confused with region competition. The region of interest is a rectalinear box, while the regions in region competition are of arbitrary shape and are defined by uniform intensity. We will now adjust the region of interest by dragging the sides of the selection box

In one of the slices position the mouse cursor near one of the corners of the selection box

Hold down the left mouse button and drag the mouse towards the center of the image. The size of the box will be adjusted as you move the mouse.

As you are dragging the selection box, its edges change color from red to yellow.

Using all three slice windows, adjust the selection box to include the left and right caudate nuclei. Try to simulate the selection box in the picture below.

If the selection box disappears in one of the slice windows, that means that the crosshairs position is outside of the selection box. You can adjust the crosshairs position in one of the adjacent slices.

The crosshair position can be adjusted in this mode using the left mouse button as usual. However, you will need to click a few pixels away from the selection box to move the crosshairs.

Notice that the tool options control subpanel contains two buttons: Reset Region and Segment 3D. The former is used to reset the region of interest to the entire image. The second is used to enter the automatic segmentaiton mode of SNAP.

Once you have adjusted the region of interest, press the Segment 3D button.



Step 4. Familiarize Yourself with the Automatic Segmentation Mode.

When you press the Segment 3D button, the SnAP user interface changes considerably.

Let's look at some of the new elements that have appeared:

  1. The slice windows have fewer buttons and only show the region of interest selected in the previous step.
  2. The IRIS toolbar has been replaced by a smaller toolbar containing only two buttons. These buttons are used to change the crosshairs position and to zoom and pan around the region of interest. The buttons are used in the same way as in the manual SNAP mode that you are used to, except that fewer options for setting zoom are provided.
  3. A large panel labeled "Segmentation Pipeline" appears underneath the new toolbar. This panel contains the 'wizard ' used for controlling automatic segmentation. We will work with this wizard extensively in this section.
  4. A button labeled Cancel Segmentation appears at the bottom of the control panel. You can use this button at any time to return to abort the automatic segmentation and return to the manual SNAP mode.
  5. A pair of buttons appear underneath the 3D window. These buttons have the same functionality as the similar looking buttons in the 3D toolbox in the manual mode.

If the image slices appear washed out, i.e., have low contrast, use the Intensity Curve window to change the contrast as described in Section 3.



Step 4. Construct a Region Competition Feature Image.

Recall the concept of edge and region competition feature images from the last section.In this step we will construct a region competition feature image appropriate for segmenting the caudate nuclei.

First let's tell SnAP which type of the feature image we will be using:

Select the option Intensity Regions in the section A of the Segmentation Pipeline Wizard.

Now, let's estimate the range of intensities to which the voxels in the caudate nucleus belong.

Move the crosshairs around the caudate nuclei. Look at the values of the grey level intensity, which is reported in a box labeled "Grey" underneath the toolbar.

You will find that the intensities in the caudate range between the high 40's and low 60's. This information is improtant for contructing the feature image.

Press the button labeled Preprocess Image... This button is used to construct the feature image. The following window will appear

This window is used to specify the mapping between the greyscale image intensities and the values of the feature image, which fall into the range between -1 and 1.

In the Intensity Region Filter window

  1. Set the threshold direction to Below and Above.
  2. Set the lower threshold to 48.
  3. Set the upper threshold to 63
  4. Set the smoothness to 1.7

After you use the mouse to click on and move the knobs that are used to change the threshold and smoothness values, you can use the left and right arrow keys to move these knobs one value at a time.

As soon as you change some of the parameters, the SnAP slice windows will display the feature image instead of the grey image. As you change the parameters, the slice windows are updated immideately. If you uncheck the Preview result checkbox, the slice windows will only reflect the values of the parameters when you press the Apply button.

Our goal in setting the parameters is to make sure that the voxels inside of fthe caudate nuclei are assigned positive values in the feature image, and that the voxels outside of it are assigned negative values. There are two ways to check that this happens:

Move the crosshairs around in the slice windows (the intensity region filter window will remain on top). Look at the values of the feature image, which are reported in a box labeled "Preproc" underneath the toolbar.

or

Check the Combined Display check box. The greylevel image is shown again, but the pixels where the feature image is positive are painted over with the color of the current segmentation label, as shown below. This is an easy way to make sure that the pixels in the caudate have a positive feature image value.

Uncheck Combined Display check box to see the feature image again.

The smoothness value determines the steepness of the mapping curve. It does not affect the sign of the feature function at any particular voxel, but it does have an effect on the smoothness of the snake evolution.

When you are satisfied with the feature image, press Okay to compute the feature image at all voxels and close the intensity region filter window.

SnAP lets you save and load feature images. Just use the appropriate menu items in the File menu. You can also load feature images by pressing the Load from File... button in the segmentation pipeline wizard.


Voxels in feature images are saved as floating point numbers, so some applications may not be able to load the saved images. You can not save feature images in GIPL format because if does not support floating point voxels. The Meta format is recommended.

We are now almost done with the first step of the Segmentation Pipeline Wizard.

Press the Next button in the Segmentation Pipeline Wizard to proceed to the next step.


Step 5. Initialize the Snake with Bubbles

The Segmentation Pipeline Wizard should be displaying "Step 2 od 3", as shown below.

This step of the wizard is used to position spherical bubbles to that initialize the snake, as described in the previous section.

Move the crosshairs such that the crosshairs position is inside of the right caudate nucleus in all three slice windows.

Press the Add Bubble button to place a bubble at the crosshairs position.

Use the Radius slider to change the radius of the bubble.

After adding a bubble, the SnAP window should look like this:

Place one more bubble inside the right caudate and place two bubbles inside the left caudate.

The result should look something like this:

To remove a bubble, select it in the list of bubbles underneath the Radius slider and press the Remove bubble button.

Finally,

Press the Next button in the Segmentation Pipeline Wizard to proceed to the next step.

As an alternative to using bubbles, you can use manual segmentation to initialize the snake. Before starting automatic segmentation, create a manual segmentation as described in Section 4 using the same label that you wish to use for snake segmentation. The manual segmentation will be used as the snake initialization, and you would not have to add any bubbles.



Step 6. Run the Snake Evolution

The Segmentation Pipeline Wizard should be displaying "Step 3 od 3", as shown below.

This wizard page allows you to set the parameters for snake evolution, and it allows you to control the snake using VCR-style controls.

Press Set Parameters... to open the snake parameter window.

The Snake Parameters window is shown below. This window consists of two parts. On the left is a control panel used to specify various parameters, mainly the weights of the propagation, curvature, and advection forces that were discussed in Section 5. These velocities are displayed using a fixed feature image, and not the feature image that you are working with. Nevertheless, this image is useful for understanding the relative contribution of the forces to snake evolution

The red curve for which the velocities are shown can be changed using the mouse. Left-click once in one of the four windows showing the curve and yellow 'control points' will appear. Move the control points by dragging using with the left mouse button. Try moving the curve and see how the foces change.

The snake parameter window also lets you save snake evolution parameter settings to a file and to load them from a file.


Change the curvature velocity weight to from 0.20 to 0.15

Press Accept to close the window


The Mathematical Mode tab of the Snake Parameters Window shows mathematical expression for the partial differential equation that drives the snake evolution, and allows you to set the parameters directly as constants in this equation.

You can also choose to display the experimental equation, which contains more terms and gives you more control over snake evolution.


The Advanced Mode tab of the Snake Parameters Window is discussed in the section on Tips and Tricks. It can be used to speed up large segmentations.

Now that we've set the snake parameters, we are ready to run the snake evolution. Notice the VCR-style controls located in the Segmentation Pipeline Wizard.

These controls have the following functionality:

  • The leftmost control is used to rewind the snake after it had been evolving for some time.
  • The second control is used to run the snake until stopped.
  • The third control is used to stop the running snake
  • The rightmost control is used to step the snake, i.e., to run for a fixed number of iterations.

Underneath the VCR buttons is a control that allows you to set the size of the step used in snake evolution. The larger the step value, the fewer times will the user interface be updated as the snake evolves. For small segmentations, it is advisable to leave the step size at 1. Next to the step size dropbox is a display that shows the current iteration.

Press the Step button several times to run the snake for a few iterations

Press the Run button once and watch the snake fill up the caudates.

Press the Stop button when the caudates have been filled up

Press the Rewind button if you want to restart.

To see the segmentation result in 3D, you can press the Update Mesh button underneath the 3D window, or you can select the Update Continuously checkbox.

Warning! Selecting Update Continuously will degrade segmentation performace significantly, possibly by orders of magnitude!

The result of the segmentation should look something like this.


Step 7. Finalize the Segmentation

The final step in the automatic segmentation process is to return to the SNAP manual segmentation mode, incorporating the segmentation results with other structures that have been previously segmented.

Press the Finish button in the Segmentation Pipeline Wizard

The Segmentation Pipeline Wizard will disappear, and the layout of the SNAP control panel will return to normal.

[Optional] Save your segmentation as described in Section 4.



Step 8. Use the 3D Tools to Edit the Segmentation Results

In this step we will postprocess the results of the segmentation using 3D tools.

Press the Update Mesh button below the 3D window to render the results


Now, let us examine the different tools available in the 3D Toolbox, which is shown below.

The toolbox contains four tools. Top to bottom, left to right, they are

  • 3D Trackball Tool: Used to rotate, zoom, and pan in the 3D window.
  • 3D Crosshair Tool: Used to set the crosshair location by clicking on anatomical structures shown in the 3D window
  • 3D Scalpel Tool: Used to repaint or erase parts of the anatomical structures shown in the 3D window
  • 3D Spray Paint Tool: Used to add anatomical landmarks to the surface of anatomical structures shown in the 3D window

Let's begin by examining the 3D trackball tool:

Select the 3D Trackball Tool

Press and hold the left mouse button and move the mouse to rotate the 3D view.

Press and hold the right mouse button and move the mouse up or down to zoom in and out in the 3D view

Press and hold the middle mouse button and move the mouse to pan in the 3D view.

Now try the 3D crosshair tool:

Select the 3D Crosshair Tool

Position the mouse over any point on the caudates and click the left mouse button. The crosshair position will move to the selected point and the slices shown in the slice windows will be changed.

Now, something more complex. We will use the 3D scalpel tool to assign different labels to the left and right caudate nuclei.

Use the Edit Labels button to add two new labels called 'left caudate' and 'right caudate' with different colors (for information on editing labels, see Section 3).


Select 'right caudate' as the current drawing label, and select 'caudates' (the label used for automatic segmentation) as the label to draw over, as shown below.

The 3D scalpel tool works by partitioning the space into two regions separated by a plane. The segmentation labels on one side of the plane are replaced by the current drawing label, as long as they agree with the current setting of the draw over label. In order to use the scalpel tool, we need to first rotate the 3D view such that a line can be drawn between the two caudates.

Use the 3D Trackball tool to rotate the 3D view in such a way that the caudates can be separated by an imaginary line, as shown below.

Now, we will use the 3D scalpel tool to actually draw a line in place of the imaginary line

Select the 3D Scalpel Tool

Click the left mouse button at one end of the imaginary line separating the caudates

Move the mouse around the 3D window. You will see a white line indicating where the 'cut' will be made, and an arrow indicating which half of the space will be relabeled.

Click the left mouse button again at the other end of the imaginary line

The result of this operation should look like this:

If you are not satisfied with the 'cut', press the Reset View button and try again.


Otherwise, press the Accept button to repaint one of the caudates with the active drawing label and press the Update Mesh to see the result in 3D.

The 3D scalpel is a very powerful tool for editing segmentation results. Using the clear label as the active drawing label, you can erase parts of the segmentation that have leaked outside of the caudates.


Step 9. [Optional] Segment the Ventricles

Use the procedure outlined in the steps above to segment the ventricles in the image. You will need to use the Above threshold direction setting when creating the feature image.

The result should look like this

itksnap/ProgramData/HTMLHelp/TutorialSectionTipsAndTricks.html0000644000076500000240000002205710534177574024026 0ustar paulystaff SNAP Tutorial. Section 9.

Section 9. Tips and Tricks

This section contains a list of loosely organized list of SNAP features that can make it a more powerful tool than it may appear at first look. Click below to jump to a specific tip:

  1. Displaying image information
  2. Focusing on a Single Slice View
  3. Another Way to Initialize Segmentations
  4. Automatic Segmentation of Anisotropic Images
  5. Advanced Parameter Settings
  6. Large Segmentations in SNAP

Displaying Image Information

SNAP includes an image information window that can be brought up at any time. The window displays dimensions of the image, voxel size, current cursor location and other useful information.

Select File | Image Info ...

Focusing on a Single Slice View

When performing manual segmentation, it may be useful to dedicate a larger area of the screen to a single slice of the 3D image. SNAP allows you to replace the standard four-view layout (three slice views and a 3D view) with a single-view layout.

Click on the plus button ( ) in one of the four views to expand that view.

Click on the plus button ( ) in the expanded view to collapse the view and display the other three views.

Another Way to Initialize Segmentations

In this tutorial, automatic segmentations were initialized using 'bubbles'. There is an other way to initialize segmentation. Whenever you enter the automatic segmentation mode with some label X as the current drawing label, all the pixels already labelled X will be passed on to the automatic segmentation mode as initialization pixels. Therefore, you can use the results of one segmentation attempt to initialize another. You can also use manual segmentation tools to construct the snake initialization.

Automatic Segmentation of Anisotropic Images

The snake evolution algorithms used in SNAP are poorly suited for non-isotropic images, especially wen anisotropy is great, e.g., when pixels have size 1x1x3 or 1x1x5, which is quite common in medical images. The option to resample an image has been added to SNAP especially for dealing with such images

Before entering the automatic segmentation mode, you have an option to resample the region of interest passed on to automatic segmentation. Just check the box 'Resample Region' before pressing the Segment 3D button.

After you press Segment 3D, the Region Resampling window will appear:

You can use this window to change the dimensions of the pixels that will be passed on to SNAP's automatic segmentation mode. For instance, if the input image has 1x1x5 pixels, you can have SNAP replace each pixel by 5 isotropic ones, as shown above. Be aware, however, that supersampling an image in this way increases memory usage (fivefold in this case), and should be done only for small regions of interest. A more conservative option is to resample 1x1x5 pixels to 2x2x2 pixels.

The automatic segmentation will be performed on the resampled image, and the results will be resampled back to the resolution of the original, anisotropic image.

You can choose between cubic, linear, and nearest neighbor interpolation modes. Cubic produces the best quality resampled images, but is slow. Nearest neighbor interpolation is fast but results in greatest distortion. Cubic interpolaion is recommended in most cases.

Advanced Parameter Settings

The 'Advanced' tab of the snake parameter setting window is shown below:

This tab allows you to change the algorithm used for snake propagation. At the time of this writing, the default Sparse Field Level Set Algorithm is the most efficient option. The Narrow Band Level Set Algorithm is a little slower and some small differences in the results of the two methods have been detected. The Narrow Band algorithm is more robust with respect to increasing the segmentation time step (see next paragraph). The Dense Level Set Algorithm is included purely for experimental purposes. It is orders of maginitude slower that the other two options.

This tab also allows you to override the default time step used in snake propagation. The default setting is to use the so-called optimal time step: the largest time step that allows some mathematical guarantees on the segmentation error to be established. In practice, it is possible to use a larger time step value, resulting in proportionally faster segmentations, but at a cost of (sometimes unpredictable) error. We recommend overriding the optimal step when doing rough segmentations on large regions of interest.

The experimental noise reduction option can be used in conjunction with overriding the optimal time step. For very large speedups, it may reduce the error accumulation. We recommend to leave the noise reduction setting at zero.

Large Segmentations in SNAP

You may have noticed that the speed of segmentation in SNAP is roughly proportional to the size of the structure you are segmenting. If that were always the case, it would take a very long time to segment large structures such as the whole of the brain white matter or gray matter. Luckily, SNAP provides a couple of ways to speed up large segmentations.

The first way is to take advantage of the option to resample the region of interest when entering the automatic segmentation mode. The details on such resampling are given in the Tip on Automatic Segmentation of Anisotropic Images.For example, if the original image has 1mm cube pixels and you resample it to 2x2x2 resolution, you will reduce the amount of memory needed for segmentation by eightfold and will speedup the segmentation by an eightfold as well. The gains in speed come, of course, at the price of accuracy. However, you can use subsampling to get a 'quick and dirty' segmentation and then use that segmentation as an initialization to another segmentation, this time without resampling. See the Tip on Another Way to Initialize Segmentations for more details on using segmentation results for initialization of subsequent segmentations.

The second way to speed up segmentations is to override the default time step used for snake propagation. See the Tip on Advanced Parameter Settings for details.

Using the Spray Paint 3D Tool for Landmark Placement

The manual mode of SNAP includes the 3D spray paint tool .

This tool is used to mark points on the surface of the 3D rendering of the segmentation results with the active drawing label. Simply select this tool, position the mouse over a rendered segmentation results (e.g., ventricles, as shown below), and press the left mouse button. Tiny bubbles will appear on the surface of the segmented structure under the mouse. You can move the mouse while pressing the left mouse button. Press the Accept button to relabel the pixels corresponding to these bubbles with the active drawing label, or press the Reset View to discard the spray painted bubbles.

Spray painting is useful for assigning unique labels to specific anatomical landmarks. It can also be used for tracing curves on the surface of anatomical structures, e.g., for rough tracing of the sulci or gyri.

 

itksnap/ProgramData/HTMLHelp/TutorialSectionViewingImages.html0000644000076500000240000003644110534177574024044 0ustar paulystaff SNAP Tutorial. Section 3.

Section 3. Viewing the 3D Image.

This section shows you how to interact with a three-dimensional image in SNAP. It assumes that you have successfully loaded an image following the instructions in the previous section. This section requires approximately 10 minutes to complete.

Step 1. Examining the Control Panel.

After loading a grey image, SNAP puts itself into manual segmentation mode. The control panel for this mode is displayed to the right. It is divided into four sub-panels, which are labeled "IRIS Toolbox", "Tool Options", "Segmentation Label", and "3D Toolbox". The IRIS toolbox contains four buttons, which are used to select different modes of interaction with SNAP. These interaction modes are called tools for short. The contents of the tool options sub-panel depend on the tool that is currently selected in the IRIS toolbox. The segmentation label sub-panel is used to select the labels used for manual and automatic segmentation, and the 3D toolbox provides additional tools used to interact with the three-dimensional rendering of the segmented structures.

In this section of the tutorial you will learn how to use two of these tools in the IRIS toolbox: the crosshairs tool and the zoom/pan tool, which provide different ways to quickly navigate through the three-dimensional image.


Step 2. Viewing Orthogonal Slices.

Once you have successfully loaded an image, the SNAP main window should look like this:

Let's focus our attention on the three of the four dark windows that are displaying slices of the volumetric image (the fourth window is used for 3D display and will be discussed later). These three slice windows display three views of the image volume, in the orthogonal axial, coronal, and sagittal planes.

Notice that a scrollbar is located next to each slice window. Under each scrollbar is located a slice counter, which shows the index of the displayed slice and the total number of slices in the direction perpendicular to the slice.

Use the scrollbars to change the currently displayed slice in one of the three slice windows. If your mouse has a scroll wheel, you can use it to change the slice instead. Make sure that the mouse cursor is inside of the slice window that you want to interact with.

A key feature of SNAP is that the three slices are not viewed independently, but rather linked by a common cursor. As you manipulate the scroll bars, you will notice that the blue lines in the other two slice windows are moving. These blue lines indicate the intersection between orthogonal slices. As we will see shortly, the common cursor makes it easy to examine the volumetric image near any particular voxel.

Step 3. Navigating the Image in Crosshairs Mode

Crosshairs mode is used to quickly focus all three slide views onto a location in the image. When you click on one of the slices, the other two slices are adjusted so that the pixel where you clicked becomes the point of intersection of the three slices. Thus, you can easily examine the orthogonal views of the image in the neighborhood of any pixel.

Select the crosshairs tool in the IRIS toolbox (shown below)

Left-click on any one of the three slices. Watch how the other two slices change.

You can also hold down the left mouse button and move the mouse around the slice for faster navigation.

Remember that you can use the scroll wheel of your mouse to change the slice currently displayed in the slice window under the mouse. This lets you move around the image in three dimensions, without moving the mouse away from the slice window!

You will notice that the common cursor formed by the blue dashed lines always points to the same voxel in all three slice views. The grey level intensity of the voxel and the segmentation label corresponding to it are displayed in the tool options sub-panel of the control panel:

Since we have not done any segmentation yet, the segmentation label at every voxel has numeric value 0 and is called "Clear". The action of clicking on a voxel in the image to check its greylevel intentensity value is called probing. We will make use of probing later in this tutorial.

In other SNAP modes where the left mouse button is used for other tasks, you can still move the crosshairs by using the middle mouse button, or by holding the Alt key on the keyboard and using the left mouse button.

Step 4. Navigating the Image in Zoom/Pan Mode

While the crosshairs tool lets you move around the image, the zoom/pan tool allows you to zoom in on a particular region of a slice and to pan around when the whole slice does not fit into a slice window.

Select the zoom/pan tool in the IRIS toolbox (shown below)

Click and hold the right mouse button anywhere in one of the slice windows, and drag the mouse up to zoom into the slice. Drag the mouse down to zoom out.

Click and hold the left mouse button anywhere in one of the slice windows, and drag the mouse in any direction to pan around the image.

Click the Reset View button located below each slice window to restore the original zoom level, as illustrated below.

By default, SNAP lets you change the zoom in all three slice windows independently. However, it is often desireable to make sure that each of the three slices is displayed using the same zoom level. The tool options sub-panel, which is shown below, allows you to link the zoom level between the three slice views, as well as to specify zoom level explicitly.

Tick the "Zoom slices together" check-box in the tool options sub-panel. Try zooming in and out in one of the slice windows by dragging the mouse (as before). Notice how the other slices zoom in accordingly.

Type in the number 200 into the "Zoom %" text box, and press the Enter key. The slices will zoom to double the default zoom level.

Click the "Reset 2D views" button. The zoom level will return to default in all three slices.


When slices are zoomed together, the number of pixels on the computer screen corresponding to a unit of length in the space of the 3D image is the same in all three slice windows. When slices are zoomed independently, the a distance in image space may correspond to different distances in the slice windows.

Step 5. Adjusting Image Contrast (a.k.a. Windowing)

The MRI image that we have loaded in the last section of this tutorial has fairly low contrast. It is difficult to see some of the structures in the image. SNAP provides a windowing dialog for adjusting the mapping between intensities in the 3D image and the intensities of the displayed slices.

Select Options | Intensity Curve... using the menu bar. The windowing dialog will appear.

This dialog consists of a plot area on the left and several controls and buttons on the right. The plot area displays the current mapping between image intensities and display intensities as a think red line with four embedded yellow diamonds. These diamonds are called control points and can be used to change the shape of the mapping. Also, the plot area shows in blue the histogram of the intensities in the grey image. Notice that most of the intensities fall in the lower third of the intensity range. So, to improve the contrast, we will adjust the mapping accordingly.

Type the number 100 into the 'Window' text box, and press the Enter key.

Notice how the red line changes, and the displayed slices change contrast. You can also directly manipulate the curve by moving the control points.

Move the mouse over one of the yellow control points, hold down the left mouse button and move the mouse to update the mapping

The number shown in the text box labeled 'Level' is the image intenisty of the leftmost control point. The number shown in the text box labeled 'Window' is the difference between the rightmost and leftmost control points. The slider 'Control Points' allows you to change the number of the control points, increasing the control over the shape of the mapping curve.


Press the 'Close' button to close the dialog

Step 6. [Optional] Adjusting Slice Display Order.

By default, SNAP shows the axial slice in the upper left, the sagittal in the upper right, and the coronal slice in the lower right. You can change the order of the display using the 'Display Options' dialog.

Select Options | Display Options... using the menu bar. The display options dialog will appear.

Choose the 'Layout' tab.

Experiment with selecting different display configurations.

Press 'Apply' to put the selected configuration into effect.

Press 'Close' to close the dialog.

Step 7. [Optional] Adjusting Display Apperance.

You can adjust the appearance of certain elements of the SNAP User Interface. This is useful if you are preparing a manuscript or a presentation and want to make sure that elements such as crosshairs are clearly visible.

Select Options | Display Options... using the menu bar. The display options dialog will appear.

Choose the 'Appearance' tab.

Choose one of the available user inteface elements and adjust its color, line thickness, etc.

Press 'Apply' to make changes.

Press 'Reset' to restore default settings.

Press 'Close' to close the dialog.

itksnap/ProgramData/HTMLHelp/TutorialSectionVolumes.html0000644000076500000240000001073210534177574022733 0ustar paulystaff SNAP Tutorial. Section 8.

Section 8. Computing Volumes and Statistics

This brief section describes how to use SNAP to calculate volumes of the segmented structures, as well as the statistics of the image intensity for each structure. This section will take under 5 minutes to complete.

Step 1. Load an image and an existing segmentation

We will begin by loading the image that we have been working with all along. You may already have this image loaded, in which case, reloading the image will clear the results of your previous segmentations.

Load image MRIcrop-orig.gipl as described in Section 2

The completed segmentation for this image is located in the same directory as the image itself and is called MRIcrop-seg.gipl

Select File | Load Data | Segmentation Data to bring up the image input wizard.
Use the wizard to load the image MRIcrop-seg.gipl
Press the Update Mesh button in the 3D window panel to render the segmentation in 3D.


Step 2. Compute Volumes and Statistics

In the previous step, you have loaded a segmentation that includes several structures. Now, you will create a file that contains the following information about each structure:

  • Number of voxels that belong to the structure
  • Volume of the structure (in cubic millimeters)
  • Mean image intensity inside the structure
  • Standard deviation of the image intensity in the structure
Select File | Save Data | Volumes & Statistics .

Specify a filename with a .txt extension and press Ok.

The volumes and statistics will be saved in a text file that you specify. The contents of this file are displayed below. The file can be imported into various spreadsheet applications.

##########################################################
# SNAP Voxel Count File
# File format:
# LABEL: ID / NUMBER / VOLUME / MEAN / SD
# Fields:
#    LABEL         Label description
#    ID            The numerical id of the label
#    NUMBER        Number of voxels that have that label
#    VOLUME        Volume of those voxels in cubic mm
#    MEAN          Mean intensity of those voxels
#    SD            Standard deviation of those voxels
##########################################################
vent-lat                                :    1 /      18138 /      18138 /    22.0791 /    6.70728
vent-3rd                                :    2 /       2633 /       2633 /     25.763 /    6.36361
vent-4th                                :    3 /       4775 /       4775 /    25.4262 /    6.60467
hippo-R                                 :    4 /       2250 /       2250 /    55.6178 /    4.47806
hippo-L                                 :    5 /       2548 /       2548 /    52.6429 /    4.15063
vent-temp                               :    6 /       1047 /       1047 /    23.5244 /    7.34334
caudates                                :    7 /       7661 /       7661 /    56.3759 /    3.92178
corpus-callosum                         :    8 /      16841 /      16841 /    69.2177 /    6.05497
itksnap/ProgramData/Images2D/0000755000076500000240000000000011560342171015340 5ustar paulystaffitksnap/ProgramData/Images2D/CMakeLists.txt0000644000076500000240000000027210727111331020075 0ustar paulystaffINSTALL_FILES(${SNAP_DATA_INSTALL_DIR}/Images2D "\\.(png)$") INSTALL_FILES(${SNAP_DATA_INSTALL_DIR}/Images2D "\\.(hdr)$") INSTALL_FILES(${SNAP_DATA_INSTALL_DIR}/Images2D "\\.(img.gz)$") itksnap/ProgramData/Images2D/CVS/0000755000076500000240000000000011560342171015773 5ustar paulystaffitksnap/ProgramData/Images2D/CVS/Entries0000644000076500000240000000062311560342171017330 0ustar paulystaff/CMakeLists.txt/1.2/Mon Dec 10 01:14:01 2007// /EdgeForcesExample.hdr/1.1/Sat Dec 2 04:22:20 2006/-kb/ /EdgeForcesExample.img.gz/1.1/Sat Dec 2 04:22:20 2006/-kb/ /EdgeForcesExample.png/1.1/Sat Dec 2 04:22:20 2006/-kb/ /RegionForcesExample.hdr/1.1/Sat Dec 2 04:22:20 2006/-kb/ /RegionForcesExample.img.gz/1.1/Sat Dec 2 04:22:20 2006/-kb/ /RegionForcesExample.png/1.1/Sat Dec 2 04:22:20 2006/-kb/ D itksnap/ProgramData/Images2D/CVS/Repository0000644000076500000240000000003511560342171020073 0ustar paulystaffitksnap/ProgramData/Images2D itksnap/ProgramData/Images2D/CVS/Root0000644000076500000240000000010011560342171016630 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/ProgramData/Images2D/EdgeForcesExample.hdr0000644000076500000240000000053410534177574021400 0ustar paulystaff\FLOATN@rHJ ???????itksnap/ProgramData/Images2D/EdgeForcesExample.img.gz0000644000076500000240000004537210534177574022027 0ustar paulystaff wXUDTTTVD % =N{>=Ή(oL(B(;d\u|~}o2\l>QP|6?a$ɀW*ûtg?IX#T^YqXq!ZC~Uy|p"ޤvQ]Glz秢awuxspS"99 :7! Eΐ  рTBf@G3UB;Y:Q*ҀtG1%$ZCZh> 0 rpfp8 4î74 Da <,}h >=:L\5"lcKmz &=:`QSR^qO-v/^gs2ٙd!# +301=8MBo^BTS}'%r3s%bd:Z#P8Tr]krNW { 11W;4eLYOʥN ;ܔƺ ־*'P ۘqNxhZG5}q@$w)gEÒl*fǝd5Ѷ5 qVά8?3B1t9Bm^Ho !Rw/4LںкFO2rcn1] =xd$%+ɁRۆie{]7* t$ )8u[m0^cx}l$r'|cnNdswg6f@I9GH`Dehؓb-:ESu"7ycHve,=%nw٢}"H pbg5hW0"v-{,gJ VB6J"3cLc`sBH p(4VX(Ggb"V>Y0~l5nYv.m,I2n ,MJf3aa/Q"(:T Suf“DL4S4qB:>iYIqͿܱ=r¦OSWbJ'۠ +5) 6팳fR͏gI(Vn FYYuꓲ vA(LJYH'FJ1%aFJ fUZpiT l>N2T`Y4DQحa4[b__8yǘ(Xx%|Ȓ5UfcxPX,,*w<oN"_~ar74 RQӔeKEƳ㔥!&D'yqXe2|!?h֕h z>d)̿̆X[uFϯGzA}m^: p̊~_O~&pq>F⥧;"742Ho2c~Y*|â%[{i-1kȱ'^0c㌶y0^V?a,[O(;Qy4GB;!)SFZJ ^1tFi!Bs3Ǝ^!Vb=Uow%3A"ճmCa Xg1_ Q-18_!R.E0*Dhr W`]85]O{~-IU\Ib8ʞĢASZ6Ȳ}sNl %Km}_b^*B6xp? оHAQ"n?w$|;936g^SVz)雙A8a*mplc6Vf`yw<.>j$?8pyt@5a'(OC_:U$;)ɼd&}ce)MIwl`fDAM(9R'RJ/::~bD -_+}F _xܞCǥײ2,40ڼ|-uN]0lp=cTgA%3ZL5+9j hD XPV?ȪYt$F@z"՝1xd탖otU,I?+=$.c$?I7-­Ґ{cȧH(=͟ GllFS,; X4jƩWPl{~x3׃UdI1c-.1C.dÿ>?d=},35$P GxB#CTew P%h˅xYb"lwc΢EH`+?Upd {0jk>:cm+sU᝘M]ҁ{{_F5IiTHYbkq.΁i8S6u+5E7-F3 /{`Iմ<.nQd-,HDŽ$ql!q gL~B<9+M%Q sc53XoB_H f@n_Tb2HMDŰW4F;|)keH#y"ZC8 'LJac831w?F@{(lh mp@kRmMy՗XP7}ݷ❒rsGXK])18C8M\ gKY|V_89<>?6Cѩ|H"7*zxiQ, 7 Ĭ!σǬd+1tok8~fCަ}Te#ٜtIUFXj_ Y@ osr0`Y<5 U#fy|lQ@/T|o䋇>nYG|$fag-=~(fN֭o7xE%8SW_ r.#coj ɯfɥ;-hulK l^~AkٰF0Y>k`Ҷq?s3J f?U+FJv:Ta&qt#%) o60a$Gu?؂$xI0~SB#F20!s70W?`w(qCfcH ^[iEwU荒@!6Bݷw/jRطt/P.~Ố僦fJysvNkn)=xbB5sihʻ꧞س sB^rai8E[ #l0XΗL%]I߉\&ZQ Ц Mu{L5~{cEiHwȬZ(& dOz T1(O+DBhŸx\]+GC+"D.8}6 ǩK&mEeyd/dz(}.Y@f5󿷑K{4{s˜oc}j"^@Ya΢xTsGTlQ%*ZWn^dS6wB0=?k* 9 \̟_/IhpH\ C_,ïDl?ě8`[@G=<j38EE'@.pp>e:cmTGbB_"R6R!u.1d',f'D`Fhe4Y)pqn>o$K|'b9:pm=?'lCd\Nkј g`}þ:t64KuKiU4M\AQC,"Ctx 9néإaD^ڈX n_9sefn2'kp|9Lf1UyլodzzߴEpY+13? cQݣ"T`C}?IIZ ).2(yG&9VN)ixx&NFV;W~QSӄx1.Bc_:pʉ[|mUY8h`}H܋$lAczIXM!#m!t&HCq7-Ih p2@k$TK"]_ƌ*$SS8#f{sԑ"Ǜى8#6LƑיPxK&ԒG٬X)H3!'W)Lގ47%zpzO!n_OE?$DR1T0hu%{RcBF)"[KYPe">84HNjdNC{Pz&ApY.r)Hb\j,T_>zqH5`Ң݃vj5̱P& cSi-_(C(`Tl4.׵ DzU˩}Bo4IEz91"ۓ dzڮd8x!BdCS Bbl8pU^@y/hFVGP$raHQ?r~HOex}V6R`g6}-}>9 M=f;Z`V`!wQٛر :aJ.nt@勋'^FfRDB^Be}h!OSsWٛws)]z_pn8>iPKdKurc*t4OπNkt%";n˗W20{U9jب;m ciE}?,$][BG{j' IG3t… 4&/g S8p{AXq_?/uEKTaaKc Ĥgc_P5}& Zxc!gChiی'h+7o@.b ,W}ve$ r?v| űR_:5Qa(<;fגs@❕TJuύs0-g_di7A I8O'EEKpΌfd\YJhsbq]K>GȆ#_ .'Fpd:oB]z1bm 16kae;SƦ0.lFs(p$1cot[ ݝL?bN.pEewhJ]I>TLqb8邁Q -SsLWxP\4Y.k㽬\R۴~LIK"p% cDm[.*ռKO?i]T-Q8F j利0*^lcDY~< ;[#&?:'zQч?}lZ%b 햃:5">O5B(Mm 0==6{_.k9PaAq԰XLx2d7Qb4KyiF0Zs} 9}M#82}Q22 0m*pkx7olP34?\=M0Ҳ8 zp}I`*n|B<*!|Fxln䑥X9s:!SWKצ#P!M21}^i~ۼhV(x-d46f!T&Ng3gٯ S8:v0㉅p?-K!R0!<wg4"ZUi)3ͱFɪ(_&qxr<=[ 1uM,tdER;{Nh9[6S/Բ;iF$vbo]*#Q}xy?Ƙv>\+wзwR`2:sߧS]ɣưڿlj:VFip[k3* |Q8 &C_W%#n~ZWbH,K.</N ~2~FRw,(t\;oO^IW(u ޮfl&6ơ| Pd_ ~d 6FoUtnrJR9oIat7-[4֓4:B4 Z[!0#8~#D27Ym*)3G\ʯD/yxg[x_>ʆc{q{jlA:r;;3ʂCwqJQ^qݖi)7+-FMVRutW$ȸ&G^$xL+eѼYʙ`kR4>14T ㈽x^_%kŭT}e(r}غv5F+t%?J_ebHQ,#KY_ȖcԤ6[crpσKu%܏"5lnb?#0~&Zj,Bܬ /Dttf!LAw\qMǜ8l,43f[eiznz RNN"1KbH<=LL- Fwp۠IH6$w')[yq ,=O/oq3ofwE}ňÆ%%(D#j l9 }Ã+[-i8GѪB4ǃɪ} ^3i'w(F@ ({ O.Jmw ]Ғ}T$Aѽ 1$4ZHH%phŠx#9+&o@<('/,CiOQe(E .#{Ѥi#p3{$1"m{EzhBMUM_G߹Uw Θ\v;r\!J0'59uyCԼ-"EO561yx,gSR&Lsk:bb|_ܠ:}_<5Sh\]:.7 RlHƖ8>BB8`Ӌ$Hgc`dхYhJBva#qa, #7?RCwSsl.)lG-.YX15ZB3tM[Xm?yrz=gC˳1U<%C&6עDXsX-{(*dkȉČpdWYisOeQI&p'WGM2~5.Ʒ#U|"ѾȰyO%fԩF)C f\b`=6 ϟ~"4yҹl+i]̶^"e$D$E<6\FRvErLl [8\Wb|*aYtzu\Op=#(ヱl]*އH*~& #Tsa /c!y Ј\D4i5/.sč|S${8ingfX_Ss"d~=I)]d-eBK0/;>k^CJk<~lL{#\hpƥ$/9nb7 6K9 !RZH>|{6r7lB3~,~~9fkkk#iul!Eg4GLWeKL;膊Tol3x=ɇKq{e!-iX~e|9`#o4&V5}T{Ol-Q6*n6iV6Q>wO+KS?q*h/e>IX'0 Ŕd|0_)S^‚V;,(v95#[203?W4e`>kZ{J?/Tc A[]2`O n%xuH'.fcQI +h $<'쯏^4t qaOq-qf@W*,y3q;ţIPl9D(:ií\'k*n՟;#GϦ?,oFRZsɂ.))9@߃Z27=Bw#ƅxOb-„9OD}*։azW1}lME&b_cduINnIG<h C#ź!g<6`Ě0:0u: T4>Oz xLy>HϺ'\zapJ {u{ dat|@>Y؏IcX)0#2'oql;h'>wmd۶YZ)(o'Nb4Xoa(㍀Exk9"\3 B0/F(%s1G PV-ƽx1.ҭ6~utIr,wSg*G{h|$cx">7388}¥x۽غ5|I tĒ ĝŭ9*~.z8iLMJڝ==_C4SO:iZSѼõ`Usgrs+QZu ׹Tv? <Hو)q_b5 gAr'sae Zv[vӯ,$x9_w;)8M֨jSUؗlŠô'vesp(2cgNt]-Vca}VO翳k/ޜIܦ$f(I6,bl o#ս[CHMC&CJU!pTṫGF|V1j~@#q=cz;1_OCmEtg1E<R}umpqܭ~Nr=ox:¦o+3. ;>w8>Scˏѓtv*a3 j C0 B\TQX~|~_?f,=Y '156R6kj&!0 ~ĝB+x:^lV҆ 'd m:yTBd@6+ vaʒh3dSE.ڟ ϑjɁ4$={o󅳲"_)o1n _&.&b|x4dg)i4[ˆ5\a=tY"%fV`$O4#K6l a:i2ze l/3W8ovectBOMS.xcdL72?YԻT /%852;G<e681v#wE"1; pfx* TC_KzߗDS(&}|)4Kшq,õ" aB|03'ry t'tYҽ&G3c3jq(-EK*{W)P5le!*:G..4 u#ᚤA*ZӇWWcO*뗎 ]0cȲ21\*"ݣC:?Sȣ=OJE1p<{ "O8{kkzJBuZ359_h8Wohp tɖ`GSݯ31!ƀn+uss?v_eg7m\MĶәXac8\e$٫?ٓ讙z벭%oϯ`GY'lo_^wc87:N8a>^e}FުRo(#Q'/p$WaH"\TœB"1bSUcQb5hn=iX:,!JSȞrWdT-ᡨ/c1~A(]o&~.w*Üк%&Ŀ̾K1s:q͜,\^I}%/̐s*U⛋S4#gmcoC±b]BI~v _3W?t/h!8+obA8Bڣ+Rb.@k LM輦]g#Q>}v^KkHw^^S2I1;`T$`8jCq) M Xꏼ4wOٝdœ} 9qEg?6j??Yny&L^_{0w;3`;C?\dV*712X|oS:z #JE]x-f/,f]ryw,iuF'$gPdnM@()' )~Y>,6jZq0w8B* l (ę)?NX;OЊT(gRXk]z9R_R,jIgb~N̫ >FCqm$MePA=0ײlΔiLb8~1tIشO' vVoXX-{Ԟ 3ܒ忉Hng` >].!SDА$| ( 24usdfJM_p T7~0l``]> Ð-H$d0XxLE=.Ap,؀iFBVD"tW&bn >v^溥lwO{o qzO!ݲ& \ط*/$Tű:s5c(ϯyKX/ qn ( Zɧ)K7{,Ie gʸP#ѽU}pl8 ;~S~?5wSKmOﱸbog!iD.~.3(B r>Xog+^ b1΂ Sp6-b^{Am*ϧ]}_PqMrIٝl ͋Ҟڝ̌T6Q\W)L!e%Yٕ[~<z*U#çGu Gx3!~N07azi3 PMݫ=Ɩ+ Ӝ&K4͠έlL _@~"k+LSudG@T9Qr8z, ׸RY4$o=u?,:m̼kG2.>%E8n amCq}?N0B{d%!7b4&C, !' Y)\x"x е^ͣA&;ޚ|Ǡm{K02\oCa>: }~xn=Eyr9̶EUe3#usT;[l2j7 {S/@~?Lݺu0ަ`?_ұCW%h@̦>:40.:PEkat{bV]f0;C`> |wDñ6[-`w_aTm~%ZB, `רp"S@} ^epL%4 ПX Fg;7׀w )`GV Ӓ2J]Df.YeCmY=-xcðv(g1n{%6︜~n| )±ҍ!\Nmxr~oːI h ū(].mT`cSM!/ku)j>|, o! wD'Aq*f-֤O.ZF|ᓞox(vc1rP ;=&&sXG=t{n. otá|V͟(go+yOP mJneh|JkQ٨B6nu%)GGu!sW>cVhxkhgT7e6 g g' \"?)sCѽQu3VU Ѕ}A^N TdV`S#z]ph4IHch7f9\$*rxo[{zPYjxq8wb+RY%?m iq}1#/c&5?:Y4}I>%布.f˼KXIȑj2n`0j^lNS儨FA*J$møaw@fg XD[HIM NT`GHԍiig*F@]$ff|t0~np3|~81evΘvZK(ѿ3i)p.X(vN k5ymj±e'-v<.qZAmlbrSGkM F^7Q;/صEīn V-а )Cfz*oXmj=y[=Tf(74%hܵ^J;9Tʲ yY3՞DŽCr8?U%-F1T"Q(JüEYA ܣ(xLƣgwn&0-%ּ_4 Li%}J_;E:=2Ú*ؿ< c<7֣TmnG5u;{xo/&)2~m 9=ɛ4r#\GZlS0PUdu+ZV؛,4/4Vq;ex%cg MP!DqTV`o|3s<Ѯ񱸽: $}]'C\侑\O~f-`v ~H~E{3!N^:VCqI{rCnݿdO#m,)hnST|рOn?#ї}ϘwON;6S0$JF\%GFýQgKP]iёYׂTk[%\0uw?BHRtSӑ5*%`p׫ ;cB`.ߥ߅A\s<>TF:bPlpex-͘Fr3f2LsT)5uMB73ss5olG3o8,N8 Ylpёє>Rx~o1fr0Oj%ױ p1$s+ uwN0GwR.=d$alIna~h0Oa`\U1nؓ!ƖRP1h܅8?OaB S 8 3zByC{ldׅ 2-]iy eXO+3U=#x6{Y~Qp(KP4t~B dCƄ_tkzseKNgV &*/ i@6LnG|V'4OK䙈EWu^I?"h@s 0U>u%hhL*CL:jGmN'B=A0t CZT.@"[Zcz:&@McQ$>Iߌ(7Ear@~3S dJJП/:8ꏸnhz"|.$~'%)]tE:\mj(ǹ|!R)k.'(P8+,4J)Acq~T\9Y12B˻$8xX Y)(w䑎p_R?٭h;w)0s㦂¯+%:s>1~ȼ XĜHAnn< F#w7?l Y;;;g'[wC̆[]*DȲCŸxpdi*V~FЋcI74RUjo ;rWq]KHy]toZ;t}u45,pcc+%Pi /=l$3Hxŷ^PaGK IkQ"u@9o(2k=K)ӫ8[Pθ|h@>)s$Үѡe9ӇNXGGggˣy*;[Ho9T 4 $-Ɯˡ yL<=#pG4⧬ L?zI>MOoZ>[/q:23i1X;|Bcd]VRD=uZw؞J ^VM"{i+` AHp NUtX@QCR>#}_$~I7Zl,Q YKy&VExG$ 1hT*`^xz9]RYxᨌ-dDHJǤ i):Ӝ$"U^3T#7>zA޷T`wymNZVԼ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/ProgramData/Images2D/RegionForcesExample.hdr0000644000076500000240000000053410534177574021757 0ustar paulystaff\FLOATN@rHJ ???????itksnap/ProgramData/Images2D/RegionForcesExample.img.gz0000644000076500000240000000771510534177574022405 0ustar paulystaff ˋmGś`'!3%d`=:A4 \j0jbQANt`wO!"N<_u:}:6|]UONNNncw.з?_­ {/9V9{PMDZ=ˏ۴ߟ[vO y_{x-3'mymqI+l9q l {h1#+gK*lg}M_%<e#q޸vGdHM1'>`{%XC}Ta3zn#wǎ_(q'>?s9_ͧsulM c1rX;]tLؤΪ-o7$>nG#[6=Fx&|Cq:HRd;$؀l>+_ކ y%\vpv#Rn_On _9OJy><;>#M}'69qAxp|/Oj/ۂso3?s5>Jg4ns(qB?o|\iB[cx0Bכ-J1>ԧ=18#c#gNMg 1}88>'G[:ٞǖ7ɵϏ{l3xoq} [Ro*I*w~w3޷ZbR㱴-P?M<@oOnHq .~[TczI{3§z_Gkx[١-|tȁ"yǙ9'6[z_Sfqr{s,tJzJ7Hת|-Y3{vF-}ޓx$pN#'Z@i+bQ\Q~U|T!k>|Jc"'gkd)ުlc4ajcT驿 y@აGWovE;qDݱ3#=QL9 !#EW}oyn6`A+;V~poGgqc(lz~]d^wh'08AUN(?ˆVt<=Toe^Qv,NEM[;6Kr(0;TgU a[L3|iƟPGg>@4^~/i@B?4_Te pLjqRY b >疼Nj)wdgؤ^Uk tKv!#ܛ*MB]O[3|_6~jD#Nq3Fq}#~Wq ng^:%U?P;|xNCC؈;OG{U(Piu ٬*|T9љƞ쏭y=ywmns!qjmМq&V򉉏qlѭ ~Y!|*{k&$ʟnŸ}yL Lj:?C_<iԯ Y~9x8>~Q{uHa>ԃ*0=}V)Q I)>۾n3O5 ̰#l|>:-ຕyh|>Nqa=Ӫ Gve|}FN%>'mK|TX**Ƙ|΁I=kerǹ)>lw%FnǔS?+r>K1:d9 'uǧ'FxMw-]ǟSx֡ xkqa tëGЏwF]bʎ9Wg|;:9NՈ5^|ğ*N|=P{ - ch Wś_0>Ho=]t'CR:q>>-Xn%b^G973n8:ůwan=f"=&>|Ukr #H[lH\s/lCp(.!>Ut}|+~N|dU!'ņyaK#,wtXV7fL[@Zz'suFm{ڜ0uy mnbFqpzVGtk/k1=~I Ynni[|ADƳK> l A_Kp-&_:߿'iUٛJE F.:)>>y C(NP5+w׆Qە&|'Q}E#|;'|F׬. >O|OA7 3GRsa3ܽ0F+5}}Ic>_Q-ژGx8 ~VirH ۛ1>kMD穧U-z^&"F02=EU\T:`ON9fWH1r|ئO#P 56>3ZWh}G~z>pI9$>ӫ/*\4.4FjIsՏpyBTӖ >nOpΈsi=p57~z=ߤ1wƔ$(V:B!zx.JU7:hh}V;sGs4`rx|O=?1:~-Hx|=噼zʺkNl6Ѧ yCx+E}`@݀_}Azz>zoy3=?;yWƈ㱯YѼuڿl2->im%<ϣ\])?Ԯ0Ϲv}ˇm_Pl)4_#R6.=ZNf@k2wPxVԳɳjˌG=; Gcw+2.~rr~]g-RQmcSW9xbU>Lg+\#ͭwOu6~i>wN:X[h48r̭iq!6(_S*6YI#o35ssԿB7lzLq'xy>7|U~Ws#_'Oc,jGgZ#>Z/* jlˣXhVɺOhUU#}ZH*_vO-[\}5µe825/ʲ>9߆>%6>ld&N3N3#J_a,16V[>u~D@Sitksnap/ProgramData/Images2D/RegionForcesExample.png0000644000076500000240000000212710534177574021766 0ustar paulystaffPNG  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/ProgramData/Presets/0000755000076500000240000000000011560342171015372 5ustar paulystaffitksnap/ProgramData/Presets/CMakeLists.txt0000644000076500000240000000007410727111331020127 0ustar paulystaffINSTALL_FILES(${SNAP_DATA_INSTALL_DIR}/Presets "\\.(txt)$") itksnap/ProgramData/Presets/CVS/0000755000076500000240000000000011560342171016025 5ustar paulystaffitksnap/ProgramData/Presets/CVS/Entries0000644000076500000240000000016011560342171017356 0ustar paulystaff/CMakeLists.txt/1.2/Mon Dec 10 01:14:01 2007// /SnakeParameterPreviewCurve.txt/1.1/Sat Dec 2 04:22:20 2006// D itksnap/ProgramData/Presets/CVS/Repository0000644000076500000240000000003411560342171020124 0ustar paulystaffitksnap/ProgramData/Presets itksnap/ProgramData/Presets/CVS/Root0000644000076500000240000000010011560342171016662 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/ProgramData/Presets/SnakeParameterPreviewCurve.txt0000644000076500000240000000062510534177574023424 0ustar paulystaffPoints.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/ProgramData/SNAPProgramDataDirectory.txt0000644000076500000240000000020110534177567021266 0ustar paulystaffThis file is needed by SNAP to find data that it needs to run. Don't remove this file or modify the contents of this directory! itksnap/README.html0000644000076500000240000000611411457606212013375 0ustar paulystaff ITK-SNAP Readme

ITK-SNAP README (Versions 2.1.4 and Later)

Welcome!

Thank you for your interest in ITK-SNAP! By using this program you are joining an ever growing community of SNAP users from research labs around the world. ITK-SNAP is an open-source software application made possible by the committment of dedicated developers, who often make contributions on a volunteer basis. Original development of ITK-SNAP was supported by the U.S. National Library of Medicine through PO 467-MZ-202446-1. Subsequent development of ITK-SNAP version 2.0 has been supported by the U.S. National Institute of Biomedical Imaging and BioEngineering and the NIH Blueprint for Neuroscience through grant 1 R03 EB008200-01.

ITK-SNAP is the result of over 10 years of work by many researchers, software engineers and students. Please visit the Credits page to learn about the people that made ITK-SNAP possible!

Get Involved!

Your participation as a user, developer or sponsor is essential to ensuring a bright future for ITK-SNAP future. There are many ways in which you can contribute to the stability and quality of this program:

Getting Started

Asking Questions

If you have a question about ITK-SNAP, here are the places where you may find an answer. Keep in mind that since ITK-SNAP in not a commercial product, it may take a little while for an answer to arrive!

  • If you have an question about how to use SNAP or why it's not doing what it should, post in on the ITK-SNAP Users' List.
  • If you have a technical question or a questing related to ITK, join the ITK Users' Mailing List and post your question there.
itksnap/ReleaseNotes.txt0000644000076500000240000006511611560341565014715 0ustar paulystaff====================================== InsightSNAP Release Notes Version 2.2.0 ====================================== ----------------- 1. New Features ----------------- 1.1. New in Version 2.2.0 ---------------------------------------------- This is largely a maintenance release, with a few usability enhancements based on user feedback. The main change programmatically is 64 bit support on Linux, MacOS and Windows. 1.1.1. New Features and UI Improvements - 64 bit versions of the software are available for Linux, Windows and Mac. These versions are now built nightly and will be distributed on SourceForge.net. For this to work, we had to change to newer versions of the supporting libraries: ITK 3.20, VTK 5.6.1, and FLTK 1.3.0.rc3. The latter was necessary for 64 bit MacOS, which many users have requested. Thanks to Michael Hanke for providing a patch for ITK 3.18 compatibility. - The maximum number of labels has been increased to 65535 to support interoperability with tools like FreeSurfer, which generate segmentations with large numbers of labels. - A new window for displaying volumes and statistics. Previously, users had to export volumes to a text file in order to view them. Now they can be viewed dynamically. This was possible by moving to FLTK 1.3, which includes the Fl_Table widget. - A new tab on the layer inspector displaying image metadata, particularly useful for DICOM files. - Several changes to the polygon drawing interface. The buttons at the bottom of the slice window are now shown dynamically, based on what the user is doing. Right clicking brings up a popup menu, allowing to bypass the edit mode if desired. An 'undo point' operation is provided. - Users can change the appearance of the polygon drawing UI elements. This addresses the request to get rid of the dotted line closing the polygon. - Intensity window and level in the image contrast dialog are no longer clamped by the minimum and maximum intensity in the image. This is useful for displaying statistical maps, where a certain fixed output range is desired. - Finally implemented all the options under Segmentation->Export as Mesh. You can now export meshes for all labels either as separate mesh files or as a single scene. The latter is recommended with the VTK mesh format, where the label ids of the meshes are preserved. - Collapsable slice windows. The new 'collapse' button gets rid of the UI and just shows the selected slice. This is useful when you have multiple SNAP sessions open at once. SNAP can be opened in this mode using the new command-line option '--compact ', where is 'a' for axial, 's' for sagittal or 'c' for coronal. You can restore default SNAP layout using Ctrl-F3 (Command-F3 on the Mac) or using the toolbar button that pops up. - Also added command line options --zoom and --help - The 'reset view' button under the slice windows has been renamed 'zoom to fit' and it behaves more sensibly when zoom is linked across the slice views. - Improved integration with MacOS and Windows operating systems. On both MacOS and Windows, you can drag and drop a file into an open SNAP window and you will be prompted to open that file as a grey image, as a segmentation, as an overlay, or as a grey image in another SNAP session. Additionally, on the Mac, you can drag and drop files to the SNAP icon on the Dock, even if an SNAP session is not running. - Added an option under File menu to open a new SNAP session. - Ability to save segmentation mesh in active contour mode 1.1.2. Bug Fixes and Stability Improvements - Fixed a problem with certain operations being very slow because of the way the progress bars were displayed. Preprocessing, mesh rendering and mesh IO will now be much faster - Fixed problems with the snake parameter dialog. The images are now properly displayed and animation works. - Fixed problems with automatic panning in crosshairs mode. Also added a button to enable this feature; it is disabled by default. - Changed defaults for edge-based snakes to have non-zero weight - Fixed display issues on newer MacBook Pro - Fixed problem with bubbles not being spherical for certain image orientations 1.2. New in Version 2.0 ---------------------------------------------- 1.2.1. New Features and UI Improvements - Support for multiple image layers. Users can now load gray and RGB images as overlays on top of the main image layer. For example, one can display a statistical map as an overlay over an anatomical image. As of version 1.9.8, overlays must have the same dimensions as the main image. - A new layer inspector window. Each layer in SNAP (main image and each of the overlays) can be examined using the layer inspector. Currently there are three tabs: one for setting the intensity mapping of the layer (i.e., mapping from image intensity to display intensity); one for selecting and editing the color map and transparency of the layer; and one providing information about the layer. The layer inspector replaces the old "Image Information" and "Intensity Curve" windows. The color bar editor is only partially functional as of 1.9.9. - Hiding the UI. Using the 'F3' key, users can toggle certain user interface elements on and off. Press 'F3' once, and the left sidebar and the menu bar disappear. Press 'F3' twice, and all the UI elements disappear, so you are looking just at the image. Press 'F3' again, and the UI is restored to the original state. This feature works well with the '+' buttons on the slice windows. It's intended for multi-session SNAP users, so that the screen real estate can be used more efficiently by multiple SNAP sessions. - Because now the most common SNAP commands have a shortcut, you will be able to do a lot with the UI hidden. Select 'Help->Keyboard Shortcuts' to see a listing. - Fullscreen mode. Press 'F4' to toggle fullscreen SNAP. Use it with 'F3' to let the image occupy the whole screen. - An expanded menu bar. We have split the menu into File, Segmentation and Overlay menus to provide easier and faster access to the ITK-SNAP features. - Native file chooser. On Windows and MacOS, ITK-SNAP will use a native file chooser instead of the FLTK built-in file chooser. On Mac OSX, the native file chooser can be further enhanced by installing the DTI-TK Quick Look plugin that supports NIfTI/Analyze image preview (www.nitrc.org/projects/dtitk) - When launched from command line, SNAP can automatically determine whether an image is a 3-component RGB image or a grayscale image. To use this functionality, users must run SNAP without "-g" or "-rgb" options: itksnap image.nii This feature is ideal for users who want to associate ITK-SNAP with certain 3D image types in their operating system (in Finder or Windows Explorer). - Automatic check for software update. Users can enable automatic update checking. - External web browser support. Help and other HTML pages are now displayed in the operating system's own web browser, from itksnap.org. This may displease users connected to the internet, but this makes managing documentation a lot easier and hopefully will allow us to keep the documentation up to date with the features. - Crash recovery. When an out-of-memory or other crash occurs, ITK-SNAP will ask you if you want to save the segmentation image before exiting. Of course this may not always work, but it should make a lot of frustrated users a little less frustrated. - Reduced the memory footprint. There is still room for improvement, of course. Currently, ITK-SNAP requires 6 bytes per voxel in manual segmentation mode. More memory is needed for mesh rendering, and a lot more for automatic segmentation. When loading images in 32-bit or 64-bit formats, more memory may be required at the time of image IO. That is because ITK NIFTI reader (and maybe other readers) keeps a second copy of the image in memory during IO. This memory is immediately deallocated though. - Unified navigation modes. The crosshair mode allows zoom and pan (RMB/MMB), and has an auto-pan feature when you move the crosshair close to the edge of the slice window. The zoom/pan mode is redundant, but we left it in place for backward compatibility. In the zoom/pan mode, zoom is RMB, pan is LMB, crosshair motion is MMB. In all other modes, crosshair motion is accessible through MMB as well. 1.2.2. Bug Fixes - Fixed an issue with SNAP reading certain types of image twice from disc. This should speed up the reading of floating point images, for example. - Color map cache is now computed on the fly. This makes interaction with the intensity curve and color map more real-time. - Found a problem where in Release mode, the active contour would do nothing. Did not know how to fix it correctly, so replaced the parallel sparse field solver with the non-parallel one. This may slow down automatic segmentation on some machines, so this is an outstanding issue. - Found a bug that caused images with unusual coordinate orientations to be incorrectly displayed (wrong coordinate labels assigned). This was caused by incorrect mapping of ITK direction matrix to "RAI" codes in SNAP. This affects display of NIFTI, DICOM and other image files. It also affects the behavior of the Reorient Image dialog. - Please see the bug tracker on itksnap.org for the full listing of bug fixes. 1.2.3. Website Changes - The itksnap.org website has been Wikified. Content can now be edited on the fly. 1.3. New in Version 1.8 ---------------------------------------------- 1.3.1. New Features and UI Improvements - Support for reading floating point images of arbitrary range. SNAP still represents gray images internally as signed short, but now it can load a floating point image and remap its intensity range to signed shorts. When displaying intensity values, it will map back to float. - A new 'Adaptive' paintbrush. Under the paintbrush tool, it can be selected using the 'Shape' drop down. This tool can speed up manual segmentation quite a bit for some users. This brush has the shape of a rectagle. As you click on a pixel in one of the slice views, the brush will fill a region that includes the pixel you clicked and has more or less uniform intensity. For example, in brain MRI, if you click in the ventricles near the caudate, the brush will fill the ventricle but not the caudate. This is not as powerful as running the level set segmentation, but it's very local and great for quickly segmenting structures - or dealing with inhomogeneities. The underlying algorithm is ITK's watershed segmentation. You can control the tolerance of the adaptive brush ('granularity' input, lower values produce smaller, more cohesive regions). The brush can be used in 2D or 3D. This feature was inspired by a similar tool in ITKGrey, a tool from the Vista Lab at Stanford that itself is a branch of an older version of ITK-SNAP. Let us know if this feature works for you. Potentially, we may add other algorithms in the future, including running the level set inside of the brush. - Support for image orientation. This is a major step towards NIFTI compatibility (part of our R03 effort) and something many users should find helpful. Formats such as NIFTI, DICOM, and a couple others encode the orientation of the image axes in patient space, and even allow image axes to not be parallel to the anatomical axes. SNAP now reads this information from the image header and uses it to assign anatomical labels and compute anatomical coordinates. One of the consequences of this change is that the image IO wizard no longer requires specifying an orientation code (e.g., 'RAI') when loading an image, since this information is read in the header. - A new 'reorient image' dialog has been added, so that if the orientation information in the header is wrong, you can change the orientation and save the image. For now, the user can only specify reorientations that are parallel to the anatomical axes. - World cursor coordinates (under image information) are now displayed in NIFTI / MNI coordinates as well as ITK coordinates. The difference is that the NIFTI coordinates incorporate orientation and are in the (L->R, P->A, I->S) coordinate frame. ITK coordinates are (x * spacing + origin), and ignore orientation. - 3D Meshes generated and rendered by SNAP are now represented in NIFTI world coordinates. Previously, the coordinates were computed using the formula x_mesh = x_voxel * spacing + origin In version 1.8 and beyond, the mesh coordinates are computed as x_mesh = nifti_sform_matrix * [x_voxel; 1] This means that the meshes output by earlier versions of SNAP may be translated and rotated relative to the meshes output by version 1.8. This will not affect users who simply view meshes in SNAP; however users who export meshes to other programs will be affected. - Multisession cursor (similar to yoking in MRIcro) now uses these NIFTI coordinates rather than ITK coordinates. This is a key feature because it enables users to work with MRI scans acquired during the same session with different orientations. For example, a coronal T1 scan and an oblique T2 scan can be loaded in two SNAP instances, and the cursor will be correctly linked across the two. CAVEAT: SNAP's cursor always falls on voxel centers. This means that the multisession cursor correspondence is not exact, but rounded to the nearest voxel. If in session A you move your cursor, the cursor in session B will move to the voxel center closest to the physical position referenced by the cursor in session A. - A new multi-session zoom feature. Similar to the multi-session cursor, this allows the zoom level to be maintained across multiple SNAP sessions. Useful if you do a lot of zooming in and out when working with a pair of scans. This is disabled by default and must be enabled in each SNAP session using the checkbox under the 'Zoom/Pan Tool'. - Changes to how zoom works, related to above. Now 'zoom views together' is on by default, meaning that the zoom factor is the same in axial, coronal and sagittal windows. Zoom level is specified in px/mm, where px is the number of screen pixels (in other words, a metric equivalent of dots per inch). Before, zoom was specified in percent, relative to an optimal zoom that would best fit all three windows. With the new way you have more control over the zoom. For example, if your image has 1mm voxels, you can have one to one correspondence between screen pixels and voxels by setting the zoom to 1 px/mm. - Multisession 3D views. When the multisession cursor is selected, the 3D views are also synchronized across sessions. This works even if the images opened in the two SNAP sessions have different dimensions, orientation and spacing. SNAP 3D window now uses NIFTI world coordinates, so as long as the two images overlap in world space, so will the 3D views of the two images. This feature is useful when comparing two segmentations of the same image. - A new ruler display in slice windows. Can be disabled or modified on the display options dialog. - Much better tracking of changes to the segmentation image and better promting to save changes before quitting or loading a new image. The title bar display is also improved and uses an asterisk to indicate unsaved changes. - The command-line options have been updated. You can now load a grey image without using any flags (e.g., itksnap image.nii) and there is a new '-rgb' flag for loading an RGB image from command line. The upshot is that you can now associate SNAP with image file extensions in the operating system and double-click an image file to open it in SNAP. - A new 'Tools' dialog on the label editor. This dialog is intended to provide several tools for merging or modifying labels. The first tool is to combine a pair of labels into one. Previously, this was possible using the 3D scalpel tool, but that was not really an intuitive way to relabel images. - As part of above, a new topological merge tool, developed by Nick Tustison, Brian Avants and Marcelo Siqueira (I hope I did not forget anyone). Given adjacent labels A and B, it will replace most voxels in B with the label A, while preserving the topology of A. This tool is used to preserve topology during manual segmentation. If A has correct topology and you want to add some region to A, label this region with label B, and then grow A into B with topology preservation. This is a work in progress, and feedback would be welcome on this feature. - Documented existing keyboard shortcuts and added some new ones. Available shortcuts can be listed by selecting Help->Shortcuts. 1.3.2. Programmatic/Distribution Changes - SNAP is now built against ITK 3.8, offering several improvements, especially in how image orientation is handled. - IPC communications (technology that allows multisession cursor and zoom) now has some versioning built into it, so if you are running two versions of SNAP, they will not clash. - On LINUX, we now distribute a .tgz archive instead of a script installer. Some people complained about the latter. We can also make .rpm and .deb packages although these won't be posted for public download yet. 1.3.3. Bug Fixes - Level set fix for ITK 3.8 fixes automatic segmentation's weird behavior 1.4. New in Version 1.6.0.1 --------------------------- 1.4.1. Bug Fixes - Major bug in release 1.6.0 involving disabled cursor movement in snake segmentation mode has been resolved. 1.5. New in Version 1.6.0 ------------------------- 1.5.1. New Features and UI Improvements - You can now save a sequence of all axial, coronal or sagittal slices with overlays as PNG files (File->Save->Screenshot Series). - Automatic window and level computation based on the image histogram. The window and level are set to the 1st and 99th percentiles of the intensity histogram, respectively. This is much more robust to hypo and hyper-intensity in medical imaging data. The feature is accessed in the "Options->Image Contrast" menu (or hit Alt-I in the main window). - Cursor synchronization across multiple SNAP sessions (similar to the Yoke feature in MRIcro). The mechanism uses POSIX shared memory. Can be turned off using the 'Synchronize Cursor' checkbox. Currently, only enabled in manual segmentation mode; probably will enable in snake mode in the near future. --- NOTE FOR MacOS Users --- MacOS doesn't allow you double-click the application icon to open a new instance. To open multiple instances of ITK-SNAP, you need to launch it from the command line. ---------------------------- - SNAP will prompt you before closing if there are unsaved changes. - A new 'New->Segmentation Image' menu item will clear the current segmentation. - Support for RGB (color) images in SNAP. This is great for segmenting in DTI data (manually, for the time being). RGB images can be loaded as the base image or as an overlay over the gray. To create these RGB images, use the new DTI-TK developed by Hui (Gary) Zhang, available from http://www.picsl.upenn.edu/resources_dtitk.aspx - Segmentations can be exported as VTK meshes (for example, for loading in ParaView). - Multilevel undo/redo functionality for all segmentation operations (polygon, paintbrush, freehand, 3D segmentation, 3D cutplane). Undo memory is preserved when loading new segmentation images. - Freehand drawing support in polygon mode (hold and drag the mouse button). This feature is especially useful for using SNAP on a tablet. - Added keyboard shortcuts 'a','s','d' for the opacity slider - Shortened/simplified some of the menu items 1.5.2. Bug Fixes - Various bugs have been fixed :) 1.5.3. Distribution Changes - SNAP website fully migrated to sourceforge.net - Mac Universal binaries supporting Intel and PCC, Tiger and Leopard are now available starting with 1.6.0 - Linux binaries will be available starting with 1.6.0 1.6. New in Version 1.4.1 ------------------------- 1.6.1. New Features and UI Improvements - Added paintbrush tool to the main toolbar. Paintbrush can be used to quickly touch up segmentations. Left mouse button paints with selected label, right button acts as an erasor - Went through and added/edited tooltips in the program to be more accurate. It should be easier to make sense of the program now - Added a menu option for saving the level set image during active contour evolution. This is an important feature because it allows users to save segmentations before sub-voxel accuracy is lost. In particular, this can be used in conjunction with ParaView to generate meshes from segmentations. - You can now save and restore the camera settings in the 3D view within a single SNAP session. This can be useful for generating screen shots of different segmentation from the same viewpoint. Press 's' in the 3D window to save the camera state and 'r' to restore it. 1.6.2. Bug Fixes - MAJOR: fixed bug that was causing crashes on Win32 during polygon drawing (thanks to Jeff Tsao for this bug fix!) - Fixed problems with the getsnap.sh linux script - Some menu items were enabled when they should not have been, now are disabled. - Rare bug where speed function very close to 1 was not being rounded correctly and may have caused crashes on some systems - Fixed problem where the screen was blank after loading preprocessed image - Fixed crash when changing bubble radius and then going back to preprocessing mode 1.6.3. Distribution Changes - Interim SNAP releases are now hosted on SourceForge. ITK repository will only be used to host major releases (like 1.6). This allows us to check stuff in independently of the ITK code freezes. It also makes it easier to add new developers. - SNAP CMake files should automatically detect when SNAP is being built outside of ITK's InsightApplications. This means you can build SNAP on it's own and the download size is reduced 1.7. New in Version 1.4 ----------------------- 1.7.1. New Features and User Interface Improvements - New and improved label editor. You can easily switch between labels while in the editor and the interface for adding new labels is more intuitive. You can now delete labels. - New and improved interface for intensity reparameterization. The histogram display is more visible and you have more control over the number of bins in the histogram and the scaling of the bars (linear or log). - SNAP remembers all settings associated with loading an image. This means that any image loaded previously can be reloaded without going throught the wizard. - We've added File->Load Previous menu to let you load images quickly - SNAP can now read DICOM file series (experimental support) and it can read and write VoxBo CUB image files. - SNAP remembers more image-associated settings from session to session. For example, it will remember the intensity reparameterization that you last used. SNAP will also remember the orientation ("RAI" code) that was last used to read each image. - New Image Information window is available under the File menu. It displays the size of the image and the current cursor position. - A color map feature has been added in the automatic segmentation mode. The color map lets you select different color schemes for displaying the probability map / speed image. - Small improvements to the active contour 2D example dialog have been made - A progress monitor has been added for the 3D renderer in main SNAP window. - New buttons allow taking of snapshots in each of the SNAP image windows - The tutorial has been updated to reflect the new features. 1.7.2. Bug Fixes. - SNAP should crash a lot less than before - The Left-Right orientation should be correctly handled by SNAP. You still have to supply the correct orientation ("RAI Code") when loading the image. - The bug with the segmentation being shifted when using "Resample Region" option has been fixed - 3D window handles images with non-zero origin better - Initialization bubbles have been fixed to be floating point - Lots of other small bugs have been fixed! 1.7.3. Programmatic Enhancements - SNAP and IRIS now share the sameset of OpenGL windows. This should prevent crashes on some platforms. 1.7.4. Other - SNAP available as a universal (Intel/PPC) binary for MacOS at itksnap.org 1.8. New in Version 1.2 ----------------------- 1.8.1. User Interface Improvements - The ability to switch between 4-view mode and single view mode. Each of the slice views and the 3D view can be expanded to occupy the entire SNAP window. - A zoom thumbnail is now displayed when a slice view is zoomed in. The thumbnail view can be used to pan the slice. - User can specify whether he/she prefers to start in linked zoom mode or in unlinked zoom mode. - User can change the appearance of various display elements, including the crosshairs, the region of interest selection box, the window background and more. - SNAP automatically determines the image orientation (RAI) when that information is available in the image file - SNAP remembers the last ROI used for each image. 1.8.2. Programmatic Improvements - The level set segmentation pipeline has been rewritten, taking advantage of the stop and go functionality of ITK finite difference filters. This means fewer unexplained crashes and simpler code. - A state-machine has been added to the user interface logic code. This machine automatically activates and deactivates UI widgets based on a set of flags. Rules such as Flag A => Flag B can be added to the state machine. 1.8.3 Bug Fixes - Slice views update correctly when the SNAP window is resized - Accepting a polygon now works for high resolution images. - Fixed a crash on some systems when running edge-based snake segmentation with an advection term. 2. Known Issues ----------------- - 3. Wish List ----------------- - The ability to perform 2D level set segmentation in each slice view. itksnap/Testing/0000755000076500000240000000000011560342171013161 5ustar paulystaffitksnap/Testing/CVS/0000755000076500000240000000000011560342171013614 5ustar paulystaffitksnap/Testing/CVS/Entries0000644000076500000240000000062211560342171015150 0ustar paulystaff/SNAPTestDriver.cxx/1.5/Thu Oct 14 18:09:25 2010// /SNAPTestDriver.h/1.1/Sat Dec 2 04:22:20 2006// /TestBase.h/1.2/Sat Nov 14 16:19:56 2009// /TestCompareLevelSets.cxx/1.5/Sat Nov 14 16:19:56 2009// /TestCompareLevelSets.h/1.1/Sat Dec 2 04:22:20 2006// /TestImageWrapper.h/1.3/Sat Nov 14 16:19:56 2009// /TestMain.cxx/1.1/Sat Dec 2 04:22:20 2006// /TutorialTest.cxx/1.1/Sat Dec 2 04:22:20 2006// D itksnap/Testing/CVS/Repository0000644000076500000240000000002011560342171015706 0ustar paulystaffitksnap/Testing itksnap/Testing/CVS/Root0000644000076500000240000000010011560342171014451 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Testing/SNAPTestDriver.cxx0000644000076500000240000001723211455643525016501 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: SNAPTestDriver.cxx,v $ Language: C++ Date: $Date: 2010/10/14 18:09:25 $ Version: $Revision: 1.5 $ 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 "SNAPTestDriver.h" #include "TestImageWrapper.h" #include "GreyImageWrapper.h" #include "LabelImageWrapper.h" #include "SpeedImageWrapper.h" #include "CommandLineArgumentParser.h" #include #include using namespace std; const unsigned int SNAPTestDriver::NUMBER_OF_TESTS = 4; const char *SNAPTestDriver::m_TestNames[] = { "ImageWrapper", "IRISImageData","SNAPImageData","Preprocessing" }; const bool SNAPTestDriver::m_TestTemplated[] = { true, false, false, false }; void SNAPTestDriver ::PrintUsage() { std::cerr << "SNAP Test Driver Usage:" << std::endl; std::cerr << " SNAPTest list" << std::endl; std::cerr << "or " << std::endl; std::cerr << " SNAPTest help NAME " << std::endl; std::cerr << "or " << std::endl; std::cerr << " SNAPTest test NAME [type TYPE] [options]" << std::endl; std::cerr << "Commands :" << std::endl; std::cerr << " list List all known tests" << std::endl; std::cerr << " help NAME Provide help on test 'NAME'" << std::endl; std::cerr << " test NAME Run test 'NAME'" << std::endl; std::cerr << " type TYPE Specify template parameter of test" << std::endl; std::cerr << " (e.g., unsigned_short)" << std::endl; } template SNAPTestDriver::TemplatedTestCreator ::TemplatedTestCreator(const char *name) { string strName = name; if(strName == "ImageWrapper") { if(typeid(TPixel) == typeid(GreyType)) m_Test = new TestImageWrapper(); else if(typeid(TPixel) == typeid(LabelType)) m_Test = new TestImageWrapper(); else if(typeid(TPixel) == typeid(float)) m_Test = new TestImageWrapper(); else m_Test = NULL; } else m_Test = NULL; } template TestBase * SNAPTestDriver::TemplatedTestCreator ::GetTest() { return m_Test; } TestBase * SNAPTestDriver ::CreateNonTemplateTest(const char *name) { string strName = name; TestBase *test = NULL; return test; } TestBase * SNAPTestDriver ::CreateTestInstance(const char *name) { TestBase *test = CreateNonTemplateTest(name); if(!test) test = TemplatedTestCreator(name).GetTest(); return test; } void SNAPTestDriver ::Run(int argc, char *argv[]) { // Configure the command line CommandLineArgumentParser clap; clap.AddOption("help",1); clap.AddOption("list",0); clap.AddOption("test",1); clap.AddOption("type",1); // Parse the command line CommandLineArgumentParseResult parms; if(!clap.TryParseCommandLine(argc,argv,parms,false)) { PrintUsage(); return; } // Check if the user wants help if(parms.IsOptionPresent("help")) { // Get the file name const char *name = parms.GetOptionParameter("help"); // Create a test instance, ingoring type TestBase *test = CreateTestInstance(name); // Print test usage if(test) { std::cout << "SNAPTests " << name << " options" << std::endl; std::cout << "Options: " << std::endl; test->PrintUsage(); } else std::cerr << "Unknown test name: " << name << std::endl; } else if(parms.IsOptionPresent("list")) { // Print out a header std::cout << std::setw(20) << std::ios::left << "Test Name"; std::cout << std::setw(12) << std::ios::left << "Templated"; std::cout << "Description" << std::endl; // Go through the list of known tests for(unsigned int i=0;iGetDescription() << std::endl; } } } else if(parms.IsOptionPresent("test")) { // Get the file name const char *name = parms.GetOptionParameter("test"); // Check if the test can be created without a template parameter TestBase *test = CreateNonTemplateTest(name); // If that failed, check if the test can be created using a template // parameter if(!test && (test = TemplatedTestCreator(name).GetTest())) { // Get the template type or a blank string string type = parms.IsOptionPresent("type") ? parms.GetOptionParameter("type") : ""; // Instantiate the template test of the right type if(type == "char") test = TemplatedTestCreator(name).GetTest(); else if(type == "unsigned_char") test = TemplatedTestCreator(name).GetTest(); if(type == "short") test = TemplatedTestCreator(name).GetTest(); else if(type == "unsigned_short") test = TemplatedTestCreator(name).GetTest(); if(type == "int") test = TemplatedTestCreator(name).GetTest(); else if(type == "unsigned_int") test = TemplatedTestCreator(name).GetTest(); if(type == "long") test = TemplatedTestCreator(name).GetTest(); else if(type == "unsigned_long") test = TemplatedTestCreator(name).GetTest(); if(type == "float") test = TemplatedTestCreator(name).GetTest(); else if(type == "double") test = TemplatedTestCreator(name).GetTest(); else { std::cerr << "Missing or invalid or missing type parameter. Using default (char)" << std::endl; std::cerr << "Should be one of: " << std::endl; std::cerr << " char, unsigned_char," << std::endl; std::cerr << " short, unsigned_short," << std::endl; std::cerr << " int, unsigned_int," << std::endl; std::cerr << " long, unsigned_long," << std::endl; std::cerr << " float, " << std::endl; std::cerr << " double." << std::endl; } } // See if a test has been created after all if(test) { try { // Configure the parameters of the test test->ConfigureCommandLineParser(clap); // Get the command line result CommandLineArgumentParseResult testParms; if(clap.TryParseCommandLine(argc,argv,testParms,false)) { test->SetCommandLineParameters(testParms); test->Run(); } else { test->PrintUsage(); } delete test; } catch(TestUsageException) { test->PrintUsage(); } catch(itk::ExceptionObject &exc) { std::cerr << "ITK Exception: " << std::endl << exc << std::endl; } catch(...) { std::cerr << "Unknowm Exception!" << std::endl; } } else { std::cerr << "Could not create test!" << std::endl; PrintUsage(); } } else { PrintUsage(); } } itksnap/Testing/SNAPTestDriver.h0000644000076500000240000000342710534177574016132 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: SNAPTestDriver.h,v $ Language: C++ Date: $Date: 2006/12/02 04:22:20 $ 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 __SNAPTestDriver_h_ #define __SNAPTestDriver_h_ #include "TestBase.h" /** * \class SNAPTestDriver * \brief Class used to launch different tests */ class SNAPTestDriver { public: /** Run a test as determined by the command line parameters */ void Run(int argc, char *argv[]); private: /** Print the usage of the test application */ void PrintUsage(); /** Create a test of given name, or return NULL */ static TestBase *CreateNonTemplateTest(const char *name); /** Class to create a templated test of given name, or return NULL */ template class TemplatedTestCreator { public: TemplatedTestCreator(const char *name); TestBase *GetTest(); private: TestBase *m_Test; }; /** Create a test of given name, either templated or not, o/w return NULL */ TestBase *CreateTestInstance(const char *name); /** Number of tests */ static const unsigned int NUMBER_OF_TESTS; /** Test names */ static const char *m_TestNames[]; /** Whether tests are templated or not */ static const bool m_TestTemplated[]; }; #endif // __SNAPTestDriver_h_ itksnap/Testing/TestBase.h0000644000076500000240000000552211277554254015064 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: TestBase.h,v $ Language: C++ Date: $Date: 2009/11/14 16:19:56 $ Version: $Revision: 1.2 $ 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 __TestBase_h_ #define __TestBase_h_ #include "CommandLineArgumentParser.h" #include "ImageIORoutines.h" #include "itkOrientedImage.h" /** Exception when needed parameters are omitted */ class TestUsageException : public itk::ExceptionObject {}; class TestBase { public: // Describe the usage of the test virtual void PrintUsage() = 0; // Get the ID of the test for command line specification virtual const char *GetTestName() = 0; // Get the ID of the test for command line specification virtual const char *GetDescription() = 0; // Tack on options to the command line parser virtual void ConfigureCommandLineParser(CommandLineArgumentParser &parser) = 0; // Pass command line parameters to the test virtual void SetCommandLineParameters( const CommandLineArgumentParseResult ¶meters) { m_Command = parameters; } // Create a new test with given command line parameters. The test // either runs succesfully and returns, or throws an exception // describing what happened. virtual void Run() = 0; // Destructor virtual ~TestBase() {} protected: // The command line parse result, i.e., parameters of the test CommandLineArgumentParseResult m_Command; }; template class TestBaseOneImage : public TestBase { public: // Type definitions typedef itk::OrientedImage ImageType; typedef typename ImageType::Pointer ImagePointer; // Print how to use this test virtual void PrintUsage() { std::cerr << " image FILE : Pass image name " << std::endl; } // Tack on options to the command line parser virtual void ConfigureCommandLineParser(CommandLineArgumentParser &parser) { parser.AddOption("image",1); } // Run method, loads the passed in message virtual void Run() { // Check if the image option is present if(!m_Command.IsOptionPresent("image")) throw new TestUsageException(); // Load the image - will throw an exception if something fails LoadImageFromFile(m_Command.GetOptionParameter("image"),m_Image); } // Destructor virtual ~TestBaseOneImage() {} protected: // The loaded image ImagePointer m_Image; }; #endif //__TestBase_h_ itksnap/Testing/TestCompareLevelSets.cxx0000644000076500000240000007007711277554254020011 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: TestCompareLevelSets.cxx,v $ Language: C++ Date: $Date: 2009/11/14 16:19:56 $ Version: $Revision: 1.5 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include #include #endif #include "TestCompareLevelSets.h" #include "SNAPRegistryIO.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "SNAPImageData.h" #include "ImageWrapper.h" #include "GreyImageWrapper.h" #include "SNAPLevelSetFunction.h" #include "itkDenseFiniteDifferenceImageFilter.h" #include "itkImageRegionConstIterator.h" #include "itkSparseFieldLevelSetImageFilter.h" #include "itkParallelSparseFieldLevelSetImageFilter.h" #include "itkNarrowBandLevelSetImageFilter.h" #include "itkCommand.h" #include "itkUnaryFunctorImageFilter.h" #include #include void TestCompareLevelSets ::PrintUsage() { // Run the parent's part of the test Superclass::PrintUsage(); // RAI may be passed to this test std::cout << " config FILE : Set configuration registry file (required)" << std::endl; std::cout << " generate : Instead of running the test, generate a sample config" << std::endl; } template class LevelSetExtensionFilter : public TFilter { public: /** Standard class typedefs. */ typedef LevelSetExtensionFilter Self; typedef TFilter Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Run-time type information. */ itkTypeMacro(LevelSetExtensionFilter,TFilter); /** Capture information from the superclass. */ typedef typename Superclass::InputImageType InputImageType; typedef typename Superclass::OutputImageType OutputImageType; // typedef typename Superclass::UpdateBufferType UpdateBufferType; /** Dimensionality of input and output data is assumed to be the same. * It is inherited from the superclass. */ itkStaticConstMacro(ImageDimension, unsigned int,Superclass::ImageDimension); itkNewMacro(LevelSetExtensionFilter); /** The pixel type of the output image will be used in computations. * Inherited from the superclass. */ typedef typename Superclass::PixelType PixelType; typedef typename Superclass::TimeStepType TimeStepType; /** Set/Get the number of iterations that the filter will run. */ itkSetMacro(NumberOfIterations, unsigned int); itkGetConstReferenceMacro(NumberOfIterations, unsigned int); protected: LevelSetExtensionFilter() { m_NumberOfIterations = 10; } ~LevelSetExtensionFilter() {} void PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os,indent); } /** Supplies the halting criteria for this class of filters. The * algorithm will stop after a user-specified number of iterations. */ virtual bool Halt() { if (this->GetElapsedIterations() >= m_NumberOfIterations) return true; else return false; } private: LevelSetExtensionFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented unsigned int m_NumberOfIterations; }; #ifdef _USE_FastLevelSetFunction_ class FastSNAPLevelSetFunction : public SNAPLevelSetFunction > { public: /** Standard class typedefs. */ typedef FastSNAPLevelSetFunction Self; typedef itk::Image ImageType; typedef itk::SegmentationLevelSetFunction Superclass; typedef itk::SmartPointer Pointer; typedef itk::SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(FastSNAPLevelSetFunction); /** Run-time type information (and related methods) */ itkTypeMacro( SNAPLevelSetFunction, itk::LevelSetFunction ); /** Extract some parameters from the superclass. */ typedef Superclass::ImageType ImageType; typedef ImageType::Pointer ImagePointer; typedef Superclass::NeighborhoodType NeighborhoodType; typedef Superclass::ScalarValueType ScalarValueType; typedef Superclass::RadiusType RadiusType; typedef Superclass::FloatOffsetType FloatOffsetType; typedef Superclass::VectorType VectorType; typedef itk::Image VectorImageType; typedef VectorImageType::Pointer VectorImagePointer; typedef Superclass::TimeStepType TimeStepType; typedef Superclass::GlobalDataStruct GlobalDataStruct; /** Our own initialize method: computes all offsets */ virtual void Initialize(const RadiusType &r); /** Our own compute update method: fast and furiuos */ virtual float ComputeUpdate(const NeighborhoodType &it, void *globalData, const FloatOffsetType& offset); /** Image offsets for fast neighborhood operations */ int m_OffsetFX; int m_OffsetFY; int m_OffsetFZ; int m_OffsetBX; int m_OffsetBY; int m_OffsetBZ; int m_OffsetFXFY; int m_OffsetFYFZ; int m_OffsetFZFX; int m_OffsetBXFY; int m_OffsetBYFZ; int m_OffsetBZFX; int m_OffsetFXBY; int m_OffsetFYBZ; int m_OffsetFZBX; int m_OffsetBXBY; int m_OffsetBYBZ; int m_OffsetBZBX; }; void FastSNAPLevelSetFunction ::Initialize(const RadiusType &r) { // Let the parent do his work Superclass::Initialize(r); // Compute all the offsets m_OffsetFX = m_xStride[0]; m_OffsetFY = m_xStride[1]; m_OffsetFZ = m_xStride[2]; m_OffsetBX = -m_OffsetFX; m_OffsetBY = -m_OffsetFY; m_OffsetBZ = -m_OffsetFZ; m_OffsetFXFY = m_OffsetFX + m_OffsetFY; m_OffsetFYFZ = m_OffsetFY + m_OffsetFZ; m_OffsetFZFX = m_OffsetFZ + m_OffsetFX; m_OffsetBXFY = m_OffsetBX + m_OffsetFY; m_OffsetBYFZ = m_OffsetBY + m_OffsetFZ; m_OffsetBZFX = m_OffsetBZ + m_OffsetFX; m_OffsetFXBY = m_OffsetFX + m_OffsetBY; m_OffsetFYBZ = m_OffsetFY + m_OffsetBZ; m_OffsetFZBX = m_OffsetFZ + m_OffsetBX; m_OffsetBXBY = m_OffsetBX + m_OffsetBY; m_OffsetBYBZ = m_OffsetBY + m_OffsetBZ; m_OffsetBZBX = m_OffsetBZ + m_OffsetBX; } float FastSNAPLevelSetFunction ::ComputeUpdate(const NeighborhoodType &it, void *globalData,const FloatOffsetType& offset) { // Get the central pixel location const float *pixel = it.GetCenterPointer(); // Global data structure GlobalDataStruct *gd = (GlobalDataStruct *)globalData; // Get the neighboring pixel values float v = *(pixel); float fx = *(pixel + m_OffsetFX); float bx = *(pixel + m_OffsetBX); float fy = *(pixel + m_OffsetFY); float by = *(pixel + m_OffsetBY); float fz = *(pixel + m_OffsetFZ); float bz = *(pixel + m_OffsetBZ); // Compute the second derivatives float vv = v + v; float uxx = fx + bx - vv; float uyy = fy + by - vv; float uzz = fz + bz - vv; // Forward and backward differences float uxf = fx - v; float uyf = fy - v; float uzf = fz - v; float uxb = v - bx; float uyb = v - by; float uzb = v - bz; // Compute the central first derivatives float uxc = 0.5f * (fx - bx); float uyc = 0.5f * (fy - by); float uzc = 0.5f * (fz - bz); // Compute the squared central first derivatives float uxc2 = uxc*uxc; float uyc2 = uyc*uyc; float uzc2 = uzc*uzc; // Compute the Hessian matrix and various other derivatives. Some of these // derivatives may be used by overloaded virtual functions. gd->m_GradMagSqr = 1.0e-6 + uxc2 + uyc2 + uzc2; // Compute the curvature term float curvature_term = 0.0f; if(m_CurvatureWeight != 0.0f) { // More terms to compute float fxfy = *(pixel + m_OffsetFXFY); float fyfz = *(pixel + m_OffsetFYFZ); float fzfx = *(pixel + m_OffsetFZFX); float bxfy = *(pixel + m_OffsetBXFY); float byfz = *(pixel + m_OffsetBYFZ); float bzfx = *(pixel + m_OffsetBZFX); float fxby = *(pixel + m_OffsetFXBY); float fybz = *(pixel + m_OffsetFYBZ); float fzbx = *(pixel + m_OffsetFZBX); float bxby = *(pixel + m_OffsetBXBY); float bybz = *(pixel + m_OffsetBYBZ); float bzbx = *(pixel + m_OffsetBZBX); // Compute the mixed derivatives float uxy = 0.25f * (fxfy + bxby - bxfy - fxby); float uyz = 0.25f * (fyfz + bybz - byfz - fybz); float uzx = 0.25f * (fzfx + bzbx - bzfx - fzbx); curvature_term = m_CurvatureWeight * ((uxc2+uyc2)*uzz + (uyc2+uzc2)*uxx + (uzc2+uxc2)*uyy - 2*( uxy*uxc*uyc + uyz*uyc*uzc + uzx*uzc*uxc )) / gd->m_GradMagSqr; curvature_term *= this->CurvatureSpeed(it, offset); } // Compute the advection term float advection_term = 0.0f; if (m_AdvectionWeight != 0.0f) { VectorType advection_field = this->AdvectionField(it, offset, gd); float ax = advection_field[0] * m_AdvectionWeight; float ay = advection_field[1] * m_AdvectionWeight; float az = advection_field[2] * m_AdvectionWeight; advection_term = ((ax > 0) ? uxb * ax : uxf * ax) + ((ay > 0) ? uyb * ay : uyf * ay) + ((az > 0) ? uzb * az : uzf * az); // Compute the maximal advection change float maxaxay = (ax > ay) ? ax : ay; float maxazam = (az > gd->m_MaxAdvectionChange) ? az : gd->m_MaxAdvectionChange; gd->m_MaxAdvectionChange = (maxaxay > maxazam) ? maxaxay : maxazam; } float propagation_term = 0.0f; if (m_PropagationWeight != 0.0f) { // Get the propagation speed propagation_term = m_PropagationWeight * this->PropagationSpeed(it, offset, gd); float propagation_gradient = (propagation_term > 0) ? vnl_math_sqr( vnl_math_max(uxb, 0.0f) ) + vnl_math_sqr( vnl_math_min(uxf, 0.0f) ) + vnl_math_sqr( vnl_math_max(uyb, 0.0f) ) + vnl_math_sqr( vnl_math_min(uyf, 0.0f) ) + vnl_math_sqr( vnl_math_max(uzb, 0.0f) ) + vnl_math_sqr( vnl_math_min(uzf, 0.0f) ) : vnl_math_sqr( vnl_math_min(uxb, 0.0f) ) + vnl_math_sqr( vnl_math_max(uxf, 0.0f) ) + vnl_math_sqr( vnl_math_min(uyb, 0.0f) ) + vnl_math_sqr( vnl_math_max(uyf, 0.0f) ) + vnl_math_sqr( vnl_math_min(uzb, 0.0f) ) + vnl_math_sqr( vnl_math_max(uzf, 0.0f) ); // Collect energy change from propagation term. This will be used in // calculating the maximum time step that can be taken for this iteration. gd->m_MaxPropagationChange = vnl_math_max(gd->m_MaxPropagationChange,vnl_math_abs(propagation_term)); // Scale the propagation term by gradient magnitude propagation_term *= vcl_sqrt( propagation_gradient ); } float laplacian_term = 0.0f; if(m_LaplacianSmoothingWeight != 0.0f) { laplacian_term = (uxx + uyy + uzz) * m_LaplacianSmoothingWeight * LaplacianSmoothingSpeed(it,offset, gd); } // Return the combination of all the terms. return curvature_term - propagation_term - advection_term - laplacian_term; } #endif // _USE_FastLevelSetFunction_ /** Used to report on each iteration */ class IterationReporter { public: typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator IteratorType; typedef itk::ImageToImageFilter FilterType; IterationReporter(FilterType *source,std::ofstream &fout) : m_Iteration(0), m_Out(fout), m_Source(source) { m_Command = CommandType::New(); m_Command->SetCallbackFunction(this,&IterationReporter::Callback); source->AddObserver(itk::IterationEvent(),m_Command); m_LastClock = clock(); } void Callback( itk::Object *irisNotUsed(object), const itk::EventObject &irisNotUsed(event)) { // Record the time clock_t currentClock = clock(); double delta = (currentClock - m_LastClock); m_Iteration++; // Write data m_Out << m_Iteration << "\t" << delta << /* "\t" << CountInterfaceVoxels() << */ std::endl; } unsigned long CountInterfaceVoxels() { ImageType *out = m_Source->GetOutput(); IteratorType it(out,out->GetBufferedRegion()); unsigned long count = 0; while(!it.IsAtEnd()) { if(it.Value() > -1.0f && it.Value() < 1.0f) count++; ++it; } return count; } private: int m_Iteration; clock_t m_LastClock; std::ofstream &m_Out; typedef itk::MemberCommand CommandType; CommandType::Pointer m_Command; FilterType::Pointer m_Source; }; float TestCompareLevelSets ::ComputeOverlapDistance(FloatImageType *i1,FloatImageType *i2) { typedef itk::ImageRegionConstIterator IteratorType; IteratorType it1(i1,i1->GetBufferedRegion()); IteratorType it2(i2,i2->GetBufferedRegion()); unsigned long nEither = 0; unsigned long nBoth = 0; while(!it1.IsAtEnd()) { float v1 = it1.Value(); float v2 = it2.Value(); if(v1 <= 0 || v2 <= 0) nEither++; if(v1 <= 0 && v2 <= 0) nBoth++; ++it1; ++it2; } return nBoth * 1.0f / nEither; } void TestCompareLevelSets ::RunExperiment() { // We will use a registry to save orload configuration parameters Registry registry; try { registry.ReadFromFile(m_Command.GetOptionParameter("config")); } catch(Registry::IOException &exc) { std::cerr << "Error: " << exc << std::endl; std::cerr << "Hint: Try generating a config file using 'generate' option!" << std::endl; throw new TestUsageException; } // Get the experiment ID string expID = registry["ExperimentId"]["000"]; // Initialize an IRIS application IRISApplication *app = new IRISApplication(); // Load the grey image and segmentation image app->LoadMainImage(registry["Image.Grey"][""], IRISApplication::MAIN_SCALAR); app->LoadLabelImageFile(registry["Image.Bubble"][""]); // Some image pointers SpeedImageWrapper::ImagePointer speed; // Get the preprocessing image from the registry LoadImageFromFile(registry["Image.Speed"][""],speed); // Specify the label used for segmentation app->GetGlobalState()->SetDrawingColorLabel( (LabelType)registry["Image.BubbleLabel"][255]); // Create an ROI object to specify the region of interest to select SNAPSegmentationROISettings roiSettings; // Use the entire region and no resampling of the region roiSettings.SetROI(app->GetCurrentImageData()->GetImageRegion()); roiSettings.SetResampleFlag(false); // Start SNAP logic with the selected region settings app->InitializeSNAPImageData(roiSettings); // Get a pointer to the SNAP image data SNAPImageData *snap = app->GetSNAPImageData(); // Get the snake mode bool edgeMode = registry["Image.SpeedIsEdge"][false]; // A registry IO object to read/write SNAP properties SNAPRegistryIO regio; // Pass in the speed image // Preprocess the image and initialize the level set image SnakeParameters parameters; if(edgeMode) { app->UpdateSNAPSpeedImage(speed,EDGE_SNAKE); parameters = regio.ReadSnakeParameters( registry.Folder("SnakeParameters.Edge"), SnakeParameters::GetDefaultEdgeParameters()); } else { app->UpdateSNAPSpeedImage(speed,IN_OUT_SNAKE); parameters = regio.ReadSnakeParameters( registry.Folder("SnakeParameters.InOut"), SnakeParameters::GetDefaultInOutParameters()); } // Done with the initialization. Open an output dump file string targetPath = registry["OutputPath"]["."] + string("/"); std::ofstream fout((targetPath + "report." + expID + ".txt").c_str()); // Now, we have a speed image and a level set image. We are ready to // test different segmenter typedef SpeedImageWrapper::ImageType FloatImageType; typedef itk::DenseFiniteDifferenceImageFilter< FloatImageType,FloatImageType> DenseFilterType; typedef itk::SparseFieldLevelSetImageFilter< FloatImageType,FloatImageType> SparseFilterType; typedef itk::ParallelSparseFieldLevelSetImageFilter< FloatImageType,FloatImageType> ParallelSparseFilterType; typedef itk::NarrowBandLevelSetImageFilter< FloatImageType,FloatImageType> NarrowFilterType; typedef LevelSetExtensionFilter DenseExtensionFilter; typedef LevelSetExtensionFilter SparseExtensionFilter; typedef LevelSetExtensionFilter ParallelExtensionFilter; typedef LevelSetExtensionFilter NarrowExtensionFilter; // Pull out the finite difference function std::vector dummy; snap->InitializeSegmentation( parameters,dummy,app->GetGlobalState()->GetDrawingColorLabel()); SNAPLevelSetFunction *phi = snap->GetLevelSetFunction(); SNAPLevelSetDriver( snap->GetSnake()->GetImage(), snap->GetSpeed()->GetImage(), // Decide on a number of iterations unsigned int nIterations = registry["Iterations"][10]; // Set up the dense filter DenseExtensionFilter::Pointer fltDense = DenseExtensionFilter::New(); fltDense->SetInput(snap->GetSnake()->GetImage()); fltDense->SetDifferenceFunction(phi); fltDense->SetNumberOfIterations(nIterations); // Set up the sparse fileter SparseExtensionFilter::Pointer fltSparse = SparseExtensionFilter::New(); fltSparse->SetInput(snap->GetSnake()->GetImage()); fltSparse->SetDifferenceFunction(phi); fltSparse->SetNumberOfIterations(nIterations); fltSparse->SetNumberOfLayers(3); fltSparse->SetIsoSurfaceValue(0.0f); ParallelExtensionFilter::Pointer fltParallel = ParallelExtensionFilter::New(); fltParallel->SetInput(snap->GetSnake()->GetImage()); fltParallel->SetDifferenceFunction(phi); fltParallel->SetNumberOfIterations(nIterations); fltParallel->SetNumberOfLayers(3); fltParallel->SetIsoSurfaceValue(0.0f); // Create iteration reporters IterationReporter irDense(fltDense.GetPointer(),fout); IterationReporter irSparse(fltSparse.GetPointer(),fout); IterationReporter irParallel(fltParallel.GetPointer(),fout); // Create a filter for filling in the interior of a distance transform typedef itk::UnaryFunctorImageFilter< FloatImageType,LabelImageWrapper::ImageType,InteriorFunctor> InteriorFilter; InteriorFilter::Pointer fltInterior = InteriorFilter::New(); // Run and save the three filters fout << "Running the Narrow filter!" << std::endl; fltNarrow->Update(); fout << "Running the Sparse filter!" << std::endl; fltSparse->Update(); fout << "Running the Parallel filter!" << std::endl; fltParallel->Update(); // fout << "Running the Dense filter!" << std::endl; // fltDense->Update(); // Compute the difference between filter outputs fout << "Narrow-Sparse\t" << ComputeOverlapDistance(fltNarrow->GetOutput(),fltSparse->GetOutput()) << std::endl; fout << "Narrow-Dense\t" << ComputeOverlapDistance(fltNarrow->GetOutput(),fltDense->GetOutput()) << std::endl; fout << "Narrow-Parallel\t" << ComputeOverlapDistance(fltNarrow->GetOutput(),fltParallel->GetOutput()) << std::endl; fout << "Sparse-Dense\t" << ComputeOverlapDistance(fltSparse->GetOutput(),fltDense->GetOutput()) << std::endl; fout << "Sparse-Parallel\t" << ComputeOverlapDistance(fltSparse->GetOutput(),fltParallel->GetOutput()) << std::endl; fout << "Dense-Parallel\t" << ComputeOverlapDistance(fltDense->GetOutput(),fltParallel->GetOutput()) << std::endl; // Write each result to a file fltInterior->SetInput(fltNarrow->GetOutput()); string file = targetPath + "CompareLevelSets." + expID + ".narrow.gipl"; SaveImageToFile(file.c_str(),fltInterior->GetOutput()); fltInterior->SetInput(fltDense->GetOutput()); file = targetPath + "CompareLevelSets." + expID + ".dense.gipl"; SaveImageToFile(file.c_str(),fltInterior->GetOutput()); fltInterior->SetInput(fltSparse->GetOutput()); file = targetPath + "CompareLevelSets." + expID + ".sparse.gipl"; SaveImageToFile(file.c_str(),fltInterior->GetOutput()); fltInterior->SetInput(fltParallel->GetOutput()); file = targetPath + "CompareLevelSets." + expID + ".parallel.gipl"; SaveImageToFile(file.c_str(),fltInterior->GetOutput()); // Close the output fout.close(); } void TestCompareLevelSets ::Run() { RunExperiment(); return; // Run the parent's part of the test (loads image) Superclass::Run(); // We need to wrap the image for some operations GreyImageWrapper wrapper; wrapper.SetImage(m_Image); // We need some default parameters for the test SnakeParameters parmSnakeEdge = SnakeParameters::GetDefaultEdgeParameters(); SnakeParameters parmSnakeInOut = SnakeParameters::GetDefaultInOutParameters(); EdgePreprocessingSettings parmPreprocessEdge = EdgePreprocessingSettings::MakeDefaultSettings(); ThresholdSettings parmPreprocessInOut = ThresholdSettings::MakeDefaultSettings(&wrapper); // Some other defaults: which snake to use bool parmUseEdgeSnake = true; // Bubble default, based on the image Vector3d parmBubbleCenter = to_double(wrapper.GetSize()) / 2.0; double parmBubbleRadius = 2.0; // Get the configuration file if(!m_Command.IsOptionPresent("config")) throw new TestUsageException; // We will use a registry to save orload configuration parameters Registry registry; // A registry IO object to read/write SNAP properties SNAPRegistryIO regio; // Check if the user wants to generate a config file if(m_Command.IsOptionPresent("generate")) { // Store the options in the registry regio.WriteSnakeParameters( parmSnakeEdge,registry.Folder("SnakeParameters.Edge")); regio.WriteSnakeParameters( parmSnakeInOut,registry.Folder("SnakeParameters.InOut")); regio.WriteEdgePreprocessingSettings( parmPreprocessEdge,registry.Folder("Preprocessing.Edge")); regio.WriteThresholdSettings( parmPreprocessInOut,registry.Folder("Preprocessing.InOut")); // Store the default position (center of image, kinda-dumb) registry["Seed.Position"] << parmBubbleCenter; registry["Seed.Radius"] << parmBubbleRadius; // Store the snake mode to use registry["SnakeMode"] << parmUseEdgeSnake; // Write the registry registry.WriteToFile(m_Command.GetOptionParameter("config")); return; } // Try loading the registry try { registry.ReadFromFile(m_Command.GetOptionParameter("config")); } catch(Registry::IOException &exc) { std::cerr << "Error: " << exc << std::endl; std::cerr << "Hint: Try generating a config file using 'generate' option!" << std::endl; throw new TestUsageException; } // Read snake parameters from the registry parmSnakeEdge = regio.ReadSnakeParameters( registry.Folder("SnakeParameters.Edge"),parmSnakeEdge); parmSnakeInOut = regio.ReadSnakeParameters( registry.Folder("SnakeParameters.InOut"),parmSnakeInOut); // Read preprocessing settings from the registry parmPreprocessEdge = regio.ReadEdgePreprocessingSettings( registry.Folder("Preprocessing.Edge"),parmPreprocessEdge); parmPreprocessInOut = regio.ReadThresholdSettings( registry.Folder("Preprocessing.InOut"),parmPreprocessInOut); // Read the seed position parmBubbleCenter = registry["Seed.Position"][parmBubbleCenter]; parmBubbleRadius = registry["Seed.Radius"][parmBubbleRadius]; // Read the snake mode to use parmUseEdgeSnake = registry["SnakeMode"][parmUseEdgeSnake]; /** Now we have some configuration parameters and an image to work with. Let's get to the gritty of it! */ // Initialize an IRIS application IRISApplication *app = new IRISApplication(); // Pass the image to the application app->UpdateIRISGreyImage(m_Image, GreyTypeToNativeFunctor()); // Start SNAP logic with the entire region SNAPSegmentationROISettings roiSettings; roiSettings.SetROI(app->GetCurrentImageData()->GetImageRegion()); roiSettings.SetResampleFlag(false); app->InitializeSNAPImageData(roiSettings); // Get a pointer to the SNAP image data SNAPImageData *snap = app->GetSNAPImageData(); // Create an initialization bubble Bubble bubble; bubble.center = to_int(parmBubbleCenter); bubble.radius = (int) parmBubbleRadius; std::vector bubbleList; bubbleList.push_back(bubble); // Preprocess the image and initialize the level set image if(parmUseEdgeSnake) { snap->DoEdgePreprocessing(parmPreprocessEdge); snap->InitializeSegmentation(parmSnakeEdge,bubbleList,255); } else { snap->DoInOutPreprocessing(parmPreprocessInOut); snap->InitializeSegmentation(parmSnakeEdge,bubbleList,255); } // Now, we have a speed image and a level set image. We are ready to // test different segmenters typedef SpeedImageWrapper::ImageType FloatImageType; typedef itk::DenseFiniteDifferenceImageFilter< FloatImageType,FloatImageType> DenseFilterType; typedef itk::SparseFieldLevelSetImageFilter< FloatImageType,FloatImageType> SparseFilterType; typedef itk::NarrowBandLevelSetImageFilter< FloatImageType,FloatImageType> NarrowFilterType; typedef LevelSetExtensionFilter DenseExtensionFilter; typedef LevelSetExtensionFilter SparseExtensionFilter; typedef LevelSetExtensionFilter NarrowExtensionFilter; // Pull out the finite difference function SNAPLevelSetFunction *phi = snap->GetLevelSetFunction(); // Decide on a number of iterations unsigned int nIterations = 10; // Create a callback object typedef itk::MemberCommand CommandType; CommandType::Pointer callback = CommandType::New(); callback->SetCallbackFunction(this,&TestCompareLevelSets::IterationCallback); // Set up the dense filter DenseExtensionFilter::Pointer fltDense = DenseExtensionFilter::New(); fltDense->SetInput(snap->GetSnake()->GetImage()); fltDense->SetDifferenceFunction(phi); fltDense->SetNumberOfIterations(nIterations); fltDense->AddObserver(itk::IterationEvent(),callback); // Set up the sparse fileter SparseExtensionFilter::Pointer fltSparse = SparseExtensionFilter::New(); fltSparse->SetInput(snap->GetSnake()->GetImage()); fltSparse->SetDifferenceFunction(phi); fltSparse->SetNumberOfIterations(nIterations); fltSparse->SetNumberOfLayers(3); fltSparse->SetIsoSurfaceValue(0.0f); fltSparse->AddObserver(itk::IterationEvent(),callback); // Set up the narrow band filter NarrowExtensionFilter::Pointer fltNarrow = NarrowExtensionFilter::New(); fltNarrow->SetSegmentationFunction(phi); fltNarrow->SetInput(snap->GetSnake()->GetImage()); fltNarrow->SetFeatureImage(snap->GetSpeed()->GetImage()); fltNarrow->SetNumberOfIterations(nIterations); fltNarrow->AddObserver(itk::IterationEvent(),callback); // Create a filter for filling in the interior of a distance transform typedef itk::UnaryFunctorImageFilter< FloatImageType,LabelImageWrapper::ImageType,InteriorFunctor> InteriorFilter; InteriorFilter::Pointer fltInterior = InteriorFilter::New(); // Run and save the narrow filter std::cout << "Running the Narrow filter!" << std::endl; //fltInterior->SetInput(fltNarrow->GetOutput()); //fltInterior->Update(); //SaveImageToFile("CompareLevelSets.narrow.gipl",fltInterior->GetOutput()); // Run and save the dense filter std::cout << "Running the Dense filter!" << std::endl; //fltInterior->SetInput(fltDense->GetOutput()); //fltInterior->Update(); //SaveImageToFile("CompareLevelSets.dense.gipl",fltInterior->GetOutput()); // Run and save the sparse filter std::cout << "Running the Sparse filter!" << std::endl; fltInterior->SetInput(fltSparse->GetOutput()); fltInterior->Update(); SaveImageToFile("CompareLevelSets.sparse.gipl",fltInterior->GetOutput()); } void TestCompareLevelSets ::IterationCallback(itk::Object *object, const itk::EventObject &irisNotUsed(event)) { static clock_t lastTime = clock(); clock_t currentTime = clock(); double delta = (double)(currentTime - lastTime) / CLOCKS_PER_SEC; double progress = reinterpret_cast(object)->GetProgress(); std::cout << std::setw(20) << progress << std::setw(20) << delta * 1000 << std::endl; } itksnap/Testing/TestCompareLevelSets.h0000644000076500000240000000365610534177574017436 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: TestCompareLevelSets.h,v $ Language: C++ Date: $Date: 2006/12/02 04:22:20 $ 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 __TestCompareLevelSets_h_ #define __TestCompareLevelSets_h_ #include "SNAPCommon.h" #include "TestBase.h" /** * This class is used to test the functionality in the ImageWrapper class */ class TestCompareLevelSets : public TestBaseOneImage { public: typedef TestBaseOneImage Superclass; void PrintUsage(); void Run(); void RunExperiment(); const char *GetTestName() { return "CompareLevelSets"; } const char *GetDescription() { return "Compare level set methods"; } virtual void ConfigureCommandLineParser(CommandLineArgumentParser &parser) { Superclass::ConfigureCommandLineParser(parser); parser.AddOption("config",1); parser.AddOption("generate",0); } private: // Progress callback void IterationCallback(itk::Object *object, const itk::EventObject &event); // Interior extraction functor class InteriorFunctor { public: unsigned char operator()(float input) { return input <= 0.0f ? 255 : 0; } }; // Image type typedef itk::Image FloatImageType; // Compute volume overlap as (A int B) / (A union B) float ComputeOverlapDistance(FloatImageType *i1,FloatImageType *i2); }; #endif // __TestCompareLevelSets_h_ itksnap/Testing/TestImageWrapper.h0000644000076500000240000000526311277554254016577 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: TestImageWrapper.h,v $ Language: C++ Date: $Date: 2009/11/14 16:19:56 $ Version: $Revision: 1.3 $ 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 __TestImageWrapper_h_ #define __TestImageWrapper_h_ #include "TestBase.h" #include "ScalarImageWrapper.h" #include "ScalarImageWrapper.txx" #include "GreyImageWrapper.h" #include "LabelImageWrapper.h" /** * This class is used to test the functionality in the ImageWrapper class */ template class TestImageWrapper : public TestBaseOneImage { public: typedef TestBaseOneImage Superclass; typedef TWrapper WrapperType; void PrintUsage(); void Run(); const char *GetTestName() { return "ImageWrapper"; } const char *GetDescription() { return "A suite of ImageWrapper tests"; } }; template void TestImageWrapper ::PrintUsage() { // Run the parent's part of the test Superclass::PrintUsage(); // RAI may be passed to this test std::cout << " rai CODE : Pass in an RAI anatomy-image code" << std::endl; } template void TestImageWrapper ::Run() { // Run the parent's part of the test (loads image) Superclass::Run(); // Do the rest std::cout << "Testing code in ImageWrapper.h" << std::endl; // Create an image wrapper WrapperType *wrapper = new WrapperType(); // Insert image into the wrapper wrapper->SetImage(this->m_Image); // Set the cursor position in the slice wrapper wrapper->SetSliceIndex(wrapper->GetSize() / ((unsigned int)2)); // Create a transform that specifies the image-slice geometry ImageCoordinateTransform ict; ict.SetTransform(Vector3i(-2,1,-3),wrapper->GetSize()); // Get a slice in the z-direction in the image typename WrapperType::SlicePointer slice = wrapper->GetSlice(0); // Report min/max intensities std::cout << "Max intensity: " << wrapper->GetImageMax() << std::endl; std::cout << "Min intensity: " << wrapper->GetImageMin() << std::endl; // We are finished testing std::cout << "Testing complete" << std::endl; } #endif //__TestImageWrapper_h_ itksnap/Testing/TestMain.cxx0000644000076500000240000000157510534177574015456 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: TestMain.cxx,v $ Language: C++ Date: $Date: 2006/12/02 04:22:20 $ 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. =========================================================================*/ // Define the verbose output stream #include "SNAPTestDriver.h" std::ostream &verbose = std::cout; int main(int argc, char *argv[]) { SNAPTestDriver driver; driver.Run(argc,argv); return 0; } itksnap/Testing/TutorialTest.cxx0000644000076500000240000004454610534177574016402 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: TutorialTest.cxx,v $ Language: C++ Date: $Date: 2006/12/02 04:22:20 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include #endif #include "ImageIORoutines.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "LevelSetMeshPipeline.h" #include "SNAPImageData.h" #include "vtkPolyData.h" #include "vtkPolyDataWriter.h" #include /** * This function is designed to serve as a tutorial through the classes * that form the logical framework of SNAP. In this test, the student will * be able to load images, perform preprocessing, initialize and run segmentation, * output slices from the segmentation and relabel the segmentatinon * * The method should be invoked with two parameters. First is the full path to * the file MRIcrop-orig.gipl which contains the test image. Second is the full * path to the label file MRIcrop-seg.label. Both of these files can be downloaded * from the SNAP website */ int main(int argc, char *argv[]) { if(argc < 3) { cerr << "Must specify two arguments." << endl; return 1; } /* ======================================================================= * Section 1. Starting the Application and Loading an Image * ====================================================================== */ /* We begin by creating an IRISApplication object. This is the most high * level object in the SNAP class hierarchy and it provides access to the * remainder of the SNAP logic classes */ IRISApplication application; /* Next we will load the test image. In this example it is expected that the * image MRIcrop-orig.gipl, which can be downloaded from the SNAP website * will be used. The user will pass the full path to this file as the first * parameter to the program */ const char *sImageFileName = argv[1]; try { /* The responsibility for loading the image is with the user. First, we * create an itk::Image object into which the image will be loaded from * disk */ IRISApplication::GreyImageType::Pointer imgGrey; /* SNAP provides a helper method LoadImageFromFile for loading images */ LoadImageFromFile(sImageFileName,imgGrey); /* Once the image has been loaded, it can be passed on to the IRISApplication. * However, SNAP needs to know the orientation of the image, which is * expressed as the position of the image's origin {X,Y,Z}={0,0,0} in * patient coordinates. This information is passed on in the form of a * three letter RAI code. The value "ASR" means that the origin lies in the * Anterior-Superior-Right corner of the patient coordinate system, with * X axis corresponding to the Anterior-Posterior axis, Y axis corresponding * to the Inferior-Superior axis and Z to the Right-Left axis. For the image * we are loading, the correct code is "RAI" */ application.UpdateIRISGreyImage(imgGrey,"RAI"); /* In addition to loading the image, we are going to load the segmentation * labels associated with this image. Segmentation labels are stored in the * file MRIcrop-seg.label and passed on as an argument to this test program */ const char *sLabelFileName = argv[2]; application.GetColorLabelTable()->LoadFromFile(sLabelFileName); } catch(itk::ExceptionObject &exception) { // This code is called if LoadImageFromFile fails. cout << "Error Loading Image: " << exception; return -1; } /* ======================================================================= * Section 2. Accessing Image Data Using IRISImageData Class * ====================================================================== */ /* Now the image has been loaded into IRISApplication. IRISApplication stores * its data using two high level classes: IRISImageData and SNAPImageData. * IRISImageData holds the original greyscale image that we have loaded as * well as the labelling of that image that is constructed throughout the * segmentation. SNAPImageData is used for automatic segmentation and contains * a subregion of the original greyscale image. At this point SNAPImageData has * not yet been allocated. SNAPImageData is a child class of IRISImageData. * * The method IRISApplication::GetCurrentImageData() returns the IRISImageData * object associated with the current state of SNAP. In manual state (current * state right now) it returns a pointer to an IRISImageData object. In * automatic segmentation state, it returns a pointer to a SNAPImageData * object. * * The IRISImageData and SNAPImageData objects can also be accessed explicitly * using the GetIRISImageData() and GetSNAPImageData() pointers. * * We will begin by examining the IRISImageData class. We can use it to access * the underlying image and to examine the segmentation. */ IRISImageData *irisData = application.GetCurrentImageData(); /* IRISImageData contains two images: the grey level image and a label or * segmentation image. Both images are of the same dimensions. The grey image * has voxels of type GreyType and the segmentation image of type LabelType */ cout << "Image Dimensions: " << to_int(irisData->GetVolumeExtents()) << endl; // Ignore the to_int! // cout << "Voxel Size : " << irisData->GetVoxelScaleFactor() << endl; /* IRISImageData also handles segmentation labels. We can inquire about each label. * Labels are indexed from 1 to 255 (0 is the clear label). Only some labels are * defined as 'valid', i.e., available to the user to edit */ for(unsigned int iLabel=0; iLabel < MAX_COLOR_LABELS; iLabel++) { /* Labels are represented by the class ColorLabel. Each label has a color, * a transparency level, a visible flag, a 'visible in 3d mesh flag', and a * textual description */ const ColorLabel &label = application.GetColorLabelTable()->GetColorLabel(iLabel); cout << "Label Number : " << iLabel << endl; cout << " Decription : " << label.GetLabel() << endl; cout << " Color : {" << (int) label.GetRGB(0) << "," << (int) label.GetRGB(1) << "," << (int) label.GetRGB(2) << "}" << endl; cout << " Opacity : " << label.GetAlpha() << endl; } /* The current drawing label and the current draw-over label are properties of * the SNAP application and are accessed using the GlobalState class. * GlobalState stores a great number of application-wide settings */ application.GetGlobalState()->SetDrawingColorLabel(7); // caudate label application.GetGlobalState()->SetCoverageMode(PAINT_OVER_ALL); /* The next class we will learn about is the ImageWrapper class, which is a * wrapper around the standard itk::Image class. The ImageWrapper provides * several mini-pipelines involving the itk::Image stored within. First * let's access the grey images' wrapper in IRISImageData */ GreyImageWrapper *wrapGrey = irisData->GetGrey(); /* The class GreyImageWrapper is a subclass of the generic templated class * ImageWrapper with template argument TPixel=GreyType. We can * use ImageWrapper to get to the itk::Image itself */ GreyImageWrapper::ImagePointer imgGrey = wrapGrey->GetImage(); /* Here are some of the methods available through the ImageWrapper class */ cout << "Grey Min : " << wrapGrey->GetImageMin() << endl; cout << "Grey Max : " << wrapGrey->GetImageMax() << endl; /* The IRISImageData and ImageWrapper classes keep track of the current * position of the SNAP 3D cursor. When an image is loaded, the cursor is * positioned in the center of the image, as we can see by querying the * ImageWrapper class */ Vector3ui vCursor = wrapGrey->GetSliceIndex(); cout << "Cursor Position : " << to_int(vCursor) << endl; // Ignore the to_int! cout << "Value at Cursor : " << wrapGrey->GetVoxel(vCursor) << endl; /* The cursor position obtained above is given in image coordinates, where * Z is the slice number, Y is the row number and X is the column number. * In order to specify slice coordinates in patient (anatomical) * coordinates, we need to know the correct transformation, which we had \ * specified using an 'RAI' code when loading the grey image. The * transformation can be accessed through the IRISApplication object */ ImageCoordinateGeometry geometry = irisData->GetImageGeometry(); Vector3ui vCursorPatient = geometry.GetImageToAnatomyTransform().TransformVoxelIndex(vCursor); cout << "Anatomical Posn.: " << to_int(vCursorPatient) << endl; /* The cursor position is the same for the grey image wrapper and the label * image wrapper. Hence, to set the cursor position, we must call the * appropriate method in IRISImageData. We can also call the method * IRISApplication::SetCursorPosition for the same effect. */ vCursor -= Vector3ui(1,0,0); irisData->SetCrosshairs(vCursor); /* ImageWrapper provides a way to extract orthogonal 2D slices form the * image. A slice is extracted by setting the crosshair position and calling * the GetDisplaySlice method. This returns a 2D image of type unsigned char * that can be displayed on the screen. * * The mapping between the intensities in the grey image (which are shorts) * and intensities in the slice (which are bytes) can be changed by calling * the method SetIntensityMapFunction() */ GreyImageWrapper::DisplaySlicePointer imgSlice = wrapGrey->GetDisplaySlice(0); /* So far, we have looked at the grey image wrapper. The segmentation image * wrapper is very similar, except that it produces slices that are RGB * images. * * When we loaded the grey image, the segmentation image in IRISImageData was * automatically initialized to the same size and filled with voxels of * label 0, which is the 'Clear' label in SNAP. To update the segmentation, * we can directly modify the image wrapped by the LabelImageWrapper or * we can use the GetVoxelForUpdate() method: */ irisData->GetSegmentation()->GetVoxelForUpdate(vCursor) = 2; LabelImageWrapper::DisplaySlicePointer imgRGBSlice = irisData->GetSegmentation()->GetDisplaySlice(0); /* For validation purposes, we are going to write to disk the grey and * segmentation slices that we've extracted in this Section */ SaveImageToFile("greyslice01.png",imgSlice.GetPointer()); SaveImageToFile("rgbslice01.png",imgRGBSlice.GetPointer()); /* ======================================================================= * Section 3. Automatic Segmentation Mode * ====================================================================== */ /* To begin automatic segmentation, we need to switch on the automatic * segmentation mode. In order to do that, we must specify the region of * interest that we want to pass on to this mode and whether or not we * want to perform resampling on the region of interest. In this case we * will use the entire image as the region of interest and do no resampling */ SNAPSegmentationROISettings roiSettings; roiSettings.SetROI(irisData->GetImageRegion()); roiSettings.SetResampleFlag(false); /* We can now tell the IRISApplication to initialize the SNAPImageData object. * This method takes the ROI settings as the first parameter and an * itk::Command object as the optional second parameter. The Command is used * as a callback during the resampling, and is provided primarify for GUIs * that want to display a progress bar. */ application.InitializeSNAPImageData(roiSettings); /* Once the SNAP data is initalized, we can instruct the IRISApplication to * use it as the current image data, in other words, to switch to the auto * segmentation mode */ application.SetCurrentImageDataToSNAP(); /* The SNAPImageData class inherits the methods and attributes of the * IRISImageData class and provides additional functionality related to * level set image segmentation. In addition to the Grey and Segmentation * images (and image wrappers), the SNAPImageData class contains a Speed * image wrapper, a SnakeInitialization image wrapper and a Snake image * wrapper. All three of these image wrappers contain itk::Image objects * with pixel type float. In addition, the SNAPImageData class provides * method for running the automatic segmetnation pipeline */ SNAPImageData *snapData = application.GetSNAPImageData(); /* Automatic segmentation begins with preprocessing. We must choose the type * of preprocessing to apply and the parameters of the preprocessing. In this * tutorial we will use edge-based snakes and edge-detection preprocessing */ EdgePreprocessingSettings edgeSettings = EdgePreprocessingSettings::MakeDefaultSettings(); edgeSettings.SetGaussianBlurScale(0.6); edgeSettings.SetRemappingSteepness(0.02); edgeSettings.SetRemappingExponent(2.0); /* The preprocessing is performed when we call the DoEdgePreprocessing method. * the first argument is an EdgePreprocessingSettings object and the second * optional argument is a Command that can be used to implement a progress bar*/ snapData->DoEdgePreprocessing(edgeSettings); /* The result of the preprocessing is stored in the Speed image wrapper in * the SNAPImageData class. We can write out a slice of the preprocessed image */ SpeedImageWrapper *wrapSpeed = snapData->GetSpeed(); SaveImageToFile("speedslice00.mha",wrapSpeed->GetDisplaySlice(0)); /* The next step after preprocessing is to initialize the segmentation with * bubbles. This is performed by passing an array of bubbles to the method * SNAPImageData::InitializeSegmentationPipeline. The bubbles given below * are located in the caudate nuclei of our image */ std::vector bubbles(2); bubbles[0].center = Vector3i(50,29,26); bubbles[0].radius = 3; bubbles[1].center = Vector3i(50,57,34); bubbles[1].radius = 2; /* In addition to the bubble array, the method InitializeSegmentationPipeline * requires a set of level set segmentation parameters, which are stored in * the SnakeParameters class */ SnakeParameters parameters = SnakeParameters::GetDefaultEdgeParameters(); /* In order for the snake to converge, we enable the advection force */ parameters.SetAdvectionWeight(5.0); /* We can now initialize the segmentation pipeline. In addition to the * snake parameters and the bubbles, we need to specify the color label to * be used for the snake-based segmentation */ snapData->InitializeSegmentation( parameters,bubbles,application.GetGlobalState()->GetDrawingColorLabel()); /* Now the pipeline is initialized and ready to run. Run 250 iterations */ snapData->RunSegmentation(250); /* We can also rewind the segmentation */ snapData->RestartSegmentation(); /* And we can change the parameters on the fly */ snapData->RunSegmentation(100); parameters.SetAdvectionWeight(3.0); snapData->SetSegmentationParameters(parameters); snapData->RunSegmentation(200); /* Now the segmentation is done. The Snake image wrapper contains a floating * point image whose positive voxels correspond to the pixels outside of the * segmentation boundary and whose negative valued pixels are inside. In * to get an actual snake surface, we can use the LevelSetMeshPipeline object, * which uses VTK to trace the zero-level-set of the Snake image */ LevelSetMeshPipeline meshPipeline; meshPipeline.SetImage(snapData->GetSnake()->GetImage()); // meshPipeline.SetImage(snapData->GetLevelSetImage()); /* Pass the globally stored mesh options to the mesh pipeline */ meshPipeline.SetMeshOptions(application.GetGlobalState()->GetMeshOptions()); /* Create a vtkPolyData to store the resulting mesh, and run the pipeline */ vtkPolyData *meshResult = vtkPolyData::New(); meshPipeline.ComputeMesh(meshResult); /* Export the mesh to VTK format */ vtkPolyDataWriter *polyWriter = vtkPolyDataWriter::New(); polyWriter->SetInput(meshResult); polyWriter->SetFileTypeToASCII(); polyWriter->SetFileName("levelset.poly"); polyWriter->Write(); polyWriter->Delete(); /* Great. We have now executed automatic segmentation and exported the leve set * as a mesh. What's left is to exit the automatic segmentation mode and to * merge the segmentation results with the multi-label segmentation image * stored in IRISImageData. The following method will do that for us. */ application.UpdateIRISWithSnapImageData(); application.SetCurrentImageDataToIRIS(); /* Since we are done with the segmentation data, we should release resources * associated with it */ application.ReleaseSNAPImageData(); /* ======================================================================= * Section 4. Additional Functionality * ====================================================================== */ /* One of the features of SNAP it to be able to divide segmentations into two * labels using a cut-plane. This can be done programatically, using the * following code. In our example, this will divide the left and the right * caudates, giving them different labels */ /* We will paint with label 9 over label 8 */ application.GetGlobalState()->SetDrawingColorLabel(9); application.GetGlobalState()->SetCoverageMode(PAINT_OVER_COLORS); application.GetGlobalState()->SetOverWriteColorLabel(8); /* This actually performs the cut, given an normal vector and intercept that * define the cut plane */ application.RelabelSegmentationWithCutPlane(Vector3d(1,0,0), 64); /* We can now save the segmentation result as a 3d image */ SaveImageToFile("result.gipl",irisData->GetSegmentation()->GetImage()); /* Thank you for reading this tutorial! */ return 0; } // A global variable required for linkage std::ostream &verbose = std::cout; itksnap/UserInterface/0000755000076500000240000000000011560342171014303 5ustar paulystaffitksnap/UserInterface/BasicComponents/0000755000076500000240000000000011560342171017372 5ustar paulystaffitksnap/UserInterface/BasicComponents/ColorMapBox.cxx0000644000076500000240000000520410735614373022315 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ColorMapBox.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 "ColorMapBox.h" #include "SNAPOpenGL.h" ColorMapBox ::ColorMapBox(int x,int y,int w,int h,const char *label) : FLTKCanvas(x, y, w, h, label) { m_RangeStart = 0.0; m_RangeEnd = 0.0; } void ColorMapBox ::draw() { // The standard 'valid' business if(!valid()) { // Set up the basic projection with a small margin glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-0.005,1.005,-0.05,1.05); glViewport(0,0,w(),h()); // Establish the model view matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } // Clear the viewport glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Push the related attributes glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT); // Disable lighting glDisable(GL_LIGHTING); // Draw the color map glBegin(GL_QUAD_STRIP); // Get the colors unsigned int n = 256; for(unsigned int i = 0; i <= n; i++) { double u = i * 1.0 / n; double t = m_RangeStart + u * (m_RangeEnd - m_RangeStart); SpeedColorMap::OutputType xColor = m_ColorMap(t); glColor3ub(xColor[0], xColor[1], xColor[2]); glVertex2d(u, 0.0); glVertex2d(u, 1.0); } glEnd(); // Pop the attributes glPopAttrib(); // Done glFlush(); } itksnap/UserInterface/BasicComponents/ColorMapBox.h0000644000076500000240000000405210735614373021742 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ColorMapBox.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:15 $ 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 __ColorMapBox_h_ #define __ColorMapBox_h_ #include "FLTKCanvas.h" #include "SpeedColorMap.h" /** * \class ColorMapBox * \brief An FLTK Box used to display a color map */ class ColorMapBox : public FLTKCanvas { public: /** The standard FLTK window constructor */ ColorMapBox(int x,int y,int w,int h,const char *label=NULL); /** Set the current color map functor */ void SetColorMap(const SpeedColorMap &map) { m_ColorMap = map; redraw(); } /** Set the range for the color map */ void SetRange(double x0, double x1) { m_RangeStart = x0; m_RangeEnd = x1; } /** The draw method */ void draw(); private: SpeedColorMap m_ColorMap; double m_RangeStart, m_RangeEnd; }; #endif itksnap/UserInterface/BasicComponents/ColorMapWidget.cxx0000644000076500000240000002621211254242201022772 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ColorMapWidget.cxx,v $ Language: C++ Date: $Date: 2009/09/16 20:03:13 $ Version: $Revision: 1.7 $ 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 "ColorMapWidget.h" #include "SNAPOpenGL.h" #include "LayerInspectorUILogic.h" ColorMapWidget ::ColorMapWidget(int x,int y,int w,int h,const char *label) : FLTKCanvas(x, y, w, h, label) { m_TextureId = 0xffffffff; m_Interactor = new ColorMapInteraction(this); this->PushInteractionMode(m_Interactor); m_Parent = NULL; m_SelectedCMPoint = -1; m_SelectedSide = BOTH; } ColorMapWidget ::~ColorMapWidget() { delete m_Interactor; } void ColorMapWidget ::gl_draw_circle_with_border(double x, double y, double r, bool select) { 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 / this->w(), 1.2 / this->h(), 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(); if(select) glColor3ub(0xff,0x00,0xff); else glColor3ub(0x00,0x00,0x00); glBegin(GL_LINE_LOOP); for(size_t i = 0; i < cx.size(); i++) glVertex2d(r * cx[i], r * cy[i]); glEnd(); glPopMatrix(); } bool ColorMapWidget ::SetSelection(int pt, Side side) { bool changed = (m_SelectedCMPoint != pt) || (m_SelectedSide != side); m_SelectedCMPoint = pt; m_SelectedSide = side; if(changed) { this->redraw(); m_Parent->OnColorMapSelectedPointUpdate(); } return changed; } void ColorMapWidget ::SetSelectedCMPoint(int pt) { this->SetSelection(pt, m_SelectedSide); } void ColorMapWidget ::SetSelectedSide(Side side) { this->SetSelection(m_SelectedCMPoint, side); } void ColorMapWidget ::draw() { // The standard 'valid' business if(!valid()) { // Set up the basic projection with a small margin glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-0.1,1.1,-0.1,1.1); glViewport(0,0,w(),h()); // Establish the model view matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } // Clear the viewport glClearColor(1.0, 1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Push the related attributes glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_TEXTURE_BIT); // Disable lighting glEnable(GL_TEXTURE_2D); // Generate the texture for the background const GLsizei boxw = 16; if(m_TextureId == 0xffffffff) { // Create a checkerboard unsigned char *boxarr = new unsigned char[boxw * boxw], *p = boxarr; for(GLsizei i = 0; i < boxw; i++) for(GLsizei j = 0; j < boxw; j++) if((i < boxw / 2 && j < boxw / 2) || (i > boxw / 2 && j > boxw / 2)) *p++ = 0xef; else *p++ = 0xff; glGenTextures(1, &m_TextureId); glBindTexture(GL_TEXTURE_2D, m_TextureId); // Properties for the texture glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); // Turn off modulo-4 rounding in GL glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, boxw, boxw, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, boxarr); delete boxarr; } // Draw the background pattern glBindTexture(GL_TEXTURE_2D, m_TextureId); glColor3ub(0xff,0xff,0xff); glBegin(GL_QUADS); double tx = this->w() * 1.0 / boxw; double ty = this->h() * 1.0 / boxw; glTexCoord2d(0.0, 0.0); glVertex2d(-0.1, -0.1); glTexCoord2d(tx, 0.0); glVertex2d(1.1, -0.1); glTexCoord2d(tx, ty); glVertex2d(1.1, 1.1); glTexCoord2d(0.0, ty); glVertex2d(-0.1, 1.1); glEnd(); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); // Turn on alpha blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // Get the starting and ending color values ColorMap::RGBAType v0 = m_ColorMap.MapIndexToRGBA(-0.1); ColorMap::RGBAType v1 = m_ColorMap.MapIndexToRGBA(1.1); // Draw the actual texture glBegin(GL_QUADS); // Draw the color before 0.0 glColor4ub(v0[0], v0[1], v0[2], 0x00); glVertex2d(-0.1,0.0); glColor4ub(v0[0], v0[1], v0[2], 0xff); glVertex2d(-0.1,1.0); /* // Draw each of the points for(size_t i = 0; i < m_ColorMap.GetNumberOfCMPoints(); i++) { ColorMap::CMPoint p = m_ColorMap.GetCMPoint(i); glColor4ub(p.m_RGBA[0][0], p.m_RGBA[0][1], p.m_RGBA[0][2], 0xff); glVertex2d(p.m_Index, 1.0); glColor4ub(p.m_RGBA[0][0], p.m_RGBA[0][1], p.m_RGBA[0][2], 0x00); glVertex2d(p.m_Index, 0.0); glColor4ub(p.m_RGBA[1][0], p.m_RGBA[1][1], p.m_RGBA[1][2], 0x00); glVertex2d(p.m_Index, 0.0); glColor4ub(p.m_RGBA[1][0], p.m_RGBA[1][1], p.m_RGBA[1][2], 0xff); glVertex2d(p.m_Index, 1.0); } */ for(size_t i = 0; i < 512; i++) { double t = i / 511.0; ColorMap::RGBAType rgba = m_ColorMap.MapIndexToRGBA(t); glColor4ub(rgba[0], rgba[1], rgba[2], 0xff); glVertex2d(t, 1.0); glColor4ub(rgba[0], rgba[1], rgba[2], 0x00); glVertex2d(t, 0.0); glColor4ub(rgba[0], rgba[1], rgba[2], 0x00); glVertex2d(t, 0.0); glColor4ub(rgba[0], rgba[1], rgba[2], 0xff); glVertex2d(t, 1.0); } // Draw the color after 1.0 glColor4ub(v1[0], v1[1], v1[2], 0xff); glVertex2d(1.1,1.0); glColor4ub(v1[0], v1[1], v1[2], 0x00); glVertex2d(1.1,0.0); // Done drawing glEnd(); // Draw the gridlines glBegin(GL_LINES); glColor4ub(0x80, 0x80, 0x80, 0xff); // Horizontal gridlines glVertex2d(-0.1, 0.0); glVertex2d( 1.1, 0.0); glVertex2d(-0.1, 0.5); glVertex2d( 1.1, 0.5); glVertex2d(-0.1, 1.0); glVertex2d( 1.1, 1.0); // Vertical gridlines glVertex2d(0.0, -0.1); glVertex2d(0.0, 1.1); glVertex2d(0.25, -0.1); glVertex2d(0.25, 1.1); glVertex2d(0.5, -0.1); glVertex2d(0.5, 1.1); glVertex2d(0.75, -0.1); glVertex2d(0.75, 1.1); glVertex2d(1.0, -0.1); glVertex2d(1.0, 1.1); glEnd(); // Draw the Alpha curve glEnable(GL_LINE_SMOOTH); glLineWidth(3.0); glBegin(GL_LINE_STRIP); glColor4ub(0x00,0x00,0x00,0xff); glVertex2d(-0.1, v0[3] / 255.0); for(size_t i = 0; i < m_ColorMap.GetNumberOfCMPoints(); i++) { ColorMap::CMPoint p = m_ColorMap.GetCMPoint(i); glVertex2d(p.m_Index, p.m_RGBA[0][3] / 255.0); glVertex2d(p.m_Index, p.m_RGBA[1][3] / 255.0); } glVertex2d(1.1, v1[3] / 255.0); glEnd(); // Draw circles around the points glLineWidth(1.0); for(size_t i = 0; i < m_ColorMap.GetNumberOfCMPoints(); i++) { ColorMap::CMPoint p = m_ColorMap.GetCMPoint(i); glColor3ub(p.m_RGBA[0][0],p.m_RGBA[0][1],p.m_RGBA[0][2]); bool select = ((int)i == m_SelectedCMPoint) && (m_SelectedSide != RIGHT); gl_draw_circle_with_border(p.m_Index, p.m_RGBA[0][3] / 255.0, 5.0, select); if(p.m_RGBA[0][3] != p.m_RGBA[1][3]) { glColor3ub(p.m_RGBA[1][0],p.m_RGBA[1][1],p.m_RGBA[1][2]); bool select = ((int)i == m_SelectedCMPoint) && (m_SelectedSide != LEFT); gl_draw_circle_with_border(p.m_Index, p.m_RGBA[1][3] / 255.0, 5.0, select); } } // Pop the attributes glPopAttrib(); // Done glFlush(); } int ColorMapInteraction ::OnMousePress(const FLTKEvent &e) { // Reference to the color map ColorMap &cm = m_Parent->m_ColorMap; // 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(e.XSpace[0] - p.m_Index); double dy0 = fabs(e.XSpace[1] - p.m_RGBA[0][3] / 255.0); double dy1 = fabs(e.XSpace[1] - p.m_RGBA[1][3] / 255.0); if(dx / 1.2 < 5.0 / m_Parent->w()) { if(dy0 / 1.2 < 5.0 / m_Parent->h()) { // We return 0 when the selected point changes to avoid dragging if(p.m_Type == ColorMap::CONTINUOUS) m_Parent->SetSelection(i, ColorMapWidget::BOTH); else m_Parent->SetSelection(i, ColorMapWidget::LEFT); return 1; } else if (dy1 / 1.2 < 5.0 / m_Parent->h()) { m_Parent->SetSelection(i, ColorMapWidget::RIGHT); return 1; } } } // No selection has been made, so we insert a new point if(e.XSpace[0] > 0.0 && e.XSpace[0] < 1.0) { size_t sel = cm.InsertInterpolatedCMPoint(e.XSpace[0]); m_Parent->SetSelection(sel, ColorMapWidget::BOTH); return 1; } return 0; } int ColorMapInteraction ::OnMouseDrag(const FLTKEvent &e, const FLTKEvent &pe) { // Reference to the color map ColorMap &cm = m_Parent->m_ColorMap; int isel = m_Parent->m_SelectedCMPoint; // Nothing happens if zero is selected if(isel < 0) return 0; ColorMap::CMPoint psel = cm.GetCMPoint(isel); // Get the new alpha and index double j = e.XSpace[0]; double a = e.XSpace[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(m_Parent->m_SelectedSide != ColorMapWidget::RIGHT) psel.m_RGBA[0][3] = (unsigned char) a; if(m_Parent->m_SelectedSide != ColorMapWidget::LEFT) psel.m_RGBA[1][3] = (unsigned char) a; // Redraw cm.UpdateCMPoint(isel, psel); m_Parent->redraw(); // Fire the update event m_Parent->GetParent()->OnColorMapChange(); return 1; } int ColorMapInteraction ::OnMouseRelease(const FLTKEvent &e, const FLTKEvent &pe) { return 1; // return 0; // this->OnMouseDrag(e,pe); } itksnap/UserInterface/BasicComponents/ColorMapWidget.h0000644000076500000240000000616511254242201022424 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ColorMapWidget.h,v $ Language: C++ Date: $Date: 2009/09/16 20:03:13 $ Version: $Revision: 1.6 $ 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 __ColorMapWidget_h_ #define __ColorMapWidget_h_ #include "FLTKCanvas.h" #include "ColorMap.h" class LayerInspectorUILogic; class ColorMapInteraction; /** * \class ColorMapWidget * \brief An FLTK Box used to display a color map */ class ColorMapWidget : public FLTKCanvas { public: /** The standard FLTK window constructor */ ColorMapWidget(int x,int y,int w,int h,const char *label=NULL); virtual ~ColorMapWidget(); /** Set the current color map functor */ void SetColorMap(const ColorMap &map) { m_ColorMap = map; redraw(); } /** Get the current color map functor */ ColorMap &GetColorMap() { return m_ColorMap; } // Get/set the parent object irisGetMacro(Parent,LayerInspectorUILogic *); irisSetMacro(Parent,LayerInspectorUILogic *); /** The draw method */ void draw(); enum Side {LEFT, RIGHT, BOTH}; irisGetMacro(SelectedSide, Side); irisGetMacro(SelectedCMPoint, int); void SetSelectedCMPoint(int pt); void SetSelectedSide(Side side); /** Sets selected point and side, returns true if changed */ bool SetSelection(int pt, Side side); private: ColorMap m_ColorMap; unsigned int m_TextureId; int m_SelectedCMPoint; Side m_SelectedSide; void gl_draw_circle_with_border(double x, double y, double r, bool select); /** Parent object */ LayerInspectorUILogic *m_Parent; ColorMapInteraction *m_Interactor; friend class ColorMapInteraction; }; class ColorMapInteraction : public InteractionMode { public: ColorMapInteraction(ColorMapWidget *parent) : InteractionMode(parent) { this->m_Parent = parent; } int OnMousePress(const FLTKEvent &e); int OnMouseDrag(const FLTKEvent &e, const FLTKEvent &pe); int OnMouseRelease(const FLTKEvent &e, const FLTKEvent &pe); private: ColorMapWidget *m_Parent; }; #endif itksnap/UserInterface/BasicComponents/CVS/0000755000076500000240000000000011560342171020025 5ustar paulystaffitksnap/UserInterface/BasicComponents/CVS/Entries0000644000076500000240000000301411560342171021357 0ustar paulystaff/ColorMapBox.cxx/1.2/Sun Dec 30 04:05:15 2007// /ColorMapBox.h/1.2/Sun Dec 30 04:05:15 2007// /ColorMapWidget.cxx/1.7/Wed Sep 16 20:03:13 2009// /ColorMapWidget.h/1.6/Wed Sep 16 20:03:13 2009// /FLTKCanvas.cxx/1.8/Tue Oct 19 20:28:56 2010// /FLTKCanvas.h/1.2/Sun Dec 30 04:05:16 2007// /FLTKEvent.h/1.3/Tue Oct 19 20:28:56 2010// /FLTKWidgetActivationManager.h/1.5/Sat Nov 15 12:20:38 2008// /FunctionPlot2D.cxx/1.2/Sun Dec 30 04:05:16 2007// /FunctionPlot2D.h/1.2/Sun Dec 30 04:05:16 2007// /FunctionPlot2DBox.cxx/1.2/Sun Dec 30 04:05:16 2007// /FunctionPlot2DBox.h/1.2/Sun Dec 30 04:05:16 2007// /IntensityCurveBox.cxx/1.8/Thu Jun 3 19:25:32 2010// /IntensityCurveBox.h/1.4/Thu Jun 3 19:25:32 2010// /InteractionMode.h/1.3/Fri Apr 16 04:02:35 2010// /InteractionModeClient.cxx/1.2/Sun Dec 30 04:05:16 2007// /InteractionModeClient.h/1.1/Sat Dec 2 04:22:21 2006// /MetaDataTable.cxx/1.4/Mon Jun 28 18:45:08 2010// /MetaDataTable.h/1.2/Thu Jun 3 12:31:27 2010// /RecursiveInteractionMode.cxx/1.2/Sun Dec 30 04:05:16 2007// /RecursiveInteractionMode.h/1.1/Sat Dec 2 04:22:21 2006// /SNAPFormattedOutput.h/1.3/Tue Jun 15 16:27:44 2010// /SNAPValueOutput.h/1.2/Tue Jun 15 16:27:44 2010// /SnakeParametersPreviewBox.cxx/1.6/Tue Oct 19 19:15:14 2010// /SnakeParametersPreviewBox.h/1.3/Thu Jul 16 22:02:27 2009// /SnakeParametersPreviewPipeline.cxx/1.6/Tue Oct 19 19:15:14 2010// /SnakeParametersPreviewPipeline.h/1.4/Fri Jan 23 20:09:38 2009// /StatisticsTable.cxx/1.1/Mon May 31 19:52:37 2010// /StatisticsTable.h/1.1/Mon May 31 19:52:37 2010// D itksnap/UserInterface/BasicComponents/CVS/Repository0000644000076500000240000000004611560342171022127 0ustar paulystaffitksnap/UserInterface/BasicComponents itksnap/UserInterface/BasicComponents/CVS/Root0000644000076500000240000000010011560342171020662 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/BasicComponents/FLTKCanvas.cxx0000644000076500000240000001366311457377610022036 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: FLTKCanvas.cxx,v $ Language: C++ Date: $Date: 2010/10/19 20:28:56 $ Version: $Revision: 1.8 $ 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 #include #include "FLTKCanvas.h" #include #include "SNAPOpenGL.h" #include "GLToPNG.h" using namespace std; FLTKCanvas ::FLTKCanvas(int x, int y, int w, int h, const char *label) : Fl_Gl_Window(x,y,w,h,label) { m_Dragging = false; m_FlipYCoordinate = true; m_GrabFocusOnEntry = false; m_Focus = false; m_DumpPNG = NULL; } void FLTKCanvas ::SaveAsPNG(const char *filename) { m_DumpPNG = filename; this->redraw(); Fl::flush(); m_DumpPNG = NULL; } int FLTKCanvas ::handle(int eventID) { // Create an event object FLTKEvent event; event.Id = eventID; // Note that the y coordinate is optionally reflected event.XCanvas[0] = Fl::event_x(); event.XCanvas[1] = m_FlipYCoordinate ? h()-1-Fl::event_y() : Fl::event_y(); // Get the event timestamp event.Source = this; event.Button = Fl::event_button(); event.State = Fl::event_state(); event.Key = Fl::event_key(); // Construct the software button if(event.Button == FL_LEFT_MOUSE && (event.State & FL_ALT)) event.SoftButton = FL_RIGHT_MOUSE; else if(event.Button == FL_LEFT_MOUSE && (event.State & FL_CTRL)) event.SoftButton = FL_MIDDLE_MOUSE; else if(event.Button == FL_RIGHT_MOUSE && (event.State & FL_CTRL)) event.SoftButton = FL_MIDDLE_MOUSE; else event.SoftButton = event.Button; // If the window has not been displayed, we can't compute the // object space coordinates if (shown() && visible()) { // Make this window the current context make_current(); // Convert the event coordinates into the model view coordinates double modelMatrix[16], projMatrix[16]; GLint viewport[4]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); glGetDoublev(GL_PROJECTION_MATRIX, projMatrix); glGetIntegerv(GL_VIEWPORT,viewport); // Projection works with doubles, event is a float Vector3d xProjection; gluUnProject(event.XCanvas[0],event.XCanvas[1],0, modelMatrix,projMatrix,viewport, &xProjection[0],&xProjection[1],&xProjection[2]); event.XSpace = to_float(xProjection); } // Record the event as the drag-start if the mouse was pressed if (eventID == FL_PUSH) { // Debug positions m_DragStartEvent = event; m_Dragging = true; } else if(eventID == FL_RELEASE) { m_Dragging = false; } // Propagate the event through the stack for (list::iterator it = m_Interactors.begin(); it != m_Interactors.end();it++) { InteractionMode *mode = *it; int result; // Delegate the event base on the ID switch (eventID) { case FL_PUSH : result = mode->OnMousePress(event); break; case FL_DRAG : result = mode->OnMouseDrag(event,m_DragStartEvent); break; case FL_RELEASE : result = mode->OnMouseRelease(event,m_DragStartEvent); break; case FL_ENTER : result = mode->OnMouseEnter(event); break; case FL_LEAVE : result = mode->OnMouseLeave(event); break; case FL_MOVE : result = mode->OnMouseMotion(event); break; case FL_MOUSEWHEEL : result = mode->OnMouseWheel(event); break; case FL_KEYDOWN : result = mode->OnKeyDown(event); break; case FL_KEYUP : result = mode->OnKeyUp(event); break; case FL_SHORTCUT : result = mode->OnShortcut(event); break; case FL_DND_ENTER : case FL_DND_LEAVE : case FL_DND_DRAG : case FL_DND_RELEASE : case FL_PASTE : result = mode->OnDragAndDrop(event); break; default: result = mode->OnOtherEvent(event); }; // Break out if the event was taken care of if (result) return 1; } // Take care of focus grabbing on entry if(eventID == FL_ENTER && m_GrabFocusOnEntry) { this->take_focus(); m_Focus = true; redraw(); return 1; } else if(eventID == FL_LEAVE && m_GrabFocusOnEntry) { m_Focus = false; redraw(); return 1; } else if(eventID == FL_FOCUS && m_GrabFocusOnEntry) { // m_Focus = true; return 1; } else if(eventID == FL_UNFOCUS && m_GrabFocusOnEntry) { // m_Focus = false; return 1; } // The event was not handled return 0; } void FLTKCanvas ::draw() { // Let the children do the drawing FireInteractionDrawEvent(); // dump png if requested if (m_DumpPNG != NULL) { vtkImageData* img = GLToVTKImageData((unsigned int) GL_RGBA, 0, 0, w(), h()); VTKImageDataToPNG(img, m_DumpPNG); img->Delete(); } } itksnap/UserInterface/BasicComponents/FLTKCanvas.h0000644000076500000240000000711510735614374021455 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: FLTKCanvas.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:16 $ 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 __FLTKCanvas_h_ #define __FLTKCanvas_h_ #include #include "FLTKEvent.h" #include "InteractionMode.h" #include "InteractionModeClient.h" #include /** * \class FLTKCanvas * \brief An extension of Fl_Gl_Window with advanced interaction handling. * * This is an extension of the Fl_Gl_Window from FLTK that allows * the concept of interaction modes to be used. InteractionModes * are event handlers that are associated with different ways of user * interaction with the window. This is used with toolbars, where different * toolbar buttons change the behavior of the interaction. * * Multiple interaction modes can be used simultaneously in a stack. The top mode * in the stack is the first to receive events, and the event is propagated through * the stack until it has been handled properly. */ class FLTKCanvas : public Fl_Gl_Window, public InteractionModeClient { public: /** Constructor sets up some basics, sets interaction mode stack to be empty */ FLTKCanvas(int x, int y, int w, int h, const char *label); virtual ~FLTKCanvas() {} /** Handle events */ virtual int handle(int eventID); /** Default drawing handler */ virtual void draw(); /** Are we dragging ? */ irisIsMacro(Dragging); /** Are we flipping? */ irisSetMacro(FlipYCoordinate,bool); /** Check if keyboard focus is grabbed when the mouse enters the window */ irisGetMacro(GrabFocusOnEntry,bool); /** Set whether keyboard focus is grabbed when the mouse enters the window */ irisSetMacro(GrabFocusOnEntry,bool); /** Check if the window has keyboard focus */ irisGetMacro(Focus,bool); /** Save the window content to a PNG file */ void SaveAsPNG(const char *filename); private: // The event at the start of a drag operation (if there is one going on) FLTKEvent m_DragStartEvent; // Are we dragging or not bool m_Dragging; // Should we flip the Y coordinates of the events (top = 0, bottom = h) bool m_FlipYCoordinate; /** Whether the keyboard focus is grabbed when the mouse enters the window */ bool m_GrabFocusOnEntry; /** Whether the window has keyboard focus */ bool m_Focus; /** PNG filename to save on the next draw command */ const char *m_DumpPNG; }; #endif // __FLTKCanvas_h_ itksnap/UserInterface/BasicComponents/FLTKEvent.h0000644000076500000240000000573511457377610021332 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: FLTKEvent.h,v $ Language: C++ Date: $Date: 2010/10/19 20:28:56 $ 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 __FLTKEvent_h_ #define __FLTKEvent_h_ #include #include #include "SNAPCommonUI.h" class FLTKCanvas; extern void fl_gettime(long *sec, long *usec); struct FLTKEventTimeStamp { long sec, usec; FLTKEventTimeStamp() { fl_gettime(&sec, &usec); } long ElapsedUSecFrom(const FLTKEventTimeStamp &earlier) { return 1000000 * (sec - earlier.sec) + (usec - earlier.usec); } }; /** * \class FLTKEvent * \brief A wrapper around FLTK event info. * * \sa FLTKCanvas */ struct FLTKEvent { /** The FLTK id of the event */ int Id; /** The cursor position of the event in window coordinates */ Vector2i XCanvas; /** The cursor position of the event in object space coordinates */ Vector3f XSpace; /** Time when the event was generated (value of the clock() function) */ FLTKEventTimeStamp TimeStamp; /** The button that generated this event */ int Button; /** * The simulated event mouse button. If user presses CTRL-LEFT, this will * have the value FL_MIDDLE_MOUSE and when the user presses ALT-LEFT, this * will have the value FL_RIGHT_MOUSE. Useful for Macs and people without * a middle mouse button */ int SoftButton; /** The state of the interface (ALT,CTRL,SHIFT) */ int State; /** The key associated with the event */ int Key; /** * Pointer to the FLTKCanvas that generated this event. This is a safety * measure because the recipient of these events should already have such * a pointer to the specific subclass of FLTKCanvas */ FLTKCanvas *Source; }; #endif // __FLTKEvent_h_ itksnap/UserInterface/BasicComponents/FLTKWidgetActivationManager.h0000644000076500000240000002264011107537226024774 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: FLTKWidgetActivationManager.h,v $ Language: C++ Date: $Date: 2008/11/15 12:20:38 $ 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 _FLTKWidgetActivationManager_h_ #define _FLTKWidgetActivationManager_h_ #include "FL/Fl_Widget.H" #include "FL/Fl_Menu_Item.H" #include "FL/Fl_Check_Button.H" #include #include #include #include /** * \class WidgetActivationManager * \brief A generic state machine for widget management * \see FLTKWidgetActivationManager */ template class WidgetActivationManager { public: /** Observer, used to callback when a flag changes */ class Observer { public: virtual ~Observer() {}; virtual void OnStateChange(TFlag flag, bool value) = 0; }; /** * With default parameters, this call specifies that flag A implies flag B. * Using the optional parameters, you can specify A->!B, !A->B or !A->!B. * Read: A in stateA implies B is in stateB * */ void SetFlagImplies(TFlag A, TFlag B, bool stateA=true, bool stateB=true) { // Apply the forward rule m_Flags[A].Rules[ stateA ? 1 : 0 ].push_back( Rule(B, stateB) ); // Apply the reverse rule m_Flags[B].Rules[ stateB ? 0 : 1 ].push_back( Rule(A, !stateA) ); } /** * This is a quick way to set up a set of flags that are mutually exclusive * (but all the flags may be set to false at once) */ void SetFlagsMutuallyExclusive( int nFlags, TFlag *F ) { // For each flag, set up a rule to all the other flags for(unsigned int i = 0; i < nFlags; i++) for(unsigned int j = 0; j < i; j++) SetFlagImplies( F[i], F[j], true, false ); } /** Set up mutual exclusion between three flags */ void SetFlagsMutuallyExclusive( TFlag A, TFlag B, TFlag C ) { SetFlagImplies( A, B, true, false ); SetFlagImplies( A, C, true, false ); SetFlagImplies( B, C, true, false ); } /** Get the value of a flag */ bool GetFlag(TFlag flag) { return m_Flags[flag].State; } /** * Add a listener/observer to a flag. When the flag is changed, the observer * is executed with the pointer to the flag that has been changed and its new * value */ void AddObserver(Observer *observer, TFlag flag) { m_Flags[flag].Observers.insert(observer); } /** * Update a flag to a new value. Ignore the third parameter, it * is used internally to throw an exception if the flag machine * enters an infinite loop */ void UpdateFlag(TFlag flag, bool value, int recursionCount = 0) { // Do nothing if the flag already has the given value if(m_Flags[flag].State == value) return; // Throw an exception if the recursion depth is too great if(recursionCount > static_cast(m_Flags.size())) throw std::string("Infinite loop in the flag state machine!"); // Get the flag data for this flag FlagData &fd = m_Flags[flag]; // Update the flag fd.State = value; // Trigger all the rules for this flag. We do this before updating the widgets // so that the settings related to the actual flag being updated are applied last typename std::list::iterator itRule = fd.Rules[ value ? 1 : 0 ].begin(); while( itRule != fd.Rules[ value ? 1 : 0 ].end() ) { UpdateFlag( itRule->Target, itRule->Value, recursionCount + 1 ); ++itRule; } // Activate all the widgets tied to this flag typename std::set::iterator itWidget = fd.Widgets.begin(); while( itWidget != fd.Widgets.end() ) (*itWidget++)->OnStateChange(value); // Fire all the observers typename std::set::iterator itObserver = fd.Observers.begin(); while( itObserver != fd.Observers.end() ) (*itObserver++)->OnStateChange(flag, value); } /** Destructor, deletes widget wrappers */ virtual ~WidgetActivationManager() { typename std::set::iterator itWidget = m_AllWidgets.begin(); while(itWidget != m_AllWidgets.end()) delete *(itWidget++); } /** * A class used to control the state of an FLTK widget */ class WidgetWrapper { public: virtual ~WidgetWrapper() {} virtual void OnStateChange(bool newState) = 0; }; protected: /** * A rule relating one flag to another. The rule has a target and a * value. When a rule is triggered, it set's the target flag to the value */ struct Rule { // The target of the rule TFlag Target; // The value the rule assigns to the target bool Value; // A constructor Rule(TFlag target, bool value) : Target(target), Value(value) {} }; /** * A structure associated with a flag */ struct FlagData { // A list of rules that are triggered when the flag is set to true // and false states std::list Rules[2]; // A list of widget controllers that are affected by the flag's state std::set Widgets; // A list of generic observers for this widget std::set Observers; // The state of the menu item bool State; // Constructor FlagData() { State = false; } }; /** * Add a controlled widget to a flag. The wrapper will be deleted when * the destructor is called, so don't call delete! */ void AddWidgetWrapper(WidgetWrapper *widget, TFlag flag) { m_Flags[flag].Widgets.insert(widget); m_AllWidgets.insert(widget); widget->OnStateChange(false); } private: // A list of flags and associated flag data std::map m_Flags; // A list of widget wrappers, to be deleted std::set m_AllWidgets; }; /** * A wrapper for a generic FLTK widget or menu item, calls activate * or deactivate depending on the state */ template class GenericWidgetWrapper : public WidgetActivationManager::WidgetWrapper { public: virtual ~GenericWidgetWrapper() {} // State change method virtual void OnStateChange(bool newState) { if(newState) m_Widget->activate(); else m_Widget->deactivate(); } // Constructor GenericWidgetWrapper(TWidget *w) : m_Widget(w) {}; protected: TWidget *m_Widget; }; /** * A special wrapper for FLTK widgets that can be assigned a value. It * can assign a default value to the widget when it's deactivated and when * it's activated */ template class ValuatorWidgetWrapper : public GenericWidgetWrapper { public: virtual ~ValuatorWidgetWrapper() {} // State change method virtual void OnStateChange(bool newState) { GenericWidgetWrapper::OnStateChange(newState); this->m_Widget->value( newState ? m_OnValue : m_OffValue ); } // Constructor ValuatorWidgetWrapper(TWidget *w, const TValue &on, const TValue &off) : GenericWidgetWrapper(w), m_OffValue(off), m_OnValue(on) {} protected: TValue m_OffValue, m_OnValue; }; /** * \class FLWidgetActivationManager * \brief Simplifies management of widget on/off status * This class can link the on-off status of FLTK widgets and menu items * with a set of flags in a 'state machine'. The flags can be related to * each other, for example, a flag can imply another flag or a set of * flags can be mutually exclusive */ template class FLTKWidgetActivationManager : public WidgetActivationManager { public: /** Associate an FLTK widget with a flag */ void AddWidget(Fl_Widget *widget, TFlag flag) { typedef GenericWidgetWrapper WrapperType; AddWidgetWrapper(new WrapperType(widget), flag); } /** Associate an FLTK menu item with a flag */ void AddMenuItem(Fl_Menu_Item *menu, TFlag flag) { typedef GenericWidgetWrapper WrapperType; AddWidgetWrapper(new WrapperType(menu), flag); } /** Associate an FLTK checkbox with a flag and two values */ void AddCheckBox(Fl_Check_Button *cb, TFlag flag, bool onState, bool offState) { typedef ValuatorWidgetWrapper WrapperType; AddWidgetWrapper(new WrapperType(cb,onState ? 1 : 0,offState ? 1 : 0), flag); } protected: }; #endif itksnap/UserInterface/BasicComponents/FunctionPlot2D.cxx0000644000076500000240000001077110735614374022750 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: FunctionPlot2D.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:16 $ 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 "FunctionPlot2D.h" #include "FL/gl.h" FunctionPlot2D ::FunctionPlot2D() { m_Settings = FunctionPlot2DSettings::GetDefaultSettings(); } void FunctionPlot2D ::SetDataPoints(float *xPoints,float *yPoints,unsigned int nPoints) { // Intialize the array m_Points.clear(); m_Points.reserve(nPoints); // Fill the array for(unsigned int i=0;idata_block()); } glEnd(); // Restore the model matrix glPopMatrix(); // Restore the attributes glPopAttrib(); } FunctionPlot2DSettings FunctionPlot2DSettings ::GetDefaultSettings() { FunctionPlot2DSettings settings; settings.m_PlotRangeMin = Vector2f(-1.0f); settings.m_PlotRangeMax = Vector2f(1.0f); settings.m_AxesPosition = Vector2f(0.0f); settings.m_MajorTickSpacing = Vector2f(0.5f); settings.m_MinorTickSpacing = Vector2f(0.1f); settings.m_ShowAxes = true; settings.m_ShowFrame = true; settings.m_ShowMajorTicks = true; settings.m_ShowMinorTicks = true; settings.m_AxesColor = Vector3f(0.0f); settings.m_FrameColor = Vector3f(0.0f); settings.m_PlotColor = Vector3f(1.0f,0.0f,0.0f); settings.m_BackgroundColor = Vector3f(1.0f); return settings; } itksnap/UserInterface/BasicComponents/FunctionPlot2D.h0000644000076500000240000000736410735614374022401 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: FunctionPlot2D.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:16 $ 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 __FunctionPlot2D_h_ #define __FunctionPlot2D_h_ #include "SNAPCommonUI.h" #include /** * Settings for plotting 2D functions. Based on Mathematica options * to the Plot command (a small subset, of course). */ class FunctionPlot2DSettings { public: virtual ~FunctionPlot2DSettings() {} ; irisGetMacro(PlotRangeMin,Vector2f); irisSetMacro(PlotRangeMin,Vector2f); irisGetMacro(PlotRangeMax,Vector2f); irisSetMacro(PlotRangeMax,Vector2f); irisGetMacro(AxesPosition,Vector2f); irisSetMacro(AxesPosition,Vector2f); irisGetMacro(MajorTickSpacing,Vector2f); irisSetMacro(MajorTickSpacing,Vector2f); irisGetMacro(MinorTickSpacing,Vector2f); irisSetMacro(MinorTickSpacing,Vector2f); irisGetMacro(ShowAxes,bool); irisSetMacro(ShowAxes,bool); irisGetMacro(ShowFrame,bool); irisSetMacro(ShowFrame,bool); irisGetMacro(ShowMajorTicks,bool); irisSetMacro(ShowMajorTicks,bool); irisGetMacro(ShowMinorTicks,bool); irisSetMacro(ShowMinorTicks,bool); irisGetMacro(AxesColor,Vector3f); irisSetMacro(AxesColor,Vector3f); irisGetMacro(FrameColor,Vector3f); irisSetMacro(FrameColor,Vector3f); irisGetMacro(PlotColor,Vector3f); irisSetMacro(PlotColor,Vector3f); irisGetMacro(BackgroundColor,Vector3f); irisSetMacro(BackgroundColor,Vector3f); static FunctionPlot2DSettings GetDefaultSettings(); private: Vector2f m_PlotRangeMin; Vector2f m_PlotRangeMax; Vector2f m_AxesPosition; Vector2f m_MajorTickSpacing; Vector2f m_MinorTickSpacing; bool m_ShowAxes; bool m_ShowFrame; bool m_ShowMajorTicks; bool m_ShowMinorTicks; Vector3f m_AxesColor; Vector3f m_FrameColor; Vector3f m_PlotColor; Vector3f m_BackgroundColor; }; /** * \class FunctionPlot2D * \brief A UI component for plotting 2D graphs using GL functions. */ class FunctionPlot2D { public: /** Get a reference to the settings in this object */ FunctionPlot2DSettings &GetSettings() { return m_Settings; } /** Set the data to be plotted */ void SetDataPoints(float *xPoints,float *yPoints,unsigned int nPoints); /** * Draw the function in the standard OpenGL context. The method will place * the plot inside the region ((0,0),(1,1)). */ void Draw(); /** Constructor, takes on default plot settings */ FunctionPlot2D(); private: typedef std::vector PointVector; FunctionPlot2DSettings m_Settings; PointVector m_Points; }; #endif itksnap/UserInterface/BasicComponents/FunctionPlot2DBox.cxx0000644000076500000240000000443110735614374023415 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: FunctionPlot2DBox.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:16 $ 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 "FunctionPlot2DBox.h" #include "SNAPOpenGL.h" FunctionPlot2DBox ::FunctionPlot2DBox(int x,int y,int w,int h,const char *label) : FLTKCanvas(x,y,w,h,label) { } void FunctionPlot2DBox ::draw() { // The standard 'valid' business if(!valid()) { // Set up the basic projection with a small margin glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-0.05,1.05,-0.05,1.05); glViewport(0,0,w(),h()); // Establish the model view matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } // Clear the viewport glClearColor(0.906f,0.906f,0.906f,1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Push the related attributes glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT); // Disable lighting glDisable(GL_LIGHTING); // Call the plotting routine m_Plotter.Draw(); // Pop the attributes glPopAttrib(); // Done glFlush(); } itksnap/UserInterface/BasicComponents/FunctionPlot2DBox.h0000644000076500000240000000370410735614374023044 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: FunctionPlot2DBox.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:16 $ 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 __FunctionPlot2DBox_h_ #define __FunctionPlot2DBox_h_ #include "FunctionPlot2D.h" #include "FLTKCanvas.h" /** * \class FunctionPlot2DBox * \brief An FLTK Box used for drawing a plot using the above object */ class FunctionPlot2DBox : public FLTKCanvas { public: /** The standard FLTK window constructor */ FunctionPlot2DBox(int x,int y,int w,int h,const char *label=NULL); /** Access the plotting object associated with this window */ FunctionPlot2D &GetPlotter() { return m_Plotter; } /** The draw method */ void draw(); private: FunctionPlot2D m_Plotter; }; #endif itksnap/UserInterface/BasicComponents/IntensityCurveBox.cxx0000644000076500000240000003674111402000654023565 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IntensityCurveBox.cxx,v $ Language: C++ Date: $Date: 2010/06/03 19:25:32 $ Version: $Revision: 1.8 $ 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 "IntensityCurveBox.h" #include "LayerInspectorUILogic.h" #include "GreyImageWrapper.h" #include "itkImageRegionConstIterator.h" #include #include #include #include #include #include "SNAPOpenGL.h" #include using namespace std; unsigned int IntensityCurveBox::CURVE_RESOLUTION = 64; IntensityCurveBox ::IntensityCurveBox(int x, int y, int w, int h, const char *label) : FLTKCanvas(x,y,w,h,label) { // Start with the blank curve m_Curve = NULL; m_Parent = NULL; // Initialize the histogram parameters m_HistogramBinSize = 1; m_HistogramMaxLevel = 1.0; m_HistogramLog = false; // Set up the interactor m_Interactor = new IntensityCurveInteraction(this); PushInteractionMode(m_Interactor); } IntensityCurveBox ::~IntensityCurveBox() { delete m_Interactor; } void IntensityCurveBox ::draw() { // The curve should have been initialized assert(m_Curve); if (!valid()) { // Set up the basic projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-0.025,1.025,-0.05,1.05); glViewport(0,0,w(),h()); // Establish the model view matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); GLdouble modelMatrix[16], projMatrix[16]; GLint viewport[4]; glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); glGetDoublev(GL_PROJECTION_MATRIX, projMatrix); glGetIntegerv(GL_VIEWPORT,viewport); } // Clear the viewport glClearColor(.95,.95,.95,1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Push the related attributes glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT); // Disable lighting glDisable(GL_LIGHTING); // Draw the plot area. The intensities outside of the window and level // are going to be drawn in darker shade of gray float t0, t1, xDummy; m_Curve->GetControlPoint(0, t0, xDummy); m_Curve->GetControlPoint(m_Curve->GetControlPointCount() - 1, t1, xDummy); // Scale the display so that leftmost point to plot maps to 0, rightmost to 1 float z0 = std::min(t0, 0.0f); float z1 = std::max(t1, 1.0f); glPushMatrix(); glTranslated(-z0 / (z1-z0), 0, 0); glScaled(1.0 / (z1-z0), 1, 1); // Draw the quads glBegin(GL_QUADS); // Outer quad glColor3d(0.9, 0.9, 0.9); glVertex2d(z0,0); glVertex2d(z0,1); glVertex2d(z1,1); glVertex2d(z1,0); float q0 = std::max(t0, 0.0f); float q1 = std::min(t1, 1.0f); if(q1 > q0) { // Inner quad glColor3d(1.0, 1.0, 1.0); glVertex2d(q0, 0.0); glVertex2d(q0, 1.0); glVertex2d(q1, 1.0); glVertex2d(q1, 0.0); } glEnd(); // Draw a histogram if it exists if(m_Histogram.size()) { // Compute the heights of all the histogram bars float xWidth = 1.0f / m_Histogram.size(); unsigned int xPixelsPerBin = w() / m_Histogram.size(); vector xHeight(m_Histogram.size(), 0.0); // Start by computing the maximum (cutoff) height float xHeightMax = m_HistogramMax * m_HistogramMaxLevel; if(m_HistogramLog) xHeightMax = log(xHeightMax) / log(10.0); // Continue by computing each bin's height unsigned int i; for(i=0;i < m_Histogram.size();i++) { // Process the histogram height based on options xHeight[i] = m_Histogram[i]; if(m_HistogramLog) xHeight[i] = (xHeight[i] > 0) ? log(xHeight[i]) / log(10.0) : 0; if(xHeight[i] > xHeightMax) xHeight[i] = xHeightMax / 0.9f; } // Draw the horizontal lines at various powers of 10 if in log mode if(m_HistogramLog) { glColor3d(0.75, 0.75, 0.75); glBegin(GL_LINES); for(double d = 1.0; d < xHeightMax; d+=1.0) { glVertex2f(0,d); glVertex2f(1,d); } glEnd(); } // Paint the filled-in histogram bars. We fill in with black color if the // histogram width is less than 4 pixels if(xPixelsPerBin < 4) glColor3f(0.0f, 0.0f, 0.0f); else glColor3f(0.8f, 0.8f, 1.0f); // Paint the bars as quads glBegin(GL_QUADS); for(i=0;i < m_Histogram.size();i++) { // Compute the physical height of the bin float xBin = xWidth * i; float hBin = xHeight[i] * 0.9f / xHeightMax; // Paint the bar glVertex2f(xBin,0); glVertex2f(xBin,hBin); glVertex2f(xBin+xWidth,hBin); glVertex2f(xBin+xWidth,0); } glEnd(); // Draw lines around the quads, but only if the bins are thick if(xPixelsPerBin >= 4) { // Draw the vertical lines between the histogram bars glBegin(GL_LINE_STRIP); glColor3d(0.0, 0.0, 0.0); for(i = 0; i < m_Histogram.size(); i++) { // Compute the physical height of the bin float xBin = xWidth * i; float hBin = xHeight[i] * 0.9f / xHeightMax; // Draw around the bar, starting at the lower left corner glVertex2f(xBin, 0.0f); glVertex2f(xBin, hBin); glVertex2f(xBin + xWidth, hBin); glVertex2f(xBin + xWidth, 0.0f); } glEnd(); } } // Draw the box around the plot area glColor3d(0,0,0); glBegin(GL_LINE_LOOP); glVertex2d(z0,0); glVertex2d(z0,1); glVertex2d(z1,1); glVertex2d(z1,0); glEnd(); // Set up the smooth line drawing style glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glLineWidth(2.0); // Draw the curve using linear segments glColor3d(1.0,0.0,0.0); glBegin(GL_LINE_STRIP); float t = z0; float tStep = (z1-z0) / (CURVE_RESOLUTION); for (unsigned int i=0;i<=CURVE_RESOLUTION;i++) { glVertex2f(t,m_Curve->Evaluate(t)); t+=tStep; } glEnd(); // Draw the handles glLineWidth(1.0); for (unsigned int c=0;cGetControlPointCount();c++) { // Get the next control point float t,x; m_Curve->GetControlPoint(c,t,x); // Draw a quad around the control point if(c == (unsigned int)m_Interactor->GetMovingControlPoint()) { glColor3d(1,1,0); gl_draw_circle_with_border(t, x, 5.0, z1-z0, false); } else { glColor3d(1,0,0); gl_draw_circle_with_border(t, x, 4.0, z1-z0, false); } } glPopMatrix(); // Pop the attributes glPopAttrib(); // Done glFlush(); } void IntensityCurveBox ::gl_draw_circle_with_border(double x, double y, double r, double bw, bool select) { 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 * bw / this->w(), 1.2 / this->h(), 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(); if(select) glColor3ub(0xff,0x00,0xff); else glColor3ub(0x00,0x00,0x00); glBegin(GL_LINE_LOOP); for(size_t i = 0; i < cx.size(); i++) glVertex2d(r * cx[i], r * cy[i]); glEnd(); glPopMatrix(); } int IntensityCurveBox ::GetControlPointInVicinity(float x, float y, int pixelRadius) { float rx = pixelRadius * 1.0f / w(); float ry = pixelRadius * 1.0f / h(); 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; m_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; } void IntensityCurveBox ::ComputeHistogram(GreyImageWrapper *source, unsigned int iMinPixelsPerBin) { // Need a wrapper assert(source); assert(this->w() > 0); // Get 'absolute' image intensity range, i.e., the largest and smallest // intensity in the whole image GreyType iAbsMin = source->GetImageMin(); GreyType iAbsMax = source->GetImageMax(); unsigned int nFrequencies = (iAbsMax - iAbsMin) + 1; // Compute the freqencies of the intensities unsigned int *frequency = new unsigned int[nFrequencies]; memset(frequency,0,sizeof(unsigned int) * nFrequencies); GreyImageWrapper::ConstIterator it = source->GetImageConstIterator(); for(it.GoToBegin();!it.IsAtEnd();++it) { frequency[it.Value()-iAbsMin]++; } // Determine the bin size: no bin should be less than a single pixel wide if(nFrequencies * iMinPixelsPerBin > m_HistogramBinSize * this->w()) m_HistogramBinSize = (unsigned int) ceil(nFrequencies * iMinPixelsPerBin * 1.0 / this->w()); unsigned int nBins = (unsigned int) ceil(nFrequencies * 1.0 / m_HistogramBinSize); // Allocate an array of bins m_Histogram.resize(nBins); fill(m_Histogram.begin(), m_Histogram.end(), 0); // Reset the max-frequency m_HistogramMax = 0; // Put the frequencies into the bins for(unsigned int i=0;iGetControlPoint(0, t0, xDummy); m_Curve->GetControlPoint(m_Curve->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 Vector3f(e.XSpace[0] * (z1 - z0) + z0, e.XSpace[1], e.XSpace[2]); } int IntensityCurveInteraction ::OnMousePress(const FLTKEvent &event) { // Check the control point affected by the event Vector3f xCurve = m_Parent->GetEventCurveCoordinates(event); m_MovingControlPoint = m_Parent->GetControlPointInVicinity(xCurve[0],xCurve[1],5); SetCursor(m_MovingControlPoint); // Clear the dragged flag m_FlagDraggedControlPoint = false; return 1; } int IntensityCurveInteraction ::OnMouseRelease(const FLTKEvent &event, const FLTKEvent &irisNotUsed(dragEvent)) { Vector3f xCurve = m_Parent->GetEventCurveCoordinates(event); if (m_MovingControlPoint >= 0) { if(m_FlagDraggedControlPoint) { // Update the control point if (UpdateControl(xCurve)) { // Repaint parent m_Parent->redraw(); // Fire the update event (should this be done on drag?) m_Parent->GetParent()->OnCurveChange(); m_Parent->GetParent()->OnControlPointUpdate(); } } else { m_Parent->GetParent()->OnControlPointUpdate(); m_Parent->redraw(); } // Set the cursor back to normal SetCursor(-1); } return 1; } int IntensityCurveInteraction ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &irisNotUsed(dragEvent)) { Vector3f xCurve = m_Parent->GetEventCurveCoordinates(event); if (m_MovingControlPoint >= 0) { // Update the moving control point if (UpdateControl(xCurve)) { // Repaint parent m_Parent->redraw(); // Fire the update event (should this be done on drag?) m_Parent->GetParent()->OnCurveChange(); m_Parent->GetParent()->OnControlPointUpdate(); } // Set the dragged flag m_FlagDraggedControlPoint = true; } return 1; } int IntensityCurveInteraction ::OnMouseEnter(const FLTKEvent &irisNotUsed(event)) { return 1; } int IntensityCurveInteraction ::OnMouseLeave(const FLTKEvent &irisNotUsed(event)) { return 1; } int IntensityCurveInteraction ::OnMouseMotion(const FLTKEvent &event) { Vector3f xCurve = m_Parent->GetEventCurveCoordinates(event); int cp = m_Parent->GetControlPointInVicinity(xCurve[0],xCurve[1],5); SetCursor(cp); return 1; } void IntensityCurveInteraction ::SetCursor(int cp) { if (cp < 0) { fl_cursor(FL_CURSOR_DEFAULT); } else if (cp == 0 || cp == (int)(m_Parent->GetCurve()->GetControlPointCount()-1)) { fl_cursor(FL_CURSOR_WE); } else { fl_cursor(FL_CURSOR_MOVE); } } int IntensityCurveInteraction ::GetMovingControlPoint() const { return m_MovingControlPoint; } void IntensityCurveInteraction ::SetMovingControlPoint(int cp) { m_MovingControlPoint = cp; } bool IntensityCurveBox ::UpdateControlPoint(size_t i, float t, float x) { // Must be in range assert(i < m_Curve->GetControlPointCount()); // Get the current values float told, xold; m_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 = m_Curve->GetControlPointCount()-1; if (i == 0 || i == (size_t) last) { // Get the current domain float tMin,tMax,x; m_Curve->GetControlPoint(0, tMin, x); m_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 m_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 m_Curve->UpdateControlPoint(i, t, x); // Check the curve for monotonicity if(!m_Curve->IsMonotonic()) { m_Curve->UpdateControlPoint(i, told, xold); return false; } } return true; } bool IntensityCurveInteraction ::UpdateControl(const Vector3f &p) { return m_Parent->UpdateControlPoint( m_MovingControlPoint, p[0], p[1]); } itksnap/UserInterface/BasicComponents/IntensityCurveBox.h0000644000076500000240000001247611402000654023211 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IntensityCurveBox.h,v $ Language: C++ Date: $Date: 2010/06/03 19:25:32 $ 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 __IntensityCurveBox_h_ #define __IntensityCurveBox_h_ #include #include #include #include #include class LayerInspectorUILogic; class GreyImageWrapper; class IntensityCurveInteraction; /** * \class IntensityCurveBox * \brief An FLTK Box (Gl_Window) used to paint intensity mapping curves. */ class IntensityCurveBox : public FLTKCanvas { public: IntensityCurveBox(int x,int y,int w,int h,const char *label); virtual ~IntensityCurveBox(); // Get the intensity curve interactor irisGetMacro(Interactor, IntensityCurveInteraction *); /** * Handle displaying the curve */ void draw(); /** Compute the histogram given an image wrapper */ void ComputeHistogram(GreyImageWrapper *source, unsigned int iMinPixelsPerBin = 1); // Get/set the intensity curve irisGetMacro(Curve,IntensityCurveInterface *); irisSetMacro(Curve,IntensityCurveInterface *); // Get/set the parent object irisGetMacro(Parent,LayerInspectorUILogic *); irisSetMacro(Parent,LayerInspectorUILogic *); // Get the histogram itself const std::vector &GetHistogram() const { return m_Histogram; } // Get/set the histogram properties irisGetMacro(HistogramBinSize, unsigned int); irisSetMacro(HistogramBinSize, unsigned int); irisGetMacro(HistogramMaxLevel, float); irisSetMacro(HistogramMaxLevel, float); irisIsMacro(HistogramLog); irisSetMacro(HistogramLog, bool); // External command for moving the control points. The method will // not allow updates that violate constraints. The return value is // true if the constraints were not violated. Return values tnew and // xnew give the values of the control point after update. bool UpdateControlPoint(size_t i, float t, float x); /** * The resolution of the curve displayed on the screen * TODO: Control over curve resolution */ static unsigned int CURVE_RESOLUTION; private: /** * Check if a control point is close to another point (i.e. mouse position) */ int GetControlPointInVicinity(float x, float y, int pixelRadius); /** * Map event coordinates to image space coordinates */ Vector3f GetEventCurveCoordinates(const FLTKEvent &e); /** The intensity mapping curve */ IntensityCurveInterface *m_Curve; /** Parent object */ LayerInspectorUILogic *m_Parent; /** Histogram of the image */ std::vector m_Histogram; /** Max frequency in the histogram */ unsigned int m_HistogramMax; /** Max level in the histograms: bins above this level are truncated */ float m_HistogramMaxLevel; /** Size of the bin, in intensities */ unsigned int m_HistogramBinSize; /** Flag, whether log of the frequencies is used */ bool m_HistogramLog; // Draw circles void gl_draw_circle_with_border(double x, double y, double r, double bw, bool select); IntensityCurveInteraction* m_Interactor; // Allow access to private data friend class IntensityCurveInteraction; }; /** * Interaction handler for control point manipulation */ class IntensityCurveInteraction : public InteractionMode { public: IntensityCurveInteraction(IntensityCurveBox *parent); int OnMousePress(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent); int OnMouseEnter(const FLTKEvent &event); int OnMouseLeave(const FLTKEvent &event); int OnMouseMotion(const FLTKEvent &event); int GetMovingControlPoint() const; void SetMovingControlPoint(int cp); private: // Pointer to the parent canvas IntensityCurveBox *m_Parent; // Set cursor depending on selected point void SetCursor(int iControlPoint); // Update a control point to reflect mouse motion to p bool UpdateControl(const Vector3f &p); // Control point being currently edited int m_MovingControlPoint; bool m_FlagDraggedControlPoint; }; #endif // __IntensityCurveBox_h_ itksnap/UserInterface/BasicComponents/InteractionMode.h0000644000076500000240000001045611361760533022642 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: InteractionMode.h,v $ Language: C++ Date: $Date: 2010/04/16 04:02:35 $ 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 __InteractionMode_h_ #define __InteractionMode_h_ #include #include "SNAPCommonUI.h" #include "FLTKEvent.h" // Forward reference to the canvas object class FLTKCanvas; /** * \class InteractionMode * \brief This class defines a UI interaction mode. * * It is used to define the behavior of a tool on a canvas (FLTKCanvas). * This class removes the need to write huge handle() methods */ class InteractionMode { public: /** Constructor takes the pointer to the target canvas object */ InteractionMode(FLTKCanvas *canvas) { this->m_Canvas = canvas; } /** Destructor */ virtual ~InteractionMode(void) {} /** * Called when mouse button is pressed. */ virtual int OnMousePress(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called when mouse is pressed. The event generated when the mouse * was pressed is also passed in for reference. */ virtual int OnMouseRelease(const FLTKEvent &irisNotUsed(event), const FLTKEvent &irisNotUsed(pressEvent)) { return 0; } /** * Called when the mouse is dragged. The event generated when the mouse * was pressed is also passed in for reference. */ virtual int OnMouseDrag(const FLTKEvent &irisNotUsed(event), const FLTKEvent &irisNotUsed(pressEvent)) { return 0; } /** * Called when mouse enters the canvas. Return 1 to track motion events. */ virtual int OnMouseEnter(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called when mouse exits the canvas. */ virtual int OnMouseLeave(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called when mouse moves in the canvas */ virtual int OnMouseMotion(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called when mouse moves in the canvas */ virtual int OnMouseWheel(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called when a key on the keyboard is pressed. */ virtual int OnKeyDown(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called when a key on the keyboard is released. */ virtual int OnKeyUp(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called for FLTK short-cut events */ virtual int OnShortcut(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called for FLTK Drag and Drop paste events */ virtual int OnDragAndDrop(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Called for all other FLTK events */ virtual int OnOtherEvent(const FLTKEvent &irisNotUsed(event)) { return 0; } /** * Can be called when the canvas is redrawing itself. * This is not really an event but a convenience method. */ virtual void OnDraw() { return; } /** Get the pointer to the client canvas */ FLTKCanvas *GetCanvas() const { return m_Canvas; } protected: // The target canvas FLTKCanvas *m_Canvas; }; #endif // __InteractionMode_h_ itksnap/UserInterface/BasicComponents/InteractionModeClient.cxx0000644000076500000240000000523310735614374024356 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: InteractionModeClient.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:16 $ 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 "InteractionModeClient.h" #include #include void InteractionModeClient ::PushInteractionMode(InteractionMode *mode) { m_Interactors.push_front(mode); } InteractionMode * InteractionModeClient ::PopInteractionMode() { InteractionMode *lastMode = m_Interactors.front(); m_Interactors.pop_front(); return lastMode; } void InteractionModeClient ::ClearInteractionStack() { m_Interactors.clear(); } void InteractionModeClient ::SetSingleInteractionMode(InteractionMode *mode) { this->ClearInteractionStack(); this->PushInteractionMode(mode); } unsigned int InteractionModeClient ::GetInteractionModeCount() { return m_Interactors.size(); } InteractionMode * InteractionModeClient ::GetTopInteractionMode() { return m_Interactors.front(); } bool InteractionModeClient ::IsInteractionModeAdded(InteractionMode *target) { return std::find(m_Interactors.begin(), m_Interactors.end(), target) != m_Interactors.end(); } void InteractionModeClient ::FireInteractionDrawEvent() { // Propagate the drawing event through the stack typedef std::list::iterator ModeIterator; for (ModeIterator it = m_Interactors.begin(); it!=m_Interactors.end();it++) { InteractionMode *mode = *it; mode->OnDraw(); } } itksnap/UserInterface/BasicComponents/InteractionModeClient.h0000644000076500000240000000445310534177575024012 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: InteractionModeClient.h,v $ Language: C++ Date: $Date: 2006/12/02 04:22:21 $ 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 __InteractionModeClient_h_ #define __InteractionModeClient_h_ #include "InteractionMode.h" #include /** * \class InteractionModeClient * \brief An abstract class that can pass on Fltk events to interaction modes */ class InteractionModeClient { public: /** * Push an interaction mode onto the stack of modes. Mode becomes first to * receive events. The events that it does not receive are passed on to the * next mode on the stack. */ void PushInteractionMode(InteractionMode *mode); /** * Pop the last interaction mode off the stack */ InteractionMode *PopInteractionMode(); /** * Get the top interaction mode on the stack */ InteractionMode *GetTopInteractionMode(); /** * See if the interaction mode is in the stack */ bool IsInteractionModeAdded(InteractionMode *target); /** * Remove all interaction modes */ void ClearInteractionStack(); /** * Set the interaction stack to consist of just one interaction mode. This is * equivalent to calling ClearInteractionStack() followed by PushInteractionMode() */ void SetSingleInteractionMode(InteractionMode *mode); /** * Get the number of interaction modes on the stack */ unsigned int GetInteractionModeCount(); // Virtual destructor virtual ~InteractionModeClient() {} protected: /** * This method should be called to draw the interactors (call their OnDraw methods * in a sequence from bottom to top) */ void FireInteractionDrawEvent(); // The stack of interaction modes std::list m_Interactors; }; #endif // __InteractionModeClient_h_ itksnap/UserInterface/BasicComponents/MetaDataTable.cxx0000644000076500000240000002622111412166664022560 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MetaDataTable.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ 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 "MetaDataTable.h" #include "FL/fl_draw.H" #include "itkImage.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkGDCMImageIO.h" using namespace std; string get_rai_code(itk::SpatialOrientation::ValidCoordinateOrientationFlags code) { std::map m_CodeToString; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIP] = "RIP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIP] = "LIP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSP] = "RSP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSP] = "LSP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIA] = "RIA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LIA] = "LIA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSA] = "RSA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LSA] = "LSA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRP] = "IRP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILP] = "ILP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRP] = "SRP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLP] = "SLP"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IRA] = "IRA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ILA] = "ILA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SRA] = "SRA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SLA] = "SLA"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI] = "RPI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPI] = "LPI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI] = "RAI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAI] = "LAI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPS] = "RPS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LPS] = "LPS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAS] = "RAS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_LAS] = "LAS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRI] = "PRI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLI] = "PLI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARI] = "ARI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALI] = "ALI"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PRS] = "PRS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PLS] = "PLS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ARS] = "ARS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ALS] = "ALS"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPR] = "IPR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPR] = "SPR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAR] = "IAR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAR] = "SAR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IPL] = "IPL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SPL] = "SPL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_IAL] = "IAL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_SAL] = "SAL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIR] = "PIR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSR] = "PSR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIR] = "AIR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASR] = "ASR"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIL] = "PIL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_PSL] = "PSL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIL] = "AIL"; m_CodeToString[itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_ASL] = "ASL"; return m_CodeToString[code]; } template bool try_get_metadata(itk::MetaDataDictionary &mdd, string &key, string &output, AnyType deflt) { AnyType v = deflt; if(itk::ExposeMetaData(mdd, key, v)) { ostringstream oss; oss << v << endl; output = oss.str(); return true; } else return false; } void MetaDataTable ::SetInputImage(itk::ImageBase<3> *image) { int w = 0, h = 0; // Init preferred widths m_PreferredKeyWidth = 0; m_PreferredValueWidth = 0; m_PreferredHeight = 0; // Get the data m_Header.clear(); m_Body.clear(); m_Header.push_back("Header Field"); m_Header.push_back("Value"); // Measure widths fl_font(FL_HELVETICA_BOLD, 11); fl_measure(m_Header[0].c_str(), m_PreferredKeyWidth, m_PreferredHeight); fl_measure(m_Header[1].c_str(), m_PreferredValueWidth, m_PreferredHeight); m_PreferredKeyWidth += 6; m_PreferredValueWidth += 6; fl_font(FL_HELVETICA, 11); // Get the dictionary itk::MetaDataDictionary &mdd = image->GetMetaDataDictionary(); itk::MetaDataDictionary::ConstIterator itMeta; for(itMeta = mdd.Begin(); itMeta != mdd.End(); ++itMeta) { // Get the metadata as a generic object string key = itMeta->first, v_string; string value; // Orientation flag object itk::SpatialOrientation::ValidCoordinateOrientationFlags v_oflags = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_INVALID; // Is the value a string? if(itk::ExposeMetaData(mdd, key, value)) { // For some weird reason, some of the strings returned by this method // contain '\0' characters. We will replace them by spaces ostringstream sout(""); for(unsigned int i=0;i= ' ') sout << value[i]; value = sout.str(); // Make sure the value has more than blanks if(value.find_first_not_of(" ") == v_string.npos) value=""; } else if(itk::ExposeMetaData(mdd, key, v_oflags)) { value = get_rai_code(v_oflags); } else { bool rc = false; if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) rc |= try_get_metadata(mdd, key, value, 0); if(!rc) { ostringstream oss; oss << "object of type " << itMeta->second->GetMetaDataObjectTypeName(); value = oss.str(); } } // Try to remap the key to DICOM string dcm_label; if(itk::GDCMImageIO::GetLabelFromTag(key, dcm_label)) key = dcm_label; // Store the values vector row; row.push_back(key); row.push_back(value); m_Body.push_back(row); // Measure text w = 0, h = 0; fl_measure(key.c_str(), w, h); m_PreferredKeyWidth = std::max(w + 6, m_PreferredKeyWidth); w = 0; h = 0; fl_measure(value.c_str(), w, h); m_PreferredValueWidth = std::max(w + 6, m_PreferredValueWidth); m_PreferredHeight = std::max(h, m_PreferredHeight); } // Set the number of rows and columns this->rows(std::max((int) m_Body.size(),10)); this->cols(m_Header.size()); this->col_header(1); this->col_resize(4); this->row_height_all(4+m_PreferredHeight); } void MetaDataTable ::SetColumnWidth(int total_width) { // Case 1: there is no data if(m_PreferredKeyWidth == 0) { col_width(0, total_width/2); col_width(1, total_width - col_width(0)); } else { int wk = std::min(total_width/2, m_PreferredKeyWidth); col_width(0, wk); int wv = std::max(total_width - wk, m_PreferredValueWidth); col_width(1, wv); row_height_all(m_PreferredHeight + 4); } } void MetaDataTable ::draw_cell(TableContext context, int R, int C, int X, int Y, int W, int H) { string text; // If data is not initialized, return if(m_Header.size() == 0) return; // Actual drawing switch(context) { case CONTEXT_STARTPAGE: fl_font(FL_HELVETICA, 11); return; /* case CONTEXT_ROW_HEADER: text = m_Body[R][0]; fl_push_clip(X, Y, W, H); fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, FL_LIGHT1); fl_pop_clip(); fl_push_clip(X+3, Y, W-6, H); fl_color(FL_BLACK); fl_draw(text.c_str(), X+3, Y, W-6, H, FL_ALIGN_LEFT); fl_pop_clip(); break; */ case CONTEXT_COL_HEADER: text = m_Header[C]; fl_push_clip(X, Y, W, H); fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, FL_LIGHT1); fl_pop_clip(); fl_push_clip(X+3, Y, W-6, H); fl_font(FL_HELVETICA_BOLD, 11); fl_color(FL_BLACK); fl_draw(text.c_str(), X+3, Y, W-6, H, FL_ALIGN_LEFT); fl_font(FL_HELVETICA, 11); fl_pop_clip(); break; case CONTEXT_CELL: fl_push_clip(X, Y, W, H); fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, row_selected(R) ? selection_color() : FL_WHITE); fl_pop_clip(); if(R < (int) m_Body.size()) { text = m_Body[R][C]; fl_push_clip(X+3, Y, W-6, H); fl_color(FL_BLACK); fl_draw(text.c_str(), X+3, Y, W-6, H, FL_ALIGN_LEFT); fl_pop_clip(); } break; default: break; } } itksnap/UserInterface/BasicComponents/MetaDataTable.h0000644000076500000240000000435511401720237022177 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MetaDataTable.h,v $ Language: C++ Date: $Date: 2010/06/03 12:31:27 $ 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 __MetaDataTable_h_ #define __MetaDataTable_h_ #include "FL/Fl_Table.H" #include "FL/Fl_Table_Row.H" #include "SNAPCommon.h" #include #include namespace itk { template class ImageBase; } class MetaDataTable : public Fl_Table_Row { public: MetaDataTable(int x, int y, int w, int h, const char *l=0) : Fl_Table_Row(x,y,w,h,l) { end(); } ~MetaDataTable() { } void SetInputImage(itk::ImageBase<3> *image); void SetColumnWidth(int total_width); irisGetMacro(PreferredKeyWidth, size_t); irisGetMacro(PreferredValueWidth, size_t); protected: /* Draw table cell */ void draw_cell(TableContext context, int R=0, int C=0, int X=0, int Y=0, int W=0, int H=0); std::vector< std::string > m_Header; std::vector< std::vector< std::string > > m_Body; int m_PreferredKeyWidth, m_PreferredValueWidth, m_PreferredHeight; }; #endif itksnap/UserInterface/BasicComponents/RecursiveInteractionMode.cxx0000644000076500000240000000755310735614374025116 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RecursiveInteractionMode.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:16 $ 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 "RecursiveInteractionMode.h" #include int RecursiveInteractionMode ::OnMousePress(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnMousePress(event)) return 1; return 0; } int RecursiveInteractionMode ::OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnMouseRelease(event, pressEvent)) return 1; return 0; } int RecursiveInteractionMode ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnMouseDrag(event, pressEvent)) return 1; return 0; } int RecursiveInteractionMode ::OnMouseEnter(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnMouseEnter(event)) return 1; return 0; } int RecursiveInteractionMode ::OnMouseLeave(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnMouseLeave(event)) return 1; return 0; } int RecursiveInteractionMode ::OnMouseMotion(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnMouseMotion(event)) return 1; return 0; } int RecursiveInteractionMode ::OnMouseWheel(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnMouseWheel(event)) return 1; return 0; } int RecursiveInteractionMode ::OnKeyDown(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnKeyDown(event)) return 1; return 0; } int RecursiveInteractionMode ::OnKeyUp(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnKeyUp(event)) return 1; return 0; } int RecursiveInteractionMode ::OnShortcut(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnShortcut(event)) return 1; return 0; } int RecursiveInteractionMode ::OnOtherEvent(const FLTKEvent &event) { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) if((*m)->OnOtherEvent(event)) return 1; return 0; } void RecursiveInteractionMode ::OnDraw() { for(ModeIterator m = m_Interactors.begin(); m != m_Interactors.end(); m++) (*m)->OnDraw(); } itksnap/UserInterface/BasicComponents/RecursiveInteractionMode.h0000644000076500000240000000455610534177575024547 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: RecursiveInteractionMode.h,v $ Language: C++ Date: $Date: 2006/12/02 04:22:21 $ 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 __RecursiveInteractionMode_h_ #define __RecursiveInteractionMode_h_ #include "InteractionModeClient.h" #include // Forward reference to FLTK Canvas class FLTKCanvas; /** * \class RecursiveInteractionMode * \brief An interaction mode to which more interaction modes can be added * This class is an interaction mode and an interaction mode client, so that * you can create a tree of interaction modes. The only thing to remember is * that if you override one of the OnXXX event methods, you need to call * Superclass::OnXXX in order for the child events to be invoked */ class RecursiveInteractionMode : public InteractionMode, public InteractionModeClient { public: // Typedef for the iterator typedef std::list::iterator ModeIterator; // All the events virtual int OnMousePress(const FLTKEvent &event); virtual int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent); virtual int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent); virtual int OnMouseEnter(const FLTKEvent &event); virtual int OnMouseLeave(const FLTKEvent &event); virtual int OnMouseMotion(const FLTKEvent &event); virtual int OnMouseWheel(const FLTKEvent &event); virtual int OnKeyDown(const FLTKEvent &event); virtual int OnKeyUp(const FLTKEvent &event); virtual int OnShortcut(const FLTKEvent &event); virtual int OnOtherEvent(const FLTKEvent &event); virtual void OnDraw(); // Constructor, takes the target FLTK canvas as the parameter RecursiveInteractionMode(FLTKCanvas *canvas) : InteractionMode(canvas) {} // Virtual destructor virtual ~RecursiveInteractionMode() {} }; #endif // __RecursiveInteractionMode_h_ itksnap/UserInterface/BasicComponents/SnakeParametersPreviewBox.cxx0000644000076500000240000002133611457367102025231 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParametersPreviewBox.cxx,v $ Language: C++ Date: $Date: 2010/10/19 19:15:14 $ Version: $Revision: 1.6 $ 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 "SnakeParametersPreviewBox.h" #include "SnakeParametersPreviewPipeline.h" #include "OpenGLSliceTexture.h" #include "SnakeParametersUILogic.h" #include "SNAPOpenGL.h" extern void fl_alert(const char *, ...); SnakeParametersPreviewBox ::SnakeParametersPreviewBox(int x, int y, int w, int h, const char *label) : FLTKCanvas(x,y,w,h,label), m_Interactor(this) { // Initialize the texture object m_Texture = new OpenGLSliceTexture; m_Texture->SetGlComponents(4); m_Texture->SetGlFormat(GL_RGBA); // Set up the interactor PushInteractionMode(&m_Interactor); } SnakeParametersPreviewBox ::~SnakeParametersPreviewBox() { delete m_Texture; } void SnakeParametersPreviewBox ::draw() { // Set up the projection if necessary if(!valid()) { // The window will have coordinates (0,0) to (1,1) glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,1.0,0.0,1.0); glViewport(0,0,w(),h()); // Establish the model view matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // glScaled(4,4,1); } // Update everything m_Pipeline->Update(this); // Clear the display glClearColor(0.0,0.0,0.0,1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Set up the line drawing attributes glPushAttrib(GL_LIGHTING_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT); // Set up the model matrix glPushMatrix(); glScaled(1.0 / m_Pipeline->GetSpeedImage()->GetBufferedRegion().GetSize(0), 1.0 / m_Pipeline->GetSpeedImage()->GetBufferedRegion().GetSize(1), 1.0); // Draw the speed image m_Texture->SetImage(m_Pipeline->GetDisplayImage()); m_Texture->Draw(Vector3d(1.0)); // Set up the line drawing mode glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glLineWidth(2.0); glColor3d(1.0, 0.0, 0.0); // Draw the evolving contour if it's available if(m_Pipeline->IsDemoLoopRunning()) { std::vector &points = m_Pipeline->GetDemoLoopContour(); glColor3d(1.0, 0.0, 0.0); glBegin(GL_LINES); std::vector::iterator it = points.begin(); for(; it != points.end(); ++it) { glVertex(*it); } glEnd(); glLineWidth(1.0); glColor4d(1.0, 0.0, 0.0, 0.5); } // Draw the vectors //glLineWidth(1.0); // No more image scaling glPopMatrix(); glPushMatrix(); // Get the point collection const SnakeParametersPreviewPipeline::SampledPointList &list = m_Pipeline->GetSampledPoints(); // Draw the spline glBegin(GL_LINE_LOOP); for(unsigned int j=0;jGetImagePoints(); glBegin(GL_LINES); for(unsigned int j=0;jw(), event.XCanvas[1] * 1.0 / m_Owner->h()); if(m_ControlsVisible) { // The closest point index m_ActiveControl = 0; double minDistance = 0.0; // Get a hold of the control points const SnakeParametersPreviewPipeline::ControlPointList &cp = m_Owner->m_Pipeline->GetControlPoints(); // Find the closest control point for(unsigned int i=0;i distance) { minDistance = distance; m_ActiveControl = i; } } // Make sure the distance is large enough if(minDistance < 6.0 / m_Owner->w()) { m_ControlPicked = true; } else { m_ControlsVisible = m_ControlPicked = false; m_Owner->redraw(); } } else { m_ControlsVisible = true; m_Owner->redraw(); } return true; } int SnakeParametersPreviewBox::Interactor ::OnMouseRelease(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)) { Vector2d xClick( event.XCanvas[0] * 1.0 / m_Owner->w(), event.XCanvas[1] * 1.0 / m_Owner->h()); if(m_ControlPicked) { // Update the control point m_Owner->m_Pipeline->ChangeControlPoint( m_ActiveControl, xClick, false); // Redraw the parent m_Owner->GetParentUI()->RedrawAllBoxes(); } return true; } int SnakeParametersPreviewBox::Interactor ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)) { Vector2d xClick( event.XCanvas[0] * 1.0 / m_Owner->w(), event.XCanvas[1] * 1.0 / m_Owner->h()); if(m_ControlPicked) { // Update the control point m_Owner->m_Pipeline->ChangeControlPoint( m_ActiveControl, xClick, false); // Redraw the parent m_Owner->GetParentUI()->RedrawAllBoxes(); } return true; } void SnakeParametersPreviewBox::Interactor ::OnDraw() { if(m_ControlsVisible) { glColor3f(1,0.67,0.33); // Get a hold of the control points const SnakeParametersPreviewPipeline::ControlPointList &cp = m_Owner->m_Pipeline->GetControlPoints(); for(unsigned int i=0;iw(),8,3); gluDeleteQuadric(obj); glPopMatrix(); } } } itksnap/UserInterface/BasicComponents/SnakeParametersPreviewBox.h0000644000076500000240000000752411227721563024661 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParametersPreviewBox.h,v $ Language: C++ Date: $Date: 2009/07/16 22:02:27 $ 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 __SnakeParametersPreviewBox_h_ #define __SnakeParametersPreviewBox_h_ #include "FLTKCanvas.h" #include "SnakeParameters.h" #include "itkRGBAPixel.h" class OpenGLSliceTexture; class SnakeParametersPreviewPipeline; class SnakeParametersUILogic; /** * \class SnakeParametersPreviewBox * \brief A user interface component used to preview snake * parameters in 2D. * * This component displays a portion of an image and a curve representing * an evolving snake. Using the snake equation parameters that the user specifies, * forces normal to the boundary of the image are shown as vectors */ class SnakeParametersPreviewBox : public FLTKCanvas { public: SnakeParametersPreviewBox(int x, int y, int w, int h, const char *label=0); virtual ~SnakeParametersPreviewBox(); /** A preview pipeline that has the logic of this class */ irisSetMacro(Pipeline,SnakeParametersPreviewPipeline *); irisGetMacro(Pipeline,SnakeParametersPreviewPipeline *); irisSetMacro(ParentUI, SnakeParametersUILogic *); irisGetMacro(ParentUI, SnakeParametersUILogic *); /** An enumeration of different display modes for this widget */ enum DisplayMode { CURVATURE_FORCE=0,ADVECTION_FORCE,PROPAGATION_FORCE,TOTAL_FORCE }; /** Set the display mode */ irisSetMacro(ForceToDisplay,DisplayMode); /** Draw method - paints the widget */ void draw(); protected: // Texture type for drawing speed images typedef itk::RGBAPixel RGBAType; /** Parent UI */ SnakeParametersUILogic *m_ParentUI; /** Preview pipeline logic */ SnakeParametersPreviewPipeline *m_Pipeline; /** A texture object used to store the image */ OpenGLSliceTexture *m_Texture; /** Which force is being displayed? */ DisplayMode m_ForceToDisplay; /** An interaction mode for point manipulation */ class Interactor : public InteractionMode { public: Interactor(SnakeParametersPreviewBox *owner); int OnMousePress(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)); void OnDraw(); private: SnakeParametersPreviewBox *m_Owner; bool m_ControlsVisible; bool m_ControlPicked; unsigned int m_ActiveControl; }; // Allow private access to the interactor friend class SnakeParametersPreviewBox::Interactor; /** The interactor */ Interactor m_Interactor; }; #endif // __SnakeParametersPreviewBox_h_ itksnap/UserInterface/BasicComponents/SnakeParametersPreviewPipeline.cxx0000644000076500000240000004372011457367102026247 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParametersPreviewPipeline.cxx,v $ Language: C++ Date: $Date: 2010/10/19 19:15:14 $ Version: $Revision: 1.6 $ 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 "SnakeParametersPreviewPipeline.h" #include "FL/Fl.H" #include "FL/Fl_Gl_Window.H" #include "SNAPOpenGL.h" #include "GlobalState.h" #include "LevelSetExtensionFilter.h" #include "SignedDistanceFilter.h" #include "SNAPLevelSetFunction.h" #include "itkBSplineInterpolationWeightFunction.h" #include "itkBSplineKernelFunction.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkNarrowBandLevelSetImageFilter.h" #include "itkVTKImageExport.h" #include "vtkCellArray.h" #include "vtkContourFilter.h" #include "vtkImageData.h" #include "vtkImageImport.h" #include "vtkPolyData.h" #include "SNAPLevelSetDriver.h" #include "PolygonScanConvert.h" #ifndef vtkFloatingPointType #define vtkFloatingPointType float #endif using namespace std; extern void fl_alert(const char *, ...); /** * This private-scope class creates a 2D demo of the level set segmentation * The segmentation is a 2D version of the SNAP segmentation, with contours * extracted at each iteration. The user supplies the speed image, a set of * points that form the initial contour and the snake evolution parameters. * Then, on a timer, call OnTimerEvent() to generate a demo loop of evolving * contours. */ class LevelSetPreview2d { public: typedef itk::OrientedImage FloatImageType; typedef SnakeParametersPreviewPipeline::SampledPointList CurveType; // Constructor LevelSetPreview2d() { m_Driver = NULL; m_SpeedImage = NULL; m_DriverDirty = true; m_ContourDirty = true; m_DemoLoopLength = 160; m_DemoLoopStep = 2; m_LevelSetImage = NULL; } // Destructor ~LevelSetPreview2d() { if(m_Driver) delete m_Driver; } // Timer callback, used to regenerate the current contour void OnTimerEvent() { // Clear the output m_CurrentCurve.clear(); // If the driver is dirty, we need to create a new one if(m_DriverDirty && m_Driver != NULL) { delete m_Driver; m_Driver = NULL; } // If the driver is null and all the necessary components exist, create it if(m_Driver == NULL && m_SpeedImage.IsNotNull() && m_Curve.size() > 0) { // Check if we need to allocate the level set image if(m_LevelSetImage.IsNull() || m_LevelSetImage->GetBufferedRegion() != m_SpeedImage->GetBufferedRegion()) { m_LevelSetImage = FloatImageType::New(); m_LevelSetImage->SetRegions(m_SpeedImage->GetBufferedRegion()); m_LevelSetImage->Allocate(); m_ContourDirty = true; } // Check if the contour is dirty, and create a contour image if(m_ContourDirty) { // Scale the contour by the size of the image std::vector points; points.reserve(m_Curve.size()); for(CurveType::iterator it = m_Curve.begin(); it != m_Curve.end(); ++it) { points.push_back(Vector2d( it->x[0] * m_LevelSetImage->GetBufferedRegion().GetSize()[0], it->x[1] * m_LevelSetImage->GetBufferedRegion().GetSize()[1])); } // Fill in the contour in the level set image m_LevelSetImage->FillBuffer(0.0f); typedef PolygonScanConvert< float, GL_FLOAT, std::vector::iterator> ScanConvertType; ScanConvertType::RasterizeFilled( points.begin(), points.size(), m_LevelSetImage); // Ensure that the initial level set is zero typedef itk::ImageRegionIterator IteratorType; IteratorType it2(m_LevelSetImage, m_LevelSetImage->GetBufferedRegion()); for(; !it2.IsAtEnd(); ++it2) it2.Set(it2.Get() > 0 ? -1.0 : 1.0); m_LevelSetImage->Modified(); // The contour is not dirty any more m_ContourDirty = false; } m_Driver = new SNAPLevelSetDriver2d( m_LevelSetImage.GetPointer(), m_SpeedImage, m_Parameters); m_DriverDirty = false; m_DemoLoopTime = 0; } // Now that we've made sure that the driver is OK, run the demo loop if(m_Driver != NULL) { // Run some number of level set evolutions if(m_DemoLoopTime > m_DemoLoopLength) { m_DemoLoopTime = 0; m_Driver->Restart(); } else { m_Driver->Run(m_DemoLoopStep); m_DemoLoopTime += m_DemoLoopStep; } // Initialize the VTK Importer m_VTKExporter = itk::VTKImageExport::New(); m_VTKImporter = vtkImageImport::New(); // Pipe the importer into the exporter (that's a lot of code) m_VTKImporter->SetUpdateInformationCallback( m_VTKExporter->GetUpdateInformationCallback()); m_VTKImporter->SetPipelineModifiedCallback( m_VTKExporter->GetPipelineModifiedCallback()); m_VTKImporter->SetWholeExtentCallback( m_VTKExporter->GetWholeExtentCallback()); m_VTKImporter->SetSpacingCallback( m_VTKExporter->GetSpacingCallback()); m_VTKImporter->SetOriginCallback( m_VTKExporter->GetOriginCallback()); m_VTKImporter->SetScalarTypeCallback( m_VTKExporter->GetScalarTypeCallback()); m_VTKImporter->SetNumberOfComponentsCallback( m_VTKExporter->GetNumberOfComponentsCallback()); m_VTKImporter->SetPropagateUpdateExtentCallback( m_VTKExporter->GetPropagateUpdateExtentCallback()); m_VTKImporter->SetUpdateDataCallback( m_VTKExporter->GetUpdateDataCallback()); m_VTKImporter->SetDataExtentCallback( m_VTKExporter->GetDataExtentCallback()); m_VTKImporter->SetBufferPointerCallback( m_VTKExporter->GetBufferPointerCallback()); m_VTKImporter->SetCallbackUserData( m_VTKExporter->GetCallbackUserData()); // Create and configure the contour filter m_VTKContour = vtkContourFilter::New(); m_VTKContour->SetInput(m_VTKImporter->GetOutput()); m_VTKContour->ReleaseDataFlagOn(); m_VTKContour->ComputeScalarsOff(); m_VTKContour->ComputeGradientsOff(); m_VTKContour->UseScalarTreeOn(); m_VTKContour->SetNumberOfContours(1); m_VTKContour->SetValue(0, 0.0); // Generate a contour m_VTKExporter->SetInput(m_Driver->GetCurrentState()); m_VTKContour->Update(); // Get the list of points representing the evolving contour vtkPolyData *pd = m_VTKContour->GetOutput(); m_CurrentCurve.reserve(pd->GetNumberOfCells() * 2); for(int i=0;iGetNumberOfCells();i++) { vtkFloatingPointType *pt1 = pd->GetPoint(pd->GetCell(i)->GetPointId(0)); m_CurrentCurve.push_back(Vector2d(pt1[0] + 0.5,pt1[1] + 0.5)); vtkFloatingPointType *pt2 = pd->GetPoint(pd->GetCell(i)->GetPointId(1)); m_CurrentCurve.push_back(Vector2d(pt2[0] + 0.5,pt2[1] + 0.5)); } } m_VTKImporter->Delete(); m_VTKContour->Delete(); } // Change the speed image passed as the input to the level set void SetSpeedImage(FloatImageType *image) { if(image != m_SpeedImage) { m_DriverDirty = true; m_ContourDirty = true; m_SpeedImage = image; } } // Set the initial contour curve void SetInitialContour(const CurveType &curve) { m_Curve = curve; m_ContourDirty = true; m_DriverDirty = true; } // Set the snake paramters void SetSnakeParameters(const SnakeParameters ¶meters) { if(!(m_Parameters == parameters)) { m_Parameters = parameters; m_DriverDirty = true; } } // Set the length of the demo loop void SetDemoLoopLength(unsigned int length) { m_DemoLoopLength = length; m_DriverDirty = true; } // Set the step size of the demo loop void SetDemoLoopStep(unsigned int step) { m_DemoLoopStep = step; m_DriverDirty = true; } // See if there is something to display bool IsLevelSetComputed() { return m_Driver != NULL; } // Get the level set image to display FloatImageType *GetLevelSetImage() { return m_Driver->GetCurrentState(); } // Get the evolving contour vector &GetEvolvingContour() { return m_CurrentCurve; } private: // Parameters of the level set algorithm FloatImageType::Pointer m_SpeedImage, m_LevelSetImage; CurveType m_Curve; SnakeParameters m_Parameters; unsigned int m_DemoLoopStep, m_DemoLoopLength, m_DemoLoopTime; // Snake evolution driver pointer SNAPLevelSetDriver2d *m_Driver; // VTK objects for computing a contour typedef itk::VTKImageExport ExporterType; itk::SmartPointer m_VTKExporter; vtkImageImport *m_VTKImporter; vtkContourFilter *m_VTKContour; // The zero level set, as it evolves vector m_CurrentCurve; bool m_DriverDirty, m_ContourDirty; }; void SnakeParametersPreviewPipeline ::AnimationCallback() { // Call the callback on the demo loop m_DemoLoop->OnTimerEvent(); } SnakeParametersPreviewPipeline ::SnakeParametersPreviewPipeline(GlobalState *state) { // Store the global state m_GlobalState = state; // Start with a 100 interpolated points m_NumberOfSampledPoints = 100; m_ControlsModified = false; m_SpeedModified = false; m_ParametersModified = false; m_QuickUpdate = false; // Initialize the parameters m_Parameters = SnakeParameters::GetDefaultEdgeParameters(); // Initialize the display filter m_DisplayMapper = IntensityFilterType::New(); // Set the current color map preset m_ColorMapPreset = m_GlobalState->GetSpeedColorMap(); m_DisplayMapper->SetFunctor( SpeedColorMap::GetPresetColorMap(m_ColorMapPreset)); // Create a new demo loop m_DemoLoop = new LevelSetPreview2d; // The demo loop is not running m_DemoLoopRunning = false; } SnakeParametersPreviewPipeline ::~SnakeParametersPreviewPipeline() { delete m_DemoLoop; } void SnakeParametersPreviewPipeline ::SetControlPoints(const std::vector &points) { if(m_ControlPoints != points) { // Save the points m_ControlPoints = points; // Set the flags m_ControlsModified = true; m_QuickUpdate = false; } } void SnakeParametersPreviewPipeline ::ChangeControlPoint( unsigned int index, const Vector2d &point, bool quickUpdate) { // Update the point assert(index < m_ControlPoints.size()); // Update the point m_ControlPoints[index] = point; // Set the flags m_ControlsModified = true; // Set the update flag m_QuickUpdate = quickUpdate; } void SnakeParametersPreviewPipeline ::SetSpeedImage(FloatImageType *image) { // Update the image internally if(image != m_SpeedImage) { // Set the modified flag m_SpeedModified = true; m_SpeedImage = image; // Create a filter to compute a gradient image typedef itk::GradientImageFilter GradientFilter; GradientFilter::Pointer filter = GradientFilter::New(); // Set up and run the filter filter->SetInput(m_SpeedImage); m_GradientImage = filter->GetOutput(); filter->Update(); // Pass the image to the display functor m_DisplayMapper->SetInput(m_SpeedImage); // Pass the speed image to the preview object m_DemoLoop->SetSpeedImage(image); } } void SnakeParametersPreviewPipeline ::SetSnakeParameters(const SnakeParameters ¶meters) { // Clean up the parameters SnakeParameters clean = parameters; clean.SetClamp(false); clean.SetGround(0); clean.SetLaplacianSpeedExponent(0); clean.SetLaplacianWeight(0); clean.SetSolver(SnakeParameters::PARALLEL_SPARSE_FIELD_SOLVER); // Make the 2D example behave more like 3D ... clean.SetCurvatureWeight(5 * parameters.GetCurvatureWeight()); // Don't waste time on nonsense if(m_Parameters == clean) return; // Save the parameters m_Parameters = clean; m_ParametersModified = true; // Pass the parameters to the demo loop m_DemoLoop->SetSnakeParameters(m_Parameters); } void SnakeParametersPreviewPipeline ::SetNumberOfSampledPoints(unsigned int number) { if(number!=m_NumberOfSampledPoints) { m_NumberOfSampledPoints = number; m_ControlsModified = true; } } void SnakeParametersPreviewPipeline ::Update(Fl_Gl_Window *context) { // Check what work needs to be done if(m_ControlsModified) { UpdateContour(); } if(!m_QuickUpdate) { if(m_ControlsModified) { m_DemoLoop->SetInitialContour(GetSampledPoints()); // UpdateLevelSet(context); } if(m_ParametersModified || m_ControlsModified) { UpdateForces(); m_ParametersModified = false; } } // Clear the modified flags m_ControlsModified = false; // Also, check whether the colormap used for display has changed if(m_ColorMapPreset != m_GlobalState->GetSpeedColorMap()) { m_ColorMapPreset = m_GlobalState->GetSpeedColorMap(); m_DisplayMapper->SetFunctor( SpeedColorMap::GetPresetColorMap(m_ColorMapPreset)); } } void SnakeParametersPreviewPipeline ::UpdateContour() { // Create a b-spline object typedef itk::BSplineInterpolationWeightFunction FunctionType; FunctionType::Pointer function = FunctionType::New(); // Used to compute spline derivatives itk::BSplineKernelFunction<3>::Pointer kf3 = itk::BSplineKernelFunction<3>::New(); itk::BSplineKernelFunction<2>::Pointer kf2 = itk::BSplineKernelFunction<2>::New(); itk::BSplineKernelFunction<2>::Pointer kf1 = itk::BSplineKernelFunction<2>::New(); // Initialize the sampled point array m_SampledPoints.clear(); m_SampledPoints.reserve(m_NumberOfSampledPoints); int uMax = m_ControlPoints.size() - 3; for(double t = 0; t < 1.0; t += 0.005) { double s = t * uMax; // The starting index // int si = ((int)(t * uMax)) - 1; int sidx = (int) floor(s - 1); double u = s - sidx; // Compute the position and derivatives of the b-spline Vector2d x(0.0f,0.0f); Vector2d xu(0.0f,0.0f); Vector2d xuu(0.0f,0.0f); for(int k=0; k < 4; k++) { double w = kf3->Evaluate(u); double wu = kf2->Evaluate(u+0.5) - kf2->Evaluate(u-0.5); double wuu = kf1->Evaluate(u+1) + kf1->Evaluate(u-1) - 2 * kf1->Evaluate(u); u-=1.0; int idx = (uMax + sidx + k) % uMax; x += w * m_ControlPoints[idx]; xu += wu * m_ControlPoints[idx]; xuu += wuu * m_ControlPoints[idx]; } // Create and save the point SampledPoint pt; pt.x = x; pt.t = t; xu.normalize(); pt.n = Vector2d(-xu[1],xu[0]); pt.PropagationForce = pt.CurvatureForce = pt.AdvectionForce = 0.0; pt.kappa = (xu[0] * xuu[1] - xu[1] * xuu[0]) / pow(xu[0]*xu[0] + xu[1]*xu[1],1.5); m_SampledPoints.push_back(pt); } } void SnakeParametersPreviewPipeline ::UpdateLevelSetFunction() { } void SnakeParametersPreviewPipeline ::UpdateLevelSet(Fl_Gl_Window *irisNotUsed(context)) { } void SnakeParametersPreviewPipeline ::UpdateForces() { // Image interpolator types typedef itk::LinearInterpolateImageFunction< FloatImageType,double> LerpType; typedef itk::VectorLinearInterpolateImageFunction< VectorImageType,double> VectorLerpType; // Create the speed image interpolator LerpType::Pointer sLerp = LerpType::New(); sLerp->SetInputImage(m_SpeedImage); // Create the gradient image interpolator VectorLerpType::Pointer gLerp = VectorLerpType::New(); gLerp->SetInputImage(m_GradientImage); // Get the image dimensions itk::Size<2> idim = m_SpeedImage->GetBufferedRegion().GetSize(); // Compute the geometry of each point for(unsigned int i = 0; i < m_SampledPoints.size(); i++) { // A reference so we can access the point in shorthand SampledPoint &p = m_SampledPoints[i]; // We're done computing the geometric properties of the curve. Now, let's // compute the image-related quantities. First, convert the point to image // coordinates LerpType::ContinuousIndexType idx; idx[0] = idim[0] * p.x[0]; idx[1] = idim[1] * p.x[1]; // Get the value of the g function double g = sLerp->EvaluateAtContinuousIndex(idx); // Get the value of the gradient VectorLerpType::OutputType gradG = gLerp->EvaluateAtContinuousIndex(idx); // Compute the propagation force component of the curve evolution p.PropagationForce = m_Parameters.GetPropagationWeight() * pow(g,m_Parameters.GetPropagationSpeedExponent()); // Compute the curvature force component of the curve evolution p.CurvatureForce = m_Parameters.GetCurvatureWeight() * p.kappa * pow(g,m_Parameters.GetCurvatureSpeedExponent()+1); // Compute the advection force component of the curve evolution p.AdvectionForce = - m_Parameters.GetAdvectionWeight() * (p.n[0] * gradG[0] + p.n[1] * gradG[1]) * pow(g,m_Parameters.GetAdvectionSpeedExponent()); } } std::vector & SnakeParametersPreviewPipeline ::GetDemoLoopContour() { return m_DemoLoop->GetEvolvingContour(); } itksnap/UserInterface/BasicComponents/SnakeParametersPreviewPipeline.h0000644000076500000240000001576011136422002025660 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParametersPreviewPipeline.h,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ 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 __SnakeParametersPreviewPipeline_h_ #define __SnakeParametersPreviewPipeline_h_ #include "SNAPOpenGL.h" #include "SNAPCommonUI.h" #include "itkOrientedImage.h" #include "itkRGBAPixel.h" #include "itkCovariantVector.h" #include "itkUnaryFunctorImageFilter.h" #include "SnakeParameters.h" #include "SpeedColorMap.h" template class SignedDistanceFilter; template class SNAPLevelSetFunction; template class LevelSetExtensionFilter; class Fl_Gl_Window; class vtkImageImport; class vtkContourFilter; class LevelSetPreviewPipeline2D; namespace itk { template class ParallelSparseFieldLevelSetImageFilter; }; class LevelSetPreview2d; // #define SNAKE_PREVIEW_ADVANCED 1 /** * \class SnakeParametersPreviewPipeline * \brief A pipeline used to preview snake parameters. * * Given a set of control points an image, and some snake parameters, this * class computes a b-spline curve based on those control points, creates a * level set embedding of the curve, and computes various level set evolution * forces acting on the curve. */ class SnakeParametersPreviewPipeline { public: SnakeParametersPreviewPipeline(GlobalState *state); virtual ~SnakeParametersPreviewPipeline(); // Images used by this class (internally and externally) typedef itk::OrientedImage CharImageType; typedef itk::OrientedImage FloatImageType; // Define a color image for display typedef itk::RGBAPixel DisplayPixelType; typedef itk::OrientedImage DisplayImageType; // Index type used to refer to pixels typedef FloatImageType::IndexType IndexType; // Force types enum ForceType {CURVATURE=0, ADVECTION, PROPAGATION, TOTAL }; // A sample from the curve struct SampledPoint { // The geometry of the point double t; Vector2d x; Vector2d n; double kappa; // The forces acting on the point double PropagationForce; double CurvatureForce; double AdvectionForce; double operator[](unsigned int i) const { return x[i]; } }; // Various list types typedef std::vector ControlPointList; typedef std::vector LevelSetContourType; typedef std::vector SampledPointList; /** Set the speed image */ void SetSpeedImage(FloatImageType *image); /** Set the snake parameters */ void SetSnakeParameters(const SnakeParameters ¶meters); /** Set the control points of the interface curve */ void SetControlPoints(const ControlPointList &points); /** Change just one control point, with an option of changing it * quickly, ie, not recomputing the level set, only the curve */ void ChangeControlPoint(unsigned int index, const Vector2d &point, bool quickUpdate); /** Set the number of points sampled for display of the curve */ void SetNumberOfSampledPoints(unsigned int number); /** Update the internals of the pipeline and compute the curve and the * force points. This method requires a GL context because it relies on * GL tesselation code for generating an image from the curve */ void Update(Fl_Gl_Window *context); /** Get the speed image */ irisGetMacro(SpeedImage,FloatImageType *); /** Get a reference to the control points of the interface curve */ irisGetMacro(ControlPoints,const ControlPointList &); /** Get a list of densely interpolated points on the curve (for drawing) */ irisGetMacro(SampledPoints,const SampledPointList &); /** Is the demo loop running ? */ irisIsMacro(DemoLoopRunning); irisSetMacro(DemoLoopRunning, bool); /** Get the demo loop contour */ std::vector &GetDemoLoopContour(); /** Get the color image corresponding to the speed image */ DisplayImageType *GetDisplayImage() { return m_DisplayMapper->GetOutput(); } /** Set the idle callback function that FLTK should call in demo mode */ void AnimationCallback(); private: /** The global state */ GlobalState *m_GlobalState; /** The speed image */ itk::SmartPointer m_SpeedImage; // Gradient image used by this component typedef itk::CovariantVector VectorType; typedef itk::Image VectorImageType; typedef itk::SmartPointer VectorImagePointer; /** The grandient of the speed image */ VectorImagePointer m_GradientImage; /** A set of snake parameters */ SnakeParameters m_Parameters; /** A list of control points */ ControlPointList m_ControlPoints; /** Number of points to sample from the curve */ unsigned int m_NumberOfSampledPoints; /** A list of sampled points */ SampledPointList m_SampledPoints; // Flags indicating which part of the pipeline should be refreshed bool m_ControlsModified; bool m_SpeedModified; bool m_ParametersModified; bool m_QuickUpdate; // Internal components of the Update method void UpdateLevelSetFunction(); void UpdateContour(); void UpdateLevelSet(Fl_Gl_Window *context); void UpdateForces(); void Update(); // A filter used to convert the speed image to a color image to display on the screen typedef itk::UnaryFunctorImageFilter< FloatImageType,DisplayImageType,SpeedColorMap> IntensityFilterType; typedef itk::SmartPointer IntensityFilterPointer; IntensityFilterPointer m_DisplayMapper; // Demo loop object LevelSetPreview2d *m_DemoLoop; // Whether the demo loop is currently running bool m_DemoLoopRunning; // The color map preset currently in use for speed image rendering ColorMapPreset m_ColorMapPreset; }; #endif // __SnakeParametersPreviewPipeline_h_ itksnap/UserInterface/BasicComponents/SNAPFormattedOutput.h0000644000076500000240000000420511405725000023366 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPFormattedOutput.h,v $ Language: C++ Date: $Date: 2010/06/15 16:27:44 $ 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 __SNAPFormattedOutput_h_ #define __SNAPFormattedOutput_h_ #include "FL/Fl_Output.H" #include #include /** * \class SNAPFormattedOutput * \brief An extension of Fl_Output that can take double as input */ class SNAPFormattedOutput : public Fl_Output { public: SNAPFormattedOutput(int X, int Y, int W, int H,const char *l=0) : Fl_Output(X,Y,W,H,l) { set_format("%.4g"); } void set_format(const char *format) { strncpy(m_Format, format, 32); } const char *get_format() const { return m_Format; } void value(double val) { sprintf(m_Buffer, m_Format, val); Fl_Output::value(m_Buffer); } void value(double val, const char *format) { set_format(format); value(val); } protected: char m_Format[32], m_Buffer[128]; }; #endif itksnap/UserInterface/BasicComponents/SNAPValueOutput.h0000644000076500000240000000402211405725000022512 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPValueOutput.h,v $ Language: C++ Date: $Date: 2010/06/15 16:27:44 $ 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 __SNAPValueOutput_h_ #define __SNAPValueOutput_h_ #include "FL/Fl_Value_Output.H" #include /** * \class SNAPValueOutput * \brief An extension of Fl_Value_Output with specified number of significant * digits (uses %.*g format) */ class SNAPValueOutput : public Fl_Value_Output { public: SNAPValueOutput(int X, int Y, int W, int H,const char *l=0) : Fl_Value_Output(X,Y,W,H,l) { this->step(4); } protected: int format(char *buffer) { double s = step(); if(s >= 1.0) return sprintf(buffer, "%.*g", (int) s, this->value()); else return Fl_Value_Output::format(buffer); } int handle(int event) { return 0; } }; #endif itksnap/UserInterface/BasicComponents/StatisticsTable.cxx0000644000076500000240000001231411401011605023206 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: StatisticsTable.cxx,v $ Language: C++ Date: $Date: 2010/05/31 19:52:37 $ Version: $Revision: 1.1 $ 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 "StatisticsTable.h" #include "FL/fl_draw.H" using namespace std; void StatisticsTable ::SetSegmentationStatistics( const SegmentationStatistics &data, const ColorLabelTable &clt) { static char buffer[256]; // Get the data m_Header.clear(); m_Body.clear(); // Get first entry SegmentationStatistics::Entry e = data.GetStats()[0]; // Set the columns m_Header.push_back("Label"); m_Header.push_back("Label Id"); m_Header.push_back("# Voxels"); m_Header.push_back("Volume (mm^3)"); for(size_t i = 0; i < e.gray.size(); i++) { sprintf(buffer, "Mean(%s)", e.gray[i].layer_id.c_str()); m_Header.push_back(buffer); sprintf(buffer, "S.D.(%s)", e.gray[i].layer_id.c_str()); m_Header.push_back(buffer); } // Set the body of the table for(size_t i = 0; i < MAX_COLOR_LABELS; i++) { SegmentationStatistics::Entry e = data.GetStats()[i]; const ColorLabel &cl = clt.GetColorLabel(i); if(e.count == 0) continue; vector m_Row; m_Row.push_back(cl.GetLabel()); sprintf(buffer, "%d", (int) i); m_Row.push_back(buffer); sprintf(buffer, "%d", (int) e.count); m_Row.push_back(buffer); sprintf(buffer, "%g", e.volume_mm3); m_Row.push_back(buffer); for(size_t i = 0; i < e.gray.size(); i++) { sprintf(buffer, "%g", e.gray[i].mean); m_Row.push_back(buffer); sprintf(buffer, "%g", e.gray[i].sd); m_Row.push_back(buffer); } m_Body.push_back(m_Row); } // Set the number of rows and columns this->rows(m_Body.size()); this->cols(m_Header.size()-1); this->row_header(1); this->col_header(1); this->col_resize(4); // Set the column widths (allow for scrollbar) this->col_width_all(100); if(m_Body.size() > 8) this->row_header_width(144); else this->row_header_width(160); } std::string StatisticsTable ::CopySelection() { ostringstream oss; // Count the number of selected rows size_t nsel = 0; for(size_t r = 0; r < m_Body.size(); r++) if(this->row_selected(r)) nsel++; // If no selection, include the header too if(nsel == 0) { for(size_t j = 0; j < m_Header.size(); j++) { if(j > 0) oss << "\t"; oss << m_Header[j]; } oss << endl; } // Go through the selected rows for(size_t r = 0; r < m_Body.size(); r++) { if(this->row_selected(r) || nsel == 0) { for(size_t j = 0; j < m_Body[r].size(); j++) { if(j > 0) oss << "\t"; oss << m_Body[r][j]; } oss << endl; } } return oss.str(); } void StatisticsTable ::draw_cell(TableContext context, int R, int C, int X, int Y, int W, int H) { string text; // If data is not initialized, return if(m_Header.size() == 0) return; // Actual drawing switch(context) { case CONTEXT_STARTPAGE: fl_font(FL_COURIER, 12); return; case CONTEXT_ROW_HEADER: text = m_Body[R][0]; fl_push_clip(X, Y, W, H); fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, FL_LIGHT1); fl_pop_clip(); fl_push_clip(X+3, Y, W-6, H); fl_color(FL_BLACK); fl_draw(text.c_str(), X+3, Y, W-6, H, FL_ALIGN_LEFT); fl_pop_clip(); break; case CONTEXT_COL_HEADER: text = m_Header[C+1]; fl_push_clip(X, Y, W, H); { fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, FL_LIGHT1); fl_color(FL_BLACK); fl_draw(text.c_str(), X, Y, W, H, FL_ALIGN_CENTER); } fl_pop_clip(); break; case CONTEXT_CELL: text = m_Body[R][C+1]; fl_push_clip(X, Y, W, H); fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, row_selected(R) ? selection_color() : FL_WHITE); fl_pop_clip(); fl_push_clip(X+3, Y, W-6, H); { // TEXT fl_color(FL_BLACK); fl_draw(text.c_str(), X+3, Y, W-6, H, FL_ALIGN_RIGHT); } fl_pop_clip(); break; default: break; } } itksnap/UserInterface/BasicComponents/StatisticsTable.h0000644000076500000240000000414211401011605022633 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: StatisticsTable.h,v $ Language: C++ Date: $Date: 2010/05/31 19:52:37 $ Version: $Revision: 1.1 $ 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 __StatisticsTable_h_ #define __StatisticsTable_h_ #include "FL/Fl_Table.H" #include "FL/Fl_Table_Row.H" #include "SNAPCommon.h" #include "SegmentationStatistics.h" #include "ColorLabelTable.h" class StatisticsTable : public Fl_Table_Row { public: StatisticsTable(int x, int y, int w, int h, const char *l=0) : Fl_Table_Row(x,y,w,h,l) { end(); } ~StatisticsTable() { } void SetSegmentationStatistics( const SegmentationStatistics &data, const ColorLabelTable &clt); std::string CopySelection(); protected: /* Draw table cell */ void draw_cell(TableContext context, int R=0, int C=0, int X=0, int Y=0, int W=0, int H=0); std::vector< std::string > m_Header; std::vector< std::vector< std::string > > m_Body; }; #endif itksnap/UserInterface/Common/0000755000076500000240000000000011560342171015533 5ustar paulystaffitksnap/UserInterface/Common/CVS/0000755000076500000240000000000011560342171016166 5ustar paulystaffitksnap/UserInterface/Common/CVS/Entries0000644000076500000240000000024711560342171017525 0ustar paulystaff/SNAPAppearanceSettings.cxx/1.13/Tue Oct 19 20:28:56 2010// /SNAPAppearanceSettings.h/1.12/Tue Oct 19 20:28:56 2010// /SNAPCommonUI.h/1.2/Sun Dec 30 04:05:16 2007// D itksnap/UserInterface/Common/CVS/Repository0000644000076500000240000000003511560342171020266 0ustar paulystaffitksnap/UserInterface/Common itksnap/UserInterface/Common/CVS/Root0000644000076500000240000000010011560342171017023 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/Common/SNAPAppearanceSettings.cxx0000644000076500000240000003267511457377610022551 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPAppearanceSettings.cxx,v $ Language: C++ Date: $Date: 2010/10/19 20:28:56 $ 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 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 "SNAPAppearanceSettings.h" #include "Registry.h" #include "FL/gl.h" using namespace std; // Columns: NORMAL_COLOR, ACTIVE_COLOR, LINE_THICKNESS, DASH_SPACING, // FONT_SIZE, VISIBLE, ALPHA_BLEND, FEATURE_COUNT const int SNAPAppearanceSettings ::m_Applicable[SNAPAppearanceSettings::ELEMENT_COUNT][SNAPAppearanceSettings::FEATURE_COUNT] = { { 1, 0, 1, 1, 0, 1, 1 }, // Crosshairs { 1, 0, 0, 0, 1, 1, 1 }, // Markers { 1, 1, 1, 1, 0, 0, 1 }, // ROI { 1, 0, 0, 0, 0, 0, 0 }, // Slice Background { 1, 0, 0, 0, 0, 0, 0 }, // 3D Background { 1, 1, 1, 1, 0, 1, 1 }, // Zoom thumbnail { 1, 0, 1, 1, 0, 1, 1 }, // 3D Crosshairs { 1, 0, 1, 1, 0, 1, 1 }, // Thumbnail Crosshairs { 1, 1, 1, 1, 0, 1, 1 }, // 3D Image Box { 1, 1, 1, 1, 0, 1, 1 }, // 3D ROI Box { 1, 1, 1, 1, 0, 1, 1 }, // Paintbrush outline { 1, 0, 1, 0, 1, 1, 1 }, // Rulers { 1, 0, 1, 1, 0, 0, 1 }, // POLY_DRAW_MAIN { 1, 0, 1, 1, 0, 1, 1 }, // POLY_DRAW_CLOSE { 1, 1, 1, 1, 0, 0, 1 } // POLY_EDIT }; SNAPAppearanceSettings::Element SNAPAppearanceSettings ::m_DefaultElementSettings[SNAPAppearanceSettings::ELEMENT_COUNT]; void SNAPAppearanceSettings ::InitializeDefaultSettings() { // An element pointer for setting properties Element *elt; // Crosshairs elt = &m_DefaultElementSettings[CROSSHAIRS]; elt->NormalColor = Vector3d(0.3, 0.3, 1.0); elt->ActiveColor = Vector3d(0.0, 0.0, 0.0); elt->LineThickness = 1.0; elt->DashSpacing = 1.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // Markers elt = &m_DefaultElementSettings[MARKERS]; elt->NormalColor = Vector3d(1.0, 0.75, 0.0); elt->ActiveColor = Vector3d(0.0, 0.0, 0.0); elt->LineThickness = 0.0; elt->DashSpacing = 0.0; elt->FontSize = 16; elt->Visible = true; elt->AlphaBlending = false; // ROI elt = &m_DefaultElementSettings[ROI_BOX]; elt->NormalColor = Vector3d(1.0, 0.0, 0.2); elt->ActiveColor = Vector3d(1.0, 1.0, 0.2); elt->LineThickness = 1.0; elt->DashSpacing = 3.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // Slice background elt = &m_DefaultElementSettings[BACKGROUND_3D]; elt->NormalColor = Vector3d(0.0, 0.0, 0.0); elt->ActiveColor = Vector3d(0.0, 0.0, 0.0); elt->LineThickness = 0.0; elt->DashSpacing = 0.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // 3D Window background elt = &m_DefaultElementSettings[BACKGROUND_3D]; elt->NormalColor = Vector3d(0.0, 0.0, 0.0); elt->ActiveColor = Vector3d(0.0, 0.0, 0.0); elt->LineThickness = 0.0; elt->DashSpacing = 0.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // Zoom thumbail elt = &m_DefaultElementSettings[ZOOM_THUMBNAIL]; elt->NormalColor = Vector3d(1.0, 1.0, 0.0); elt->ActiveColor = Vector3d(1.0, 1.0, 1.0); elt->LineThickness = 1.0; elt->DashSpacing = 0.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // 3D crosshairs elt = &m_DefaultElementSettings[CROSSHAIRS_3D]; elt->NormalColor = Vector3d(0.3, 0.3, 1.0); elt->ActiveColor = Vector3d(0.0, 0.0, 0.0); elt->LineThickness = 1.0; elt->DashSpacing = 1.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = true; // Thumbnail crosshairs elt = &m_DefaultElementSettings[CROSSHAIRS_THUMB]; elt->NormalColor = Vector3d(0.3, 0.3, 1.0); elt->ActiveColor = Vector3d(0.0, 0.0, 0.0); elt->LineThickness = 1.0; elt->DashSpacing = 1.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // Thumbnail crosshairs elt = &m_DefaultElementSettings[IMAGE_BOX_3D]; elt->NormalColor = Vector3d(0.2, 0.2, 0.2); elt->ActiveColor = Vector3d(0.4, 0.4, 0.4); elt->LineThickness = 1.0; elt->DashSpacing = 1.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // Thumbnail crosshairs elt = &m_DefaultElementSettings[ROI_BOX_3D]; elt->NormalColor = Vector3d(1.0, 0.0, 0.2); elt->ActiveColor = Vector3d(1.0, 1.0, 0.2); elt->LineThickness = 1.0; elt->DashSpacing = 3.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // Paintbrush outline elt = &m_DefaultElementSettings[PAINTBRUSH_OUTLINE]; elt->NormalColor = Vector3d(1.0, 0.0, 0.2); elt->ActiveColor = Vector3d(1.0, 1.0, 0.2); elt->LineThickness = 1.0; elt->DashSpacing = 1.0; elt->FontSize = 0; elt->Visible = true; elt->AlphaBlending = false; // Markers elt = &m_DefaultElementSettings[RULER]; elt->NormalColor = Vector3d(0.3, 1.0, 0.0); elt->ActiveColor = Vector3d(0.0, 0.0, 0.0); elt->LineThickness = 1.0; elt->DashSpacing = 0.0; elt->FontSize = 12; elt->Visible = true; elt->AlphaBlending = true; // Polygon outline (drawing) elt = &m_DefaultElementSettings[POLY_DRAW_MAIN]; elt->NormalColor = Vector3d(1.0, 0.0, 0.5); elt->LineThickness = 2.0; elt->Visible = true; elt->AlphaBlending = true; // Polygon outline (drawing) elt = &m_DefaultElementSettings[POLY_DRAW_CLOSE]; elt->NormalColor = Vector3d(1.0, 0.0, 0.5); elt->LineThickness = 2.0; elt->Visible = true; elt->DashSpacing = 1.0; elt->AlphaBlending = true; // Polygon outline (editing) elt = &m_DefaultElementSettings[POLY_EDIT]; elt->NormalColor = Vector3d(1.0, 0.0, 0.0); elt->ActiveColor = Vector3d(0.0, 1.0, 0.0); elt->LineThickness = 2.0; elt->Visible = true; elt->AlphaBlending = true; } const char * SNAPAppearanceSettings ::m_ElementNames[SNAPAppearanceSettings::ELEMENT_COUNT] = { "CROSSHAIRS", "MARKERS", "ROI_BOX", "BACKGROUND_2D", "BACKGROUND_3D", "ZOOM_THUMBNAIL", "CROSSHAIRS_3D", "CROSSHAIRS_THUMB", "IMAGE_BOX_3D", "ROI_BOX_3D", "RULER", "PAINTBRUSH_OUTLINE", "POLY_DRAW_MAIN", "POLY_DRAW_CLOSE", "POLY_EDIT"}; SNAPAppearanceSettings ::SNAPAppearanceSettings() { // Initialize the default settings InitializeDefaultSettings(); // Set the common flags m_FlagLinkedZoomByDefault = true; m_FlagMultisessionZoomByDefault = true; m_FlagMultisessionPanByDefault = true; m_FlagFloatingPointWarningByDefault = true; m_FlagEnableHiddenFeaturesByDefault = false; m_FlagEnableAutoCheckForUpdateByDefault = -1; m_ZoomThumbnailMaximumSize = 160; m_ZoomThumbnailSizeInPercent = 30.0; m_FlagDisplayZoomThumbnail = true; m_GreyInterpolationMode = NEAREST; m_SliceLayout = LAYOUT_ASC; m_FlagLayoutPatientAnteriorShownLeft = true; m_FlagLayoutPatientRightShownLeft = true; m_FlagAutoPan = false; m_EnumMapInterpolationMode.AddPair(NEAREST,"NearestNeighbor"); m_EnumMapInterpolationMode.AddPair(LINEAR,"Linear"); m_EnumMapSliceLayout.AddPair(LAYOUT_ASC,"ASC"); m_EnumMapSliceLayout.AddPair(LAYOUT_ACS,"ACS"); m_EnumMapSliceLayout.AddPair(LAYOUT_SAC,"SAC"); m_EnumMapSliceLayout.AddPair(LAYOUT_SCA,"SCA"); m_EnumMapSliceLayout.AddPair(LAYOUT_CAS,"CAS"); m_EnumMapSliceLayout.AddPair(LAYOUT_CSA,"CSA"); // Set the UI elements to their default values for(unsigned int iElement = 0; iElement < ELEMENT_COUNT; iElement++) m_Elements[iElement] = m_DefaultElementSettings[iElement]; // Initial visibility is true m_OverallVisibility = true; } void SNAPAppearanceSettings ::LoadFromRegistry(Registry &r) { // Load the flags and settings m_FlagDisplayZoomThumbnail = r["FlagDisplayZoomThumbnail"][m_FlagDisplayZoomThumbnail]; m_FlagLinkedZoomByDefault = r["FlagLinkedZoomByDefault"][m_FlagLinkedZoomByDefault]; m_FlagMultisessionZoomByDefault = r["FlagMultisessionZoomByDefault"][m_FlagMultisessionZoomByDefault]; m_FlagMultisessionPanByDefault = r["FlagMultisessionPanByDefault"][m_FlagMultisessionPanByDefault]; m_FlagFloatingPointWarningByDefault = r["FlagFloatingPointWarningByDefault"][m_FlagFloatingPointWarningByDefault]; m_FlagEnableHiddenFeaturesByDefault = r["FlagEnableHiddenFeaturesByDefault"][m_FlagEnableHiddenFeaturesByDefault]; m_FlagEnableAutoCheckForUpdateByDefault = r["FlagEnableAutoCheckForUpdateByDefault"][m_FlagEnableAutoCheckForUpdateByDefault]; m_FlagAutoPan = r["FlagAutoPan"][m_FlagAutoPan]; m_ZoomThumbnailSizeInPercent = r["ZoomThumbnailSizeInPercent"][m_ZoomThumbnailSizeInPercent]; m_ZoomThumbnailMaximumSize = r["ZoomThumbnailMaximumSize"][m_ZoomThumbnailMaximumSize]; m_GreyInterpolationMode = r["GreyImageInterpolationMode"].GetEnum(m_EnumMapInterpolationMode, NEAREST); // Overall visibility is not saved or loaded // Read slice layout information m_SliceLayout = r["SliceLayout"].GetEnum(m_EnumMapSliceLayout, LAYOUT_ASC); m_FlagLayoutPatientAnteriorShownLeft = r["PatientAnteriorShownLeft"][true]; m_FlagLayoutPatientRightShownLeft = r["PatientRightShownLeft"][true]; // Load the user interface elements for(unsigned int iElement = 0; iElement < ELEMENT_COUNT; iElement++) { // Create a folder to hold the element Registry& f = r.Folder( r.Key("UserInterfaceElement[%s]", m_ElementNames[iElement]) ); // Get the default element const Element &def = m_DefaultElementSettings[iElement]; Element &elt = m_Elements[iElement]; // Store the element in the folder elt.NormalColor = f["NormalColor"][def.NormalColor]; elt.ActiveColor = f["ActiveColor"][def.ActiveColor]; elt.LineThickness = f["LineThickness"][def.LineThickness]; elt.DashSpacing = f["DashSpacing"][def.DashSpacing]; elt.FontSize = f["FontSize"][def.FontSize]; elt.AlphaBlending = f["AlphaBlending"][def.AlphaBlending]; elt.Visible = f["Visible"][def.Visible]; } } void SNAPAppearanceSettings ::SaveToRegistry(Registry &r) { // Save the flags and settings r["FlagDisplayZoomThumbnail"] << m_FlagDisplayZoomThumbnail; r["FlagLinkedZoomByDefault"] << m_FlagLinkedZoomByDefault; r["FlagMultisessionZoomByDefault"] << m_FlagMultisessionZoomByDefault; r["FlagMultisessionPanByDefault"] << m_FlagMultisessionPanByDefault; r["FlagFloatingPointWarningByDefault"] << m_FlagFloatingPointWarningByDefault; r["FlagEnableHiddenFeaturesByDefault"] << m_FlagEnableHiddenFeaturesByDefault; r["FlagEnableAutoCheckForUpdateByDefault"] << m_FlagEnableAutoCheckForUpdateByDefault; r["FlagAutoPan"] << m_FlagAutoPan; r["ZoomThumbnailSizeInPercent"] << m_ZoomThumbnailSizeInPercent; r["ZoomThumbnailMaximumSize"] << m_ZoomThumbnailMaximumSize; r["GreyImageInterpolationMode"].PutEnum(m_EnumMapInterpolationMode, m_GreyInterpolationMode); // Overall visibility is not saved or loaded // Write slice layout information r["SliceLayout"].PutEnum(m_EnumMapSliceLayout, m_SliceLayout); r["PatientAnteriorShownLeft"] << m_FlagLayoutPatientAnteriorShownLeft; r["PatientRightShownLeft"] << m_FlagLayoutPatientRightShownLeft; // Save each of the screen elements for(unsigned int iElement = 0; iElement < ELEMENT_COUNT; iElement++) { // Create a folder to hold the element Registry& f = r.Folder( r.Key("UserInterfaceElement[%s]", m_ElementNames[iElement]) ); // Get the default element Element &elt = m_Elements[iElement]; // Store the element in the folder f["NormalColor"] << elt.NormalColor; f["ActiveColor"] << elt.ActiveColor; f["LineThickness"] << elt.LineThickness; f["DashSpacing"] << elt.DashSpacing; f["FontSize"] << elt.FontSize; f["AlphaBlending"] << elt.AlphaBlending; f["Visible"] << elt.Visible; } } void SNAPAppearanceSettings ::ApplyUIElementLineSettings(const Element &elt, bool applyThickness, bool applyStipple) { // Apply the thickness properties if(applyThickness) { // Choose whether to use blending or not if( elt.AlphaBlending ) { glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } glLineWidth( elt.LineThickness ); } if(applyStipple && elt.DashSpacing != 0) { // Set the line thickness and stipple glEnable(GL_LINE_STIPPLE); glLineStipple( static_cast(elt.DashSpacing), 0x9999 ); // 0011 0011 0011 0011 // 1001 1001 1001 1001 } } void SNAPAppearanceSettings ::GetAnatomyToDisplayTransforms(string &rai1, string &rai2, string &rai3) { unsigned int order[6][3] = {{0,1,2},{0,2,1},{1,0,2},{1,2,0},{2,0,1},{2,1,0}}; // Start with stock orientations string axes[3] = {string("RPS"),string("AIL"),string("RIP")}; // Switch the configurable directions if(!m_FlagLayoutPatientRightShownLeft) { axes[0][0] = axes[2][0] = 'L'; } if(!m_FlagLayoutPatientAnteriorShownLeft) { axes[1][0] = 'P'; } // Convert layout index to integer size_t i = (size_t) m_SliceLayout; // Set the axes rai1 = axes[order[i][0]]; rai2 = axes[order[i][1]]; rai3 = axes[order[i][2]]; } itksnap/UserInterface/Common/SNAPAppearanceSettings.h0000644000076500000240000001574011457377610022170 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPAppearanceSettings.h,v $ Language: C++ Date: $Date: 2010/10/19 20:28:56 $ Version: $Revision: 1.12 $ 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 __SNAPAppearanceSettings_h_ #define __SNAPAppearanceSettings_h_ #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif // Include the common items from the logic part of SNAP #include "SNAPCommonUI.h" #include "Registry.h" #include /** * \class SNAPAppearanceSettings * \brief User interface settings that the user can configure */ class SNAPAppearanceSettings { public: /** * A structure that describes the appearance of a screen element */ struct Element { Vector3d NormalColor; Vector3d ActiveColor; double LineThickness; double DashSpacing; int FontSize; bool Visible, AlphaBlending; }; /** An enumeration of available screen elements */ enum UIElements { CROSSHAIRS = 0, MARKERS, ROI_BOX, BACKGROUND_2D, BACKGROUND_3D, ZOOM_THUMBNAIL, CROSSHAIRS_3D, CROSSHAIRS_THUMB, IMAGE_BOX_3D, ROI_BOX_3D, PAINTBRUSH_OUTLINE, RULER, POLY_DRAW_MAIN, POLY_DRAW_CLOSE, POLY_EDIT, ELEMENT_COUNT }; /** An enumeration of the fields that an element may possess */ enum UIElementFeatures { NORMAL_COLOR = 0, ACTIVE_COLOR, LINE_THICKNESS, DASH_SPACING, FONT_SIZE, VISIBLE, ALPHA_BLEND, FEATURE_COUNT }; /** Enumeration of interpolation modes */ enum UIGreyInterpolation { NEAREST = 0, LINEAR }; /** Enumeration of 2D display layouts */ enum UISliceLayout { LAYOUT_ASC = 0, LAYOUT_ACS, LAYOUT_SAC, LAYOUT_SCA, LAYOUT_CAS, LAYOUT_CSA, LAYOUT_COUNT }; SNAPAppearanceSettings(); virtual ~SNAPAppearanceSettings() {} void LoadFromRegistry(Registry ®istry); void SaveToRegistry(Registry ®istry); // Access a user interface element Element &GetUIElement(unsigned int iElement) { return m_Elements[iElement]; } // Set a user interface element void SetUIElement(unsigned int iElement, const Element &value) { m_Elements[iElement] = value; } // Check whether the feature is applicable to an element static bool IsFeatureApplicable(unsigned int iElement, unsigned int iFeature) { return m_Applicable[iElement][iFeature] != 0; } // Apply the GL settings associated with an appearance element static void ApplyUIElementLineSettings(const Element &elt, bool applyThickness = true, bool applyStipple = true); irisGetMacro(FlagDisplayZoomThumbnail, bool); irisSetMacro(FlagDisplayZoomThumbnail, bool); irisGetMacro(FlagLinkedZoomByDefault, bool); irisSetMacro(FlagLinkedZoomByDefault, bool); irisGetMacro(FlagMultisessionZoomByDefault, bool); irisSetMacro(FlagMultisessionZoomByDefault, bool); irisGetMacro(FlagMultisessionPanByDefault, bool); irisSetMacro(FlagMultisessionPanByDefault, bool); irisGetMacro(FlagFloatingPointWarningByDefault, bool); irisSetMacro(FlagFloatingPointWarningByDefault, bool); irisGetMacro(FlagEnableAutoCheckForUpdateByDefault, int); irisSetMacro(FlagEnableAutoCheckForUpdateByDefault, int); irisGetMacro(FlagEnableHiddenFeaturesByDefault, bool); irisSetMacro(FlagEnableHiddenFeaturesByDefault, bool); irisGetMacro(ZoomThumbnailSizeInPercent, double); irisSetMacro(ZoomThumbnailSizeInPercent, double); irisGetMacro(ZoomThumbnailMaximumSize, int); irisSetMacro(ZoomThumbnailMaximumSize, int); irisGetMacro(GreyInterpolationMode, UIGreyInterpolation); irisSetMacro(GreyInterpolationMode, UIGreyInterpolation); irisGetMacro(FlagLayoutPatientAnteriorShownLeft, bool); irisSetMacro(FlagLayoutPatientAnteriorShownLeft, bool); irisGetMacro(FlagLayoutPatientRightShownLeft, bool); irisSetMacro(FlagLayoutPatientRightShownLeft, bool); irisGetMacro(FlagAutoPan, bool); irisSetMacro(FlagAutoPan, bool); irisGetMacro(SliceLayout, UISliceLayout); irisSetMacro(SliceLayout, UISliceLayout); irisGetMacro(OverallVisibility, bool); irisSetMacro(OverallVisibility, bool); /** * This method uses SliceLayout, FlagLayoutPatientAnteriorShownLeft and * FlagLayoutPatientRightShownLeft to generate RAI codes for the three * display views. * Use in conjunction with IRISApplication::SetDisplayToAnatomyRAI */ void GetAnatomyToDisplayTransforms(std::string &rai1, std::string &rai2, std::string &rai3); private: // Global settings bool m_FlagDisplayZoomThumbnail; bool m_FlagLinkedZoomByDefault; bool m_FlagMultisessionZoomByDefault; bool m_FlagMultisessionPanByDefault; bool m_FlagFloatingPointWarningByDefault; bool m_FlagEnableHiddenFeaturesByDefault; bool m_FlagAutoPan; int m_FlagEnableAutoCheckForUpdateByDefault; double m_ZoomThumbnailSizeInPercent; int m_ZoomThumbnailMaximumSize; bool m_OverallVisibility; // Interpolation used for rendering slices (for now, linear or n-nbr) UIGreyInterpolation m_GreyInterpolationMode; /** This is needed to read enum of interpolation modes from registry */ RegistryEnumMap m_EnumMapInterpolationMode; // Current 2D view layout UISliceLayout m_SliceLayout; // View layout additional flags bool m_FlagLayoutPatientAnteriorShownLeft; bool m_FlagLayoutPatientRightShownLeft; // This is needed to read 2D view layout enums RegistryEnumMap m_EnumMapSliceLayout; /** An array of user interface elements */ Element m_Elements[ELEMENT_COUNT]; /** A list of flags that indicate for each element, whether each feature is * applicable or not */ static const int m_Applicable[ELEMENT_COUNT][FEATURE_COUNT]; /** Names of the appearance elements */ static const char *m_ElementNames[]; /** The set of default values for each element */ static Element m_DefaultElementSettings[ELEMENT_COUNT]; /** Initialize the default settings */ static void InitializeDefaultSettings(); }; #endif // __SNAPAppearanceSettings_h_ itksnap/UserInterface/Common/SNAPCommonUI.h0000644000076500000240000000325710735614374020075 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPCommonUI.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:16 $ 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 __SNAPCommonIO_h_ #define __SNAPCommonIO_h_ /** * This is an include file that should be inserted into all SNAP UI * header files **/ // Include the common items from the logic part of SNAP #include "SNAPCommon.h" // Enable cygwin support #if defined(_WIN32) #include #endif #endif // __SNAPCommonIO_h_ itksnap/UserInterface/CVS/0000755000076500000240000000000011560342171014736 5ustar paulystaffitksnap/UserInterface/CVS/Entries0000644000076500000240000000006011560342171016266 0ustar paulystaff/SNAPMain.cxx/1.23/Wed May 4 15:25:42 2011// D itksnap/UserInterface/CVS/Entries.Log0000644000076500000240000000021611560342171017011 0ustar paulystaffA D/BasicComponents//// A D/Common//// A D/ImageIOWizard//// A D/MainComponents//// A D/MeshIOWizard//// A D/SliceWindow//// A D/Window3D//// itksnap/UserInterface/CVS/Repository0000644000076500000240000000002611560342171017036 0ustar paulystaffitksnap/UserInterface itksnap/UserInterface/CVS/Root0000644000076500000240000000010011560342171015573 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/ImageIOWizard/0000755000076500000240000000000011560342171016736 5ustar paulystaffitksnap/UserInterface/ImageIOWizard/Artwork/0000755000076500000240000000000011560342171020367 5ustar paulystaffitksnap/UserInterface/ImageIOWizard/Artwork/CVS/0000755000076500000240000000000011560342171021022 5ustar paulystaffitksnap/UserInterface/ImageIOWizard/Artwork/CVS/Entries0000644000076500000240000000112111560342171022351 0ustar paulystaff/dollAIR.gif/1.1/Sat Dec 2 04:22:21 2006/-kb/ /dollARI.gif/1.1/Sat Dec 2 04:22:21 2006/-kb/ /dollIAR.gif/1.1/Sat Dec 2 04:22:21 2006/-kb/ /dollIRA.gif/1.1/Sat Dec 2 04:22:21 2006/-kb/ /dollInvalid.gif/1.1/Sat Dec 2 04:22:21 2006/-kb/ /dollRAI.gif/1.1/Sat Dec 2 04:22:21 2006/-kb/ /dollRIA.gif/1.1/Sat Dec 2 04:22:21 2006/-kb/ /x01.png/1.1/Sat Nov 15 12:20:38 2008/-kb/ /x02.png/1.1/Sat Nov 15 12:20:38 2008/-kb/ /y01.png/1.1/Sat Nov 15 12:20:38 2008/-kb/ /y02.png/1.1/Sat Nov 15 12:20:38 2008/-kb/ /z01.png/1.1/Sat Nov 15 12:20:38 2008/-kb/ /z02.png/1.1/Sat Nov 15 12:20:38 2008/-kb/ D itksnap/UserInterface/ImageIOWizard/Artwork/CVS/Repository0000644000076500000240000000005411560342171023123 0ustar paulystaffitksnap/UserInterface/ImageIOWizard/Artwork itksnap/UserInterface/ImageIOWizard/Artwork/CVS/Root0000644000076500000240000000010011560342171021657 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/ImageIOWizard/Artwork/dollAIR.gif0000644000076500000240000001726010534177575022370 0ustar paulystaffGIF89a   %3(4 *#8###'((+++%*633315<;;;'J/X1\,H4W8h=t$.B*5H,c@zHm.AX7CX.Dg/Jw.Vz>Je4Js3Ly=Np>Rx>aCCCKKKEIQMR_SSSWXZ[[[CMdJSgIXxSZiT_tLg[am[cucccbdjlllejvmqysssqt{wx|{{{GMRY^\_bdp/N/P/Q,U2T?V=X>X>]0^?`"f/a/a c$f%i/d)g*k>e1g2mpjn p pjws$k#n.m+q&u3p5sx5zE[A^LaGdXhVl_x\qGiGnNqJrSmXtTuamepdpltjtixrxr~eza|HyD}Y}&?[~vkhups}~BJDT^\U^^KX^centwxhuwރ܏ђÚßʄ犻搻࠮Ƨ˭άضŽȹ!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8r[5I%NΨ/gz0Iä)$IH=e=3JõRQ)) AfhV֨I3+Sa "9vmE]D\ĸq5k2i)ԩ{dK3Z a"FϢ^ 2<4rL|_K٬y&Λ6С=V4_Q\ fKײfε7xq|ؑ 3dHA.6FFI^|a3,Ӌ0C d$Aq' 3 2d@B 2Ф=tv1Gp4K|`h dP3y5Kw>`=8 <AČӎpOl9;@L)jȣxVR &y$E"D^,cOis2A4K/c4@>2暍'}gd 8.`B )J )M4ah@ y,s;B%m s 4@0Cpػʽ y!?j 4,`ϻ4Ww0KͰGm;(;2Ӌ'P`"B u|.YPD 1X"b F4ѧA@(< j 2̀@HĎ+-tRKx!]$^b0<0L>`ۑe&S@nc4Ҵkϙs93`)$rHTo?|$0Eئ44cb1 M&F-[ &M >?ϙ`@ x@ } ߕ! }S߆g 8 y82ԤEE\ԀА@;Qi#.>JC>ECE Vh5၉fDC F,l"%p.08hZt(:=zHLNqA`@6E3agy(p8 8h-f Iw(*/ X6yn'83P`0cyy@ iTl!.sHSՎiBe2Ee؇:P[x@N*W-l X'$@(w.v b1h`]tA`F0VTj QJDX80ssQB Ɂ `V P B\Qֵ0BΜ\;$P2Q >d.-Ae8 G9 2k\] Kb X1D td4H_@}k(WcUfC Jg 8O3|ЂJ=G3v 5Rj8 (O5)xK  ~'{.x,.LOQP b.-qv 0=cX"\լ,t`{"&Z"( 1,8|Bs @g[!ET`<ɑ{>a*wt@9Ջ.D8 d"||0E,\us+<]SJte.YGdSqA)>嵀@#.ҫ:,}DH> 1m hP=ǖ΃ w "@=ю@YP'ig- .Zg)8"xЃ"N!LB0xv20JG},UB_.qEa!h,o p`4( > F!F7ɑp;&u>E#K]XOC)#jz 17͠3}+ 1p`#X g4iS[֖n5^stڝ a4}t@S %,{O{l3'keDl!- > 56Bd;q7.z( t1vЇDb!gNtS[Ҕ6 ǙGD#ob ^ T@ۇpoZ3kA{ f}qL0X36|4|J{Xr Ӄ?.ujru Z^ o( v~`dDG~V a+wkDXmk CJp&> 9 oQ x~0`~W v~V !'$Bf~A[(fbTh)<8f5 ozX8mTЈ';?@@C pX>JT1tdHҐBWJ]D: g PB 8~|B ¸:p7Pei4~ Z]6] yP^| 1[15g59l'H` 1}`ƁTv H*gtCp{~ (c|QQk/ 3.>Q0x|K}XTp}{h~f4Pe ogX!q39Tّ"*&(q:T Ʉpo{`9i6Pٓ4A@W'VD($ȗ]&Z79K1@)Cp{44B: Tpy95К؏@?)57 $:pҐOpyMuc(q- F>3(׀0Zz؏C@=p895@5`HXC:v'(&P )(s& U y)  }`"ZVz7 @>8f|wX9Ymт ҃ @ 4~`Tzj7 rJ10 &U &i1@ADP=pG s`jXpYp)CY&1A1 "'GGQk .LGJ pzȪ֊Zph4t (cg`q3T gtBᅿ ifB^j l [J fwLְL| $Y'yr=&*  -(,uP EC I {ں{˵`HVx'^U/( r" 0xGIg [K peƴH)!a}C@:k&|%J0ZʗGQ^X4[p1 ɴ3I9r#W(|0&00>.q- J|˸l7݌{8@A:mBu)  &> T|& Gh:> \;/͍;5<;<+=\|`Lu!& Γ2]$('/''jsx-ͺ6ȏƀ2fuq])U9@:b^DlF\|Ȝ@8 g͌(j&M|4 NPKMJ kHwkմӥ *85GTf49B͘&|)0 ,x}u`T1Y )- l(>Ϸ0}ߎZX3"ßn8 : #Ь];ԉ׋hpn '8ifJgf!*D49 31V4q1?.5ǨF]l39E35mgbEpO6n}Ð\; ( b!9Zn~p>pr" ' x3P軝cÌ u!x2!!LPrl1Gy:b`V7{,9U"A>Ą A ]>Ү35pe1ھia!^3 ,nEuGyГm2' U f h Ȕ\ ێ;шJfe'@Lk8y{{:LXo|O߆;;0)5 ƞ@A>mAp&Kn#G<gz=0:飬<V) iQ? A:nKj89MҠPzo1CF% ].g˅ FϨC`79JKZ!1tBsصk B0I%JdHF4c|RH%?"cDAٳw&|5kaoNT}={ιhwyxbn8IeI*KAy4ȱ'OCNM۵) vX!c +F:5,2iFW Mm⋱'`BI < 1!n'.\Dxx49$I"IBEIƌY]r=d7tԪ1h'2ε# DzhF6Eĩ"Ba/WG idyGq}5|p|iR.Ff (梯?`FT>= "hdΞspy0Bؑ9ى}iǁ *v FZ,@Gflأc7z i"Ov!iJIrj$ vFEX?$sEn#4 R]1:\J4v&`eApAqL4]$Rfyj̙!tif'!Df( P`'ĸHQe5+v)-{fvzNe- eN68_͊G}\ >M%Y9G~Кde:i@FҌ(y$!=y]!zi4 »e9<\&kl$٣SaxHfA>]n {eCH4a{[;]z٩i|?2D,ZIhtӵ aC UnjgQuwmOw_@;f%Gѝ|`f٨{7OSyQdjIG? 2s--J4%΀JFč@6X&.s \hAza}3<0I w(P":ħ$!.0hNoAC[ƃ1*r& O^H^ 1E3A4J\ ,b [BEejIP(Nqn ty004 CNM!08衚:-.pZ_ɨq,CUejo0Ā@]5;k`d&(1xUe CPt#[݋S,Ҹ#fUqjNثX{ЋFֶE9ڂ8 qm[!u ׹B "#}nv_ox;^׼Eozջ^׽o|{ـ;itksnap/UserInterface/ImageIOWizard/Artwork/dollARI.gif0000644000076500000240000002036710534177575022372 0ustar paulystaffGIF89a    &!-$6####%,+++")6,08333;;;/Y0[ 1Y3Y7W5Y8\5c7h9k ^4:H*?aA{BsEz~ AJ"DJPT-aYf.=}`ۯ_>zYc L!>FgCm[Lܹys}͙>:XkTȖм3GDp̗sެY{ZSdljޗ>zm&!̗pan,ش!O#g-\f|=_qgq#4JmSTVv^lRa}BE\lS]?7.[lQ"[8@{>dEmvidF_M7(EB<?BgN}|aSH֭בF&dc7@ԃD9``UӔ>مC?&%ĕ:gTo<)'FsO`0 UrsNWJi6ۥ^DPRCdG[nN?8Ϝ~gO֪#lcޅոz`~RJ!?kg<> A U'Wyv\XRB p% &AVo?H`A~`hY)] >tAh0uP3`3T g6Og`E3WaX֟UahjrMܤ3PMhTV(Yqg|Yᘶ DDOG>9?F[r7Ϗϩ L6Vf'35>ސCY>\i;23 ?0S_~R OEAKAr gA1a\ 4] Isx"?"/K?~ n@p5)S1 VЂ >ĻcxPV0@` e{c "C}H"0?ȱ~̫H } p.lX L3L=iPW2&5e!S9F 38qF80= sB xGNLil8ڣ׬mu=|L#,}Ԏ;"==\@f11.=m&{ҟ(h `+DqSר-JPUX"=2#ʇЎ-jXR*>dz0.tfZB>]1~8ͩNwZl  茢J*  pHRB4Ї~r9 Il$g (E*^AWWuhFrJ?Ψء`=ڸf8D -A8zG"0o\j֎"$NZ;᭻|3AYD}ɂ9phb1 Uh]_qT⦥(mzk7b`r@o|w@"3\z@c_wp@ cdlfxCVP*bQ #(uG< TVH(Q>L⵩l%1 I\`$!UL~f A[ra_ e]q;%)` B3__P& c 0vj  p{w*p%ȅ)wxbH?Qm IpfܱB BB@ΰ@W  ڷ} _~z ( {  Y\E@RX7_DP?Yp,V$e`  a&z}¨}' t eyuw~aL.|Hp҂xt[t' &`@` D e׈"x'{h{X P ~{uPqpwp-YL!&8'`܁tF8^{B pc 0 v7@ op$ʡ[ +7ccܑ,) $Cְc`p0 `WP (*P0LiOɐiu@TPK'`RFY @UPY@\"/2JMWy"hȒФ .7E` ~0˴ֽ4Mq}|׳- 2QF2/B%I1, ,`t`(Dm P06=wm ' JLm8 a$<) 6F]5 `0

P,L^ !N2g("i['j@ga@K %4N* *䋬λ=]e$HPP"$ Ba5i2<`h` >Ddn~H ؊ 8Gn$@g + u`qB0ЙY` > Ȝ&; ɔ3j i7]e0`wmJPԆ%(yʳY @ RD ar p ДM٦:qݝߍ䩾kF⑞{;q*1 ` "~J Ϙ4 E4Ik`N$"KސLDp~EI nk s ޱ]й@",}1HހQ}FZT!$ >sX/Q]^* LK jOL稞@@*_ YQ;ђXa>D̰o _ {8C ϐhĬ_>PT}(Jײ E$_/'j,zb#2~"0xB 6*/f.=|H?Qcp<~23ޜQdq~lggЦ}Μ9{mdI!ѣ35g[uT5f)S 1b>xؠX -CÈHѡF[̉ċw5pDN0Yݣ i<=ShC m`DS=ﱳj֭bT|8  ݧ/L& ݌Yta(F8km>~Ӷm`qiݦEU 7|,WM"5J=(Gt3aU *Lo̹-0" .p'0(rp`n ђn1k{̛i% VHyWfiqfB):v3ȐOܜ{<&{HПk|ࢇA÷ ݤU7:k_zi x Á'gҧF'x{&͇/ 2"tp7 01WH:S-I)c&h X>'kû!ȭJM?>4` >0 halDA $5 "Cױp-0nXP$a2GAЌ`Jp=2Ǎؙ`8qAsF+`Ɠ,aiK:}130cGJ "#(p~g\h s{(DTJA0D[>WJCd|(r" 99*D`@`h4oэXLR'>;͚nt7,e89]BlK0#paf[BɏQ"3ol bF(JI D1 l,A;GD`&)⃇c*ڒ}$..BѲ 9Nw+ZF) = rlA Ҧ"T Us͖ _ª.bP24"5NUjD! 3oe!7"Ol]X]7UxM'j8 !Bf$_sT$1 ển@ M#RS:T8nv2ln+b !ᢏ!l`DNնE`#0d!h&nu]܌-H|PAt A[H"eaY7 5%A`Hx=$kvOEPS~=a0-Q]"/5L%@Y@op$K)\E`<2[8 ׸"Lcp|lu0LC"+'<-Tx37,[` L|Y!f Nl_ }H&: sYwH!##LaNf53Nx^wR ϏNP"@Cox%i&OcH7S٤?D#dY6u,<6&-o nY׺F_װ-93G9{٦| ۠G%ssvmAQ"0ǹԟ_K@ǼQ  Iϙ[g0lJVp5G|x7|%?yW|5;itksnap/UserInterface/ImageIOWizard/Artwork/dollIAR.gif0000644000076500000240000001655410534177575022375 0ustar paulystaffGIF89a   &)3"-$8####&,+++,.133315=;;;&F)D/Q0O5W 9i=s9g!-D*5K*;X29D$Je:Ns?Qo>RxCCC@DMFINKKKFLXMPUSSSTW_Z[[CMdJSgIXvSYiR\r[`nSa{\cq\f{_h{ccccfkgilkkkafqditdl{imuinymqysssqszvx}{{{HN QRMX^]_bd-N.Q-W4Q3T>V?X>X<]1^>a&g2`5c>c9f:k?pjo r qkopr&l(p&u6o9s6zDZAYJ]K_@_LaGdOsSdReYf\i[jUl\qHhCmNqIrSmYtTuamanfpdplsjtnxcscuexivhwlxm|rxq|g{GyD~Q?bt||gkfo{wzHTQXZX_JVgdgksbfuhh{샃ۗŝӂ턧ࠫǭӷDzК˺գ!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8s30$AfĐhO– +$HaĒA]<|º8JR3\ iA j x=vذM;FLL  ANh]0n ABfW|{4hƈږ He=|!bz /kВ>Q-A"twi];tQKP .i)Att֦A[ƭ6֠fTI@[ϭP 6YC _R{񀃾i6M:]`w|grK,Q_[< V]2u(_y ( N2H$w038 zI;' ;LLjA  }0lF"^>fB7XRA 8QE!1 1O^aI6~ zV\Ĥ9 6 #oxe:3B=Ym;(#>qͅnq@0hfVA0e J1XU`Cq;ڦ D381% k6Yʒ HtCr5HFlt_ (x /k[:"iLH\xv m%0 LQ\I0EPbp }(X #o C $|K@ WF h3BU!3jH6`.Y"5↣oXTmc,Y5 | DTGhW!a` h#Ƈ0 A2`A9 *u,;DF+qc`[@›!>8CtPH֨MpA+#tv&W D<b1 L|(d" et4h'$`tr! HPr=q )H%IjY)w,MLQ>x`D(`< IUH:l 8;1Et,ec:*KK(Ph @$)~h4V`DbX2as dhUvkUtRՐF4FQ cZa@KHq!c 0vf` 9`P_ϒ!bcx*V*שSLF4b&2q JT!\E0D'1 ͑ V 16&H @UU`1| I@n!ύntaa M ';Imր^"c }(o%0Ia0&nRp>`\h cxv 4~v l4p1 N%VSb0$\BnRP2 Bi@$a0>lfEL)73 sJe8Et,n171f2yiδf3ͬhk@1&xpR q9n_BN܈`%Zpm/3ƩghH~;"  0"4rRitB愰njK+^7b`Oғ~4 Cه^|$c4dFe;dXDD)miB/C° _,}{)\D@CZ<ƾ.cc@T8k$5y 8 p,_y< x^028zAC+pz$g@t|E "6 %@6Cc/{"q\KD0sxe^#Gq,}8b"q@ +@yȼ ICЄ-~KXb6g}}^u Cp;x `tX {SCk! [DPI  G@J@ (r&ze{W{.{P'|ww( |@nٰOPj6 @p7P}Zh6`cw(*~73g炷e*8{Q!iln0a Pu5P6}y`5_eG@h J6lh{qXw*Fg# jpoX`MQPpg7 (~X`  G=xڸrZ`t$$.`P$lo0#%P3c@P mIpE`Breȍ ِCht_$38 h ` P0_0(x*wۈ)8 ;Pls @ PpVpYh\؈3i7d' HaM`I P 0 @ pPW}P^z ee٘wxaa 8H vP @ Ue]Z29阴 Fwmp fp pH 0@h`mstwx^)Xzw%pFO' ki О _ gIwy]iɝ:95PF{A ~ ɞ  pgp[w*𚇹 )ؒ_ jp @ PhzC)FpIE r',ڢG&`t}€  FɑI? `eZf>]'5 eIw(I{ ࠙ 0Pbh C*)p'@9h58m:)FW6"Plz  p WPxsɧa}70(:pH{)(q< jpfZʪC kXo.,F {1 PnБ0 %{y&pf& yDI`C BVW*3:{]pL9 ɉ %r7o 8`4`yc meF IWqp s 0 4P%}[4ઍhbH:vwdEgtimr`u`0 pt`d0 Pg{^ٹ \JXzX {a|nrPQYKPd@`P Z4 CpY.}XZ HRr[tb ` wOE9 ٿHڰ3L@mq-Lq |`P pJ F;`55`?+ 0qUQ+,-,V@TP Ixj;Įj F1UtV`VY\p i}d\ۈ}pjl80ǒ\V@yS\L0kf쪇ƉyXiFq,^W 1WӠ *=L}W`& 0 ETh`z{{<=@E]ATv OٙpP@tm5B`I0 p JL %@K"'Ј Tf5G =Υ}{ P ʹ YA.EVo9x- : //ѰJ4Gy4Q'q^jB{8 1-Gc2}EKӀP Sotɀ`ȳMZ4"9'UgrP Kx< E y,>`r<&uC\C ,> а81 3`ВQɱ) yԞAya$oTA-" GPɰʑ$@X)&OWBD ?Q$e,C!$֫&H@c|a*H@+@3u/%b_CbJAGzERDBJpDG#c;r9+Ňg5bkLdpJC2*9'r%sp"Ct&'9ym"N ؐxMm G0 -rE]C~M> + `-ׂ@Ϛw!ʞ"C*\Pi:'i X‡ t@hk֋x!5 Vcqƾp'A HYa BH!kxGl<$$\i=4LRaa$޴ 9G{1 )ψ0WϤ G(I 5iՇFsMyFO a 8x-^so+oQ`[Mg7]vA hvF a.0W]hDɵ$#">ׅرtyx\S߉!c_ FΞ'h\Ay1b!f7/X\B$en5$gb ꙡI%əiVz=.ʁ1J* b2iOnv %0%dmahQQ톜9}!t]}|HoKa[b# \[pԼ:AJa (ӑa0c=ygpc\ħvy\!ܩa}۫f .̇uׯ}/+ k"?rd`d`f_ ϼ2VбD0| 4/T4\y[ |00/D 4 4 $Ӏg9U#z0|a20 Kt3k )."a0< wS6v$C"p5#~ V0bXzh5$>wA1JHJ:yrc #h p向VҔ 489bl(4@wJZV 4F1}c-& 0`O~ӟh@:PԠL@;itksnap/UserInterface/ImageIOWizard/Artwork/dollInvalid.gif0000644000076500000240000002025110534177575023335 0ustar paulystaffGIF89a     "' $++2!. 2$;#3%:(<###+++$,>)-6333;;;&@)D.J0N3U"-D$2M(2D+5K"4S&:\.>\2;K1=U#=f(=b6AW5B\?IZ+@d,Ah/Gq3Df4Fh7Jn8Gc?Mg>Mm0Gp2It6O|?RwCCCADKKKKAFPELZJMUKQ[RRR[[[KTfFUrFWyJWrLXpJZzT[lX^mR\rQ_}[anXbxcccbdkghjkkkaereiscjzimvjnxmpvmqzsssrtzux}{{{5P:T?X>YF]J]J_@]NbD`DcKcIfScQcUhYf\iZjPfTjQj\mZoZpAe@gEiAiJjLnAl@oIoJqJsRmQoSpRq\tZu^ySuRwSyYv[yZ{cmbnfpeplsktoxcrgxiumyl|qvpwtys{z|y~s|q~dzd{j}h~c~`KvTz}vtz|kbhlnutpu~~ǃŽʚ¢ȡ̬îȫϴƽƽ!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8s3.UуC( JHQFH1kɫ?R "?AH0%7^}^ڏ'N2Tt7eo҃p^TCdf[#1?15p<>B 9d?]s]t*YfFUqgWz?]C >uSX<-{f=mtAZf&lpRH: n“Tk[g )p0;, ,p@1ElWe#N< žeͥʁ! HAր>lR'Cb{kG5CWsUO&cK'x8=tl7p->@];2}w \aXZ" d:fWe*DN##@$E=굛pdehO>Ê H +(PX .Ȃf6_6VE*'0=]G;Hkb{`>|]xLd6L6m m[vCށuG< xI9L.]8_Hl+R=DIC&0шFwl3@A t& 9:Lf*`H(||!`GE"#!~.␑ҰnDpD+XA<dA G  P P #(k# >1mU!SXWÝ(̆?HKG8VvtG]Xՠzhbpx @<%P l$! oHCQAGFQMQs' '|{)<?4jF] `89Y @ ja(QJTMjѧaGy_T`p^8JC1u(?9`@"c0cl+V4*D!7TԱuTgl+}<\U~ƤEZA<z $@@k{p xh蜸=-t@d':J PU;F>'_<?WRX7ƇV 8M|$ Fr+ypo`.VOrXM<4)ǪE8">Ip)Xa9_`@LE0`1 TB=D;c&D%*! JX~kM6ófGP!缀вg#88Pyw~_8 9D.p! XC>+6 w<8J }א<*`4q"r /( ~4 ^~q!v ԫ%[Q XǮYT|c PBpr&]^&O =\ 8~% "AC3< p\ ,Y'>jɖg*i#&QZL0!ڌoq`k@2<͖"ҫ*9C*B2LH X8"q=\rHqNlzL&_pEAsr4G"\@ۏxYqu1 %v_4Q@_JJs Z"( s >WVhVGw hbcR|Bf)qq  hT Vxr~of*ghsH9Z UE ɐ(PN&!ϑ*vz{`X46)r 8xqdFZa ?s:2`0(& }xoT` @puootQ;f !`P[ hXZdA.k%$peA> q0*À_Hu7`pNfoVpY4 sXsqo48 CM00!T(D6ExoPGtd\Ru(1+YUppkXV -X7,d5eZ/ְu; 3Sr q0z ϡq#rztԓ` } bo0 R]z$DuȎv `G zo0xN{f_c`^4ODdDhaNV 5;=ZBp͐ 6Tp^ ˨kW s|W4@w-ٗ4!ԛuE2!Y0eff[0 pu !&_"WpVpXV yw4e(MxlD b5t E80T՟, "rA)ـ Sk0XSatuJ7$mzQZ1= E-=\a+1)vm#(9VࣦiERE !#$f3$I!S9IA@/Бi\!'f{^rX0w$W P 8' Q`- ʣ ݕ II&t  x@MZԟ5#q!0V x }fvCz-ԗM 򠍡!1 I2B"= OSOꕴvr)6sYokKprTyh50{9Z+AI@^2F ZVe(WQ\ibwV :bJd4a_dF'4=Jz=RkI~a xWbggp p `UT-Cǂ'h!m19 py[eL[hUrcvXJ{ w: pA  1)?P?  E dKʖ vN@y : omF tA)bq]e%`[KR0SH& $ ʖɋbP}}{5XP`@)AWVglPZ `|D](O DІuad1[jy+o0Y%pJ:{o@]I+8Q8R{VV:h nj&dk D~R6VBdtH  W3e/q-gJ!ʖ`j%coak!!!#bBprMhHf]!_m~;$-55C]`'ȩ4;r@¾U n r s0p0{/V(qh|\#$[Gř 4( Q  }W9V @ P6/4A78R @bi#eu![;D敂)!ʐ}P pTP qv/:L0w!lƬZ!fB2j!\!AttJu nЄ`_Xo voF)يq1PM ud60 ~{J!f tc/F_6g[}pWE's|`&-/Mtm!f@GF`۠Ncjg6MP/)CMN6!e"e-7 =,ْ5B)#Bp QAw 1iV%EDBU誸*O([)K_ y`}lzh BU_rmUsڞS⪕'DJ *mD az ` CjvhYH- 0W " x&R7_xY4bMbe[#\1 UU'P n sgUU VԸz҈=l &ynM=@#9&VGS\%rTb _XUi uNAX1~d  AJ-+5}ej1)79oU*vzѴ!Z0,M%hd 7WA/e!O[p-'@ ~5 `᎓4 Ƴ2&JP@TE{EU$le^ӿ P P6 ðJS[:tE?qK1NSESbŀ P  0  __Z_^-*x'I6q4 L?xUo_6)_,Z Oc|+رdLj(L6eʄgټyhެ'fc@f[^D',X Ut02[0 {M O^N=ztuDEN>.l.7qb=0;TaC?֫"/ن_]T`qƑ 9xO(,jGðM:ŚN,o<p(EΜ)LԲ>`zYq, '~9F9RB!vz,lz*}`,删ʟh18{!|.  ౬HrpqAXP1E<2ܔ:s  l n O#S& E*cB?p ,Uk:"r@0[28G>~V@R q̣NW<>V$S8R嵶BA ePhPc8] Bfw]Er£shq^CQ]+܏dD ^z\׽wA>+"̣Q̜:6]kyBTC[jai-0@ihy/:uE+8^Y)2A.`ְegx<5~ܐ*Gn0ˊJadXC8 `:7mj1dNM6 MaQmJyNd6r<#@fqQWm*}A\D5; C Z)Bs +Ҹ4ۻ X kxTqE@-M9EUvVj) Uk#~u * Y2Dn~ @v7`Hpm6,o vgt o1ı&7Ș Up"=(pqF!׃>6zqfouЕ' zOzԥ>uWWzֵuw_;;itksnap/UserInterface/ImageIOWizard/Artwork/dollIRA.gif0000644000076500000240000001654310534177575022373 0ustar paulystaffGIF89a      $(2$=$9###++++-033315<;;;&H)D0O3W 9j>t:g*5I,cHx/@^7BU.Eg/It>JdRxCCC@EOGINKKKDL[KNTOPQSSSTW_[[[CMdJRgHWwTZiQ\s[`jRd}]drZdx^i~ccccflkkk`frdiuekyjmujnymqysssqsz{{{HNRM\X^]_bgc/N/P+X0R>V?X>X;\1]>a'g2`5c>d?g>h3k?pjos qkopr&l(p&u2n8s6zDZAYJ]K_@_L`IeLqSdReVhXf\i[jSkSlZnYn\pHhAlHuJrSmXtSuVzS{Zw[xbmanjscscuexivlxm|rxr|g{FxD~Qkt|z{djimfetxyHWWJVgdcjtbfub{샃͏ڎۚƘс섧ᠫʦҶDzЗü˻ը!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8s3/"=v81a‰(!c D[adѮ'/GNf$E#uѣϤs'׻xsZc|qրQ| E!LȯϬmg~⭫&-Y#0`Al^v]{=#2=.w%mPǬ[ |k߮/<՞%P6u' D87xf`Ghwdqmuw%O_y Y#t5ޖ7)lT\m\C vL ЌbԂ]ՂI,-aeF5siwfk 2;{: 5pv+ NU90d"ҦcbNjm9Wcev=tMpդHs@ -x D0` N-2˜S.|g%jt'Ϊmcy2x+p %sQ:0*0@ԃ;$Kv )Ou <ە ;NpiՋH#ָ5̱v m"Mhf{/X'}h I eUqzFTUz4`hu 8:% G0"4@RG=qG< bj˞ebġ xC@ "x 41v# 38F XnM#BC qNC"LATd)iH gH0"ڡV_װ.*DʣgV*``Q2j1ZJ"ȍfx*啱Vਭr^qցnP0rt ~Cu8o=`+ b "gBydsZ猆xG, 0,#dOHR$UĘ5f؄&01sCv|&F5ĪCrm8gLL NF3abb%vKBPp=J3 HVA1}\x!<F۹N@1-!1׾|q !\R@;x.^"wDQJ5W`b۫ b}GLVD0`G>x(0XHmQ )i<@x b%*!_$>1&X×֡`"A}3=D[kH*YAZ d `pDi@mXe.(QJ[zu0°i0"{+=և/|1 C0N&G#u`eLёsp/Mb[::%X  ~YА\PHi 1 Sry fB66%>bMhD Ȝ~w` U6"Ԉ`Y|xd >1` K,bfD"MxZ!o2Ӝf~XB$@/P0W Xz7A*38.!u"(w;| 1̹p-ApCw =Wv͡{WԃcC1! a;AZ5?ZV/Q@NݮsILED(+lD>P$} : h`] ! .qh"&2a KpbyN>^>P9ЁW V@V0p_q d4 .0ep0Ge 0@6qu[G`vYvs'w7ppW7@sW=Gp XpIfPGp@eFo}u B *Ȃ38:W{7 "8 ~@Ȇ4wCXpw0`Pf`j(bEyaX30uh(|P {`?{h0VBW hlkHb p\}75@|}e' xЃ2~؍w&E_.`DP `hXq }x \l`oEPC@80,ȍؐs% =_AV (rU` qP_0}|(2)4`h,s E_o  #`eb2I2P` ) 0 p  `(d}503bIf٘G P$Fp 0K)w P \u \Pl3^)`Pw!Iy y0 p i@ZiG03ɘ٘$ $ I p@ "`xzl7wW9י4x#ɠə` p FD`YvZ wW`I2` }P7 :p+wp&BG) ސ /B<%~Y%PA>%Joh#syp"ҀJ¹ 8*P " 6@$pYJ*ui(xAp`zF 1eБr 69 ݠSxJ%&`%89膷'W:T@C(p0 yݰ .O B%Ъ$9d`u6x zAȟbC 鐈g 𙻠ذ-t:b׊֊~?B@ىqh؈%0p'合ju ڣZ @ l!#4$B )?8`^d@uǨ9*0 "%M9 p 0 -.`E>2I12$Z[<9'DMs(~@jn   d*P;'p{m;8r胰gDspZkr и# cp%{m붱JwjoZB u{tUk?wmn`c`_[*5?09;5k[wqPiPmpnmP]Op[ p *v@4m맆XFqe0K\i]-,.UP!`mB<߈KH21#ii r~s| ;@. 8 `M¬2yܐssˊM_%DIҸIK}A! -~*b,4h=eT4\HLKV R۶Y-[~y'A]QYk ` HH@ JV0 ʚ02Maˋݘ9}xj0:;q RHp}`S AKE# Aг^ăz<ӑP{ y_ARs GP@0# G*D0<pT{̀mц D 0I7FjO}?C" #  M*;L4 ڪ0  yL{u0坮 6@ g@ Bj-P u TNû৓1|> 2{; :x+{ P=bE[e1EpBG&ɰ-r ,0 PP5/`2=4 jpw@ D=`6`U> P[%! LBuq eTpZmn#wT0 <Ϋp<z]r1tuQEBv-t=U2R- ).C SD6 ے=ۡ֞q+C '$;q FGd?tQ `9@ @ gd/FP)Be2F`p>LP<GW2##/OhqTn'HoKnł`w]Q 3"^ G64>@M2))u#sJ.G/ٓU=By:0Sut;#F7I.>$G"Eh?c:qA(*xP4uq20:4n$7Ұ^" tHrnOqA%.@Up~ 4դEVںusQ"Ĉ#҃`ϪvLx͜!qOf{Ù?==TPEB€ "\sd5]CbȖN|HF"Ik(rTi VːvДy͜8wQ…so:pދ:y5pٲE"}{vKLZkֹ"_92 UpDt=hCDY1_9&FY;wN^Țk{.:/4jDOqBXC.':ᬋ3iVϡu|kKkJ.B#3=LjcIiigh pzn 1RI+ I{23B4@+SE~FQA dLy)-#K2y8ac&;*ûRļ|I&x>1ǝ|'t"&SOQ_<_AA͋&U"b8܁hL gIҒn.AebC*pPH}<ғ3*%b` FOF<:3|q<\ImD0fx ҘJ󎱃dRDXb'9Mq@D@*e9jvopc-OpKڌћ=a{ӝg<9OzӞg>O~ӟh@;itksnap/UserInterface/ImageIOWizard/Artwork/dollRAI.gif0000644000076500000240000002035310534177575022365 0ustar paulystaffGIF89a  &!-#4###"%,+++#)5333;;;/Y0[ 1Y3Y7W5Y8\5c7h9k ^6;E0?]A{BsEy>CN;BQ>I_.@c/Dl.Fp/Ht.Jz2Ba5Ha6Hk~`/wmב|3wsMXsZdcRa8iK]_=Q9ph!)<=SGd<TP`_e?WÝS T:(QU|3,u"I炀DMVd?EW]$+ZmxǕQ_ w 5O 9uQlsE:6kU,C>]1.HswH@j^ FE?2w37Lf^FQY: tW}wv3]ᛵoܶ]׵q`ͳED,͠*AlS(EhZ5ȷywQW\hVrzDgC5d\C3`eVuꪷޕhRRcM͢0d^sj0=L]Q?j^_" zCV󀟱?D[䐖7ctl.X\cgZhJ hԾ],uIORd8jDe2| G gcG! 6U *Q׽u+;X<ӊ z=@G3DUaaD"VxtCX&VBl,bAXb?**nmhx><1PÇfB< ssT;>B`jKmoK*gph+hF=5g ^n>s vqkأ idaHE*׾(FmkՐxH|DTo( QM" 8U aD;_F"K'cP(> >6l{Pj",&RWc3Fe 9.hu/)D#s "|ɒ8(`ST9dδ7lc9rIh&:'g!r~\e>??CDLoE{ηk ;&g.N՛emjLrӎw}⏚5p( ؇;T/Qd%ޑ} \`@ d4B@0(y ||H~o, %C ~ D 4',dapC_dN#W G=||L8~oy y\Pz!ʱtUPx@0Pم'ePvY2q`(0 |q G  ?8 uypz؇w vA dp/0Q#@G_\mx ] _֐Fp@ @  b M WW0x`?9PE'qc/GͰ2ոT@^ P w  脛pP'6BU|i@ `@MdДG2Bf$296uNqb? '8I~pp pW`yy{M`Z`zKUAsSJޱ"ER!]<2;a0o@󦇦  I\ ОI?w@400Ds; ;CaD P`hY \bwI~  P fi? s@1pJrr$lY_`atN֙ w  Y:~pХ^pP Z @iJyw 3 ) dP|F6 bGxQL V`:~Z~@j @ 0 p )m:r0q] rA耣B`z"Q< 7ĺhI ؀( zJ ` K Z P٭.)pX qo2s %d R'/b nOp ))l p^2Y~ j ʠ p K à`X[1j2p^ v'wf'yc?y q]2B M5 b T杦PҪK ˠ Nk Z ,pmih\ '[']1y7S4n`t "D v "$ g^p ˰ P{ Jqn@fP,  5jHq6EÁ+Q]@pU Gq "V'kz + Lɰŀ[{gP'pKQAGPa6ai'K# AHЎ+ 0ӺDQ[ ph0 V_Pjb`@ҴAPA z }= ۸cL֠0G"$U9G60xD %P睾 ܇fp`ߚh`|i IIEG|Q&tG@$󍁜-^ تg >@Ս``Gr d#c Ny {V@OMWK `\f*J \l9;ڀ`R2FD0նc{F߀44| @ })*Y*΢fMjn+p~$5 pQx8|.kpwqp ǠnS̛^ѭ;> y. iza]@W 9cgc@ds #[@+ipĕ;^<$s ȩ`Ba=pD0 |Edp'0,/ϗ;5_g. |cRd/ra1+ 6z!2P7 @Tpo ]߿0d_2?ZKAzWdRQBA09OG BƋ Ń Sã p ?g>izr"vqgzQq0[HH<Ͱ }\~~p/ُf~s#Dǀn"<1hfoȼ&I'q#N99ְ 3 ʓ F0ťt'}ư+~H/O~Lk.aIgx/SA48|^WA9s9䐃V`O>iȑDp |.Jњ |/ 1$P`g|DK'ZXO jiWiab 2 F\IVY&~Tѯ@ڊbgzاI燐"}͜X0\j,it hAa@:Na饕y¹F+x}"p`Bmcc 2Ge`ft́P 96]{ҧPtxf) 1Ǟd7.&͗zK y$X}g Nǀg$"gysEcvB gk`!jIG2'~JhK7-!B7sn`l{IoX8'-</”嶶|neFm kZ%a  ̣/(w7 xZ3TAq}i|ZO:9^nkR=p&=`E, = Pa&|h|C@-0&Q ]Q-`هz%1-$aԀT1A Gk#.GĄ.`&1jdF8$E G=! Ѳ@"|42Es WBј I8fQ5*e)Rp~!>z\Ai[}ɷ6z !JCH7cTf: xx- ~T35~хoZ?@ \tGM51.K@jBϒT(S&ch(E14!B3s$?!.ˑ0Mi;vHo cd7S@! @13lf(cҁC~DѨY+2> DX@:hOd@4*2V]WI)xcEAN*"occa52xa_F7ڴ(񣳬?ymKmrScgpD@o IUַ<݁|į>Ԗ ?dGt%/\#?cg*NJQ54{(j@B=cV)G7Tߑw JD-@ڣck*ZHa]񌬆HWO`d]x c!3Hfܱ 0=)op'bgnHˠ,#؃!4I3~1 2{s/Fs[5GE(yV55rҁBc%HCz`Hv]X!J@g y" loC~@<Z%}kWCh@p[%k5{vlpg&61 ,qq)І\6 gB9kVȟVӱ@$p `D`]^=q"4 )ãpCt{y N] $cʍMEKϕN1Ǖ f/yӃ4ר>dupo3$E[\!Ԃ{)NJ|?xG|x7<;itksnap/UserInterface/ImageIOWizard/Artwork/dollRIA.gif0000644000076500000240000001705110534177575022366 0ustar paulystaffGIF89a    $4'0$:###$%(+++ (:33315<;;;#B.W2\)G/T8i=t6`*5H.=Y29E1=U"=gAzHm/A]7CX/Df-Iu>Je?Op>RwCCCKKKEKVMU\SSSRV\[[[CLdJSgHXyTZgR\pLa|\`i[ctccc`ckkkkejunqysssrtzwy{{{GMRY^\_bcdr-M/P/Q,V>X=])]0^?h?`/` c$f%i.e)g*k>e1h2m?k?o>pjn q pjs$k#n.m*r&u4o9s5yE[@_LaHeXgUk_w[qHiGmNqJrSmXtTuamisiyrxr~e{b}pBtE|EyNyJzC|X}#5?[~vgtpssy}xJGTV^ZW^MX^ccgxwhtz~惃ǁ܅ڍєțÚʙҖք然␻࠯ǧ˪̪ѵȯڽɶӺ!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8bKHIF={3'=W"CDU3ÕҦNJ $0X?rHE XM^#3FLݮ^>:5 4ĥcwoq-ʂ)"H~"Șch6Ө}NVmJU.T?p ?d2L\z G2dΘQ܌󦟥 w¬^} n1>tõ)s 6iFΙ^bM1E ,DP@`w,=0;唓 d8mK1,S;8_och$aFDP A cz289Ԁa90 .K-$Lzchy;$#>E@%u\0ܓݒ1 4>&E` yf=爣Lcf@8 '2k"?9]@"40×`¡Oݰ >.Ѐ[.1N=S@BJ)R(,[`E+ R0n=򡹌‰۴i5pꪻ'|%q;/+0Ì(W$Qd}`Z,Pb`ꑇ,= r!@d30[ ,Ns+t3<{A@kq-si>~T>?c芳xo+zɤWFːI#~1 &lPAV\T3!`CC 6|8(CL, HF,48(.>3v` PuG(,r<uSfħh2Q 0 zI,ja eo=.vcfb"6)"U/͢ QB Qx5v،V̸;I@x  8TKh o( ұF{(H7|n0!^ÍC n!*nZ18 P@*PZq J&t%;u(4i$6ȢbGea8Z1t2` ( M-'T RrNTpV fQt(.Nȥ +l d(c0|\=ckלT c <4XE3&s ː,r&Ds1`40 @)$W:Vlyg e#G,屒 `@-8TOhDqsC2`Ér )xg7k d6M$@]9scUc @L)W9,JHUU B 8((Ji@#xqdBa1YV=t{hA ^P( S-\s@pSǃe\3]X~{&=Npy,q\(Ǻ @&H2˃¡m Hؑ>I ras{qV3†dfDeb>r?<irP qDfޓt 3s{}^ʿ.4 i?':͆،i\@ه\+f e4Q$cч]<`vS sHb`z(w&J5\P-,0Z (8Xb(,+x{Ůw󟏃{X`LxnK-.ơ ݛb.6Xh M%f!2 x} X} z7ks`<@Ypr!<ͲaN Cf0`0\d Pxw@  4\yTHkGs3@h`O2"ˤqT&Y8)0!-{0NrpMmPQ# p u` sOv4'@#b 7V)2l1wt gD[# 05QnV{1 jy(:Uxz&88`vFA SƠq4zڀhz}؀ u}  '~EiB 8)v =b >!4P vڬz|جZ\}Jh XX:QE&8p  g.@  P . }@j ʯ* G0Z?UEҰgpza`?O>(35Q`fr{ȲKV{@3`5WgaT <`&kF[hVGeVI  n&K.{|۷H`p6˥"ʕ@EAƭ1?6Z%& G(p \ Pv[a~;  #ZOU|8:dMVt ʷp.@¦#?_ 0 9}PyK +&95c[~3X )8#@]xxaV p( &׼tЎ(Ȓe p^Sx5i& wh&)`&fmU1XI0 z"۷ێ)-;o 2I&4;"$#0q&mD%KstX qZ\˒-9 k(GcdXX{, g&W\Pa,#p.)IpŰeR fllV { \# `}PrasLKĀ05m'ci=sQF R%SoX>v{nO' . `T9 \F{QÆAq9P 9><`˙F,}+p#G0␮\XW2Cq.e";˻,4s<H [ ǭƀe.˜,u+bk`t@j}[ēh&]XqK& $MƔ\=xy.0mZpDž'&aI= *8AR:'d9@}P+O=rom5!i-=^ 9H cpţ)`(Kq Օ[= <xA5SV+!KV<٤{$ uuْ iR!;5b@Sa@?Ve.쾤 ۣm{cTb§|E\ m:b7j~9üݷ-h4uWAí<_GvhEhxiƆ}ga#0^J;,P{9p-.x..[j% \,9srd<5imV%Qfç.7N k)޾l&i ^PI32h%!F X_u-M' ~k YЪ*90mSChdþZFt~"(bhrRh7Y+bEp-!#IAQ(\j9'@HML cbdgb!cG72@#)De*&7vr(D%Z~3CDCAD=-HA!bpKŒaV~[dj%~d&golt5F#A7c~7r1w)b`]y!4&a%\]qq7q~-}@!ra 9h9 6A:#дX8dj2/)vXЕ!9#Ae^iN*YtViR3X!f߽k{&ucz0FeYVj U\ԳdZN!rtpߘGW0YB3+4ώ#!Y7\"HgWt^G)cdɁnC߷߇q_}`1ƱM8bxA^8ወ\]WezR`⽅Poi vYP `C$ͤ" _uBE8,h"1G0̐` '*i P?\HtQB0 2a\. n`$Xwܓ "T)F0 C2 NL @`8$ G>v :#}}i U r\c&H)`Rq#J#YHW & GM֒І90F`pwJ) Y9Q PP[V8ࡲAY$q,2P #F-d|Ԅ4anEPX8.yD'~Uq/g5{ ]ERDhHb |4!ݜl.@: bN7E^d#(7<9q@ ;ЀBL*}:Z:.m)(Lisխ P3ozկl`;XְElbX6ֱa;itksnap/UserInterface/ImageIOWizard/Artwork/x01.png0000644000076500000240000000140011107537226021504 0ustar paulystaffPNG  IHDRT ssRGBgAMA a cHRMz&u0`:pQ<~IDAThC+p@E D"ȕH$"Db2D"$8$w HX~ea:IӔY+LvUTVeUUlyyF#$-%L& KAbс@@w:v&qZ.wERJIB`T.$%XF'"IPOAZM^GX@5PN!( `Aڏta%u DjQT)opqJ%+0ҴZ!AEbq>NmZ P:ͮtꚖ4 o6ߞBCKؾW,t6+vEP+WW%@SC{]Tn r޾O:kDd9ֵGI^~;-4uXZ uQ t㒭R*ޒȦs`_3$ O0,,IENDB`itksnap/UserInterface/ImageIOWizard/Artwork/y01.png0000644000076500000240000000143311107537226021513 0ustar paulystaffPNG  IHDRVsRGBgAMA a cHRMz&u0`:pQ<IDATXGŘ0!y$DV"Hz DV"HpH$%d!-p\Cr]"mDUo֠bjH fۂ*C*D@GѪ(SI]ֻzM\'gR)l/Lj߃qL^g Sj^ԲW,IJYe!B,w`!.*k2J?%ɰ_wn]}IKQIVĔ0e_R u fI8冹-Bi\ՄhBwvAeC5!`djBr,ۄV**oy-(F#:Us-{ʼ]" RGxs*sl fUa-~ 7`ۍݮMήfe;sjNXe>lt:YPweeeTa!]6,fa~eTa!1L e{C7,x<6M&=xz_Bo ۈI(Z|\@rZ/a Uowdr\+Zm,56vP.mWоr ԓ?a tTXA?;>db}+aUIENDB`itksnap/UserInterface/ImageIOWizard/Artwork/z01.png0000644000076500000240000000117411107537226021516 0ustar paulystaffPNG  IHDR)x"sRGBgAMA a cHRMz&u0`:pQ<IDATXG΂0qc{ \wNHIGG H+6!&s~_NY=Oڵ^Wuʲ(]{:int^Cz( Cj5 VPi҂lR&jԔZ͂[j5jBAUEPxZ-2[ ВR8*y5t81.U+=h$:uN:e.V ;ԽӃJ ->OVJ6Ҏs˩=L. A0yT*2˟wBQhM9SL  | eї!P\>𶽆<ϝ,jA!0A2u 7> u9@' +P~(^'F=ME[C.[Wm*{eBN4 OogPn8ZNn)IENDB`itksnap/UserInterface/ImageIOWizard/Artwork/z02.png0000644000076500000240000000115211107537226021513 0ustar paulystaffPNG  IHDR)x"sRGBgAMA a cHRMz&u0`:pQ<IDATXG0F!H$0$HB{̒=YD"D"/|]6ZkaKO瓦xf!?(O4v ("d& N'`ͲL@^[@QOlFKuVN9V׌1鮡] <"g`e_mb; v崮 rPp8[EDXy|>;7\[(hX~]El$x&(nFz#C#6zCCP/(㑰D rd(+mՋVS,M9ʨn2ƼqlmOo$V7ep_[By\N,fk TR;}mB {S[IМRV_(;!VĴe+/P<@Al hu VޡmmU(A*(wJicIENDB`itksnap/UserInterface/ImageIOWizard/CVS/0000755000076500000240000000000011560342171017371 5ustar paulystaffitksnap/UserInterface/ImageIOWizard/CVS/Entries0000644000076500000240000000052311560342171020725 0ustar paulystaff/ImageIOWizard.fl/1.8/Tue Jun 15 16:27:44 2010// /ImageIOWizardBase.h/1.3/Sat Nov 15 12:20:38 2008// /ImageIOWizardLogic.cxx/1.8/Wed May 4 15:25:42 2011// /ImageIOWizardLogic.h/1.9/Thu Jul 23 15:50:58 2009// /RestrictedImageIOWizardLogic.cxx/1.3/Thu Oct 14 16:21:04 2010// /RestrictedImageIOWizardLogic.h/1.6/Wed Jul 22 21:06:24 2009// D itksnap/UserInterface/ImageIOWizard/CVS/Entries.Log0000644000076500000240000000002011560342171021435 0ustar paulystaffA D/Artwork//// itksnap/UserInterface/ImageIOWizard/CVS/Repository0000644000076500000240000000004411560342171021471 0ustar paulystaffitksnap/UserInterface/ImageIOWizard itksnap/UserInterface/ImageIOWizard/CVS/Root0000644000076500000240000000010011560342171020226 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/ImageIOWizard/ImageIOWizard.fl0000644000076500000240000004161711405725000021717 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0109 header_name {.h} code_name {.cxx} class ImageIOWizard {open : {private ImageIOWizardBase} } { Function {MakeWindow()} {open return_type {virtual void} } { Fl_Window m_WinInput { label {ITK-SNAP Image Input Wizard} open xywh {522 175 470 330} type Double code0 {\#include "ImageIOWizardBase.h"} modal visible } { Fl_Wizard m_WizInput {open xywh {-5 -10 505 395} } { Fl_Group m_PageFile { label {File Selection} xywh {0 0 470 330} box PLASTIC_DOWN_BOX labelfont 2 labelsize 10 hide } { Fl_Group m_InFilePage { label {Select a file that contains an image:} xywh {10 15 370 40} labeltype EMBOSSED_LABEL align 20 } {} Fl_Input m_InFilePageBrowser { label {Image file name:} callback {this->OnFilePageFileInputChange();} tooltip {Enter the filename of the image you want to load, or select one using the 'Browse' and 'History' buttons} xywh {30 100 405 25} labelsize 12 align 5 when 1 textsize 12 } Fl_Button {} { label {&Browse...} callback {this->OnFilePageBrowse();} xywh {265 130 80 25} box PLASTIC_UP_BOX shortcut 0x80062 labelsize 12 } Fl_Menu_Button m_InFilePageHistory { label History callback {this->OnFilePageFileHistoryChange();} tooltip {Select one of the recently loaded images} xywh {355 130 80 25} box PLASTIC_UP_BOX selection_color 181 labelsize 12 align 20 textsize 12 } {} Fl_Choice m_InFilePageFormat { label {Image file format:} callback {this->OnFilePageFileFormatChange();} tooltip {Use this dropdown box to select an image format if one is not selected for you automatically. Normally, SNAP will figure out the file format once you select an image file using the 'Browse' button.} xywh {30 190 210 25} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } { MenuItem {} { label {Select a format...} xywh {10 10 100 20} labelsize 12 hide } } Fl_Group {} { label {Button Group} open xywh {190 285 280 45} labeltype NO_LABEL } { Fl_Button m_BtnFilePageNext { label {&Next >} callback {this->OnFilePageNext();} xywh {290 295 80 25} box PLASTIC_UP_BOX shortcut 0x8006e color 180 labelfont 1 labelsize 12 deactivate } Fl_Button {} { label {< Back} xywh {200 295 80 25} box PLASTIC_UP_BOX color 180 labelsize 12 deactivate } Fl_Button {} { label Cancel callback {this->OnCancel();} xywh {380 295 80 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } } } Fl_Group m_PageHeader { label {Header Info} xywh {0 0 470 330} box PLASTIC_DOWN_BOX labelfont 2 labelsize 10 hide deactivate } { Fl_Group {} { label {Supply missing header information:} open xywh {10 15 370 40} labeltype EMBOSSED_LABEL align 20 } {} Fl_Group {} { label {Header size:} xywh {30 60 360 35} labelsize 12 align 20 } { Fl_Value_Input m_InHeaderPageHeaderSize { label bytes callback {this->OnHeaderPageInputChange();} xywh {185 65 70 25} labelsize 12 align 8 textsize 12 } } Fl_Group {} { label {Image dimensions:} open xywh {30 100 405 35} labelsize 12 align 20 } { Fl_Value_Input m_InHeaderPageDimX { label {X:} callback {this->OnHeaderPageInputChange();} tooltip {The dimension of the image volume, in voxels} xywh {185 105 70 25} labelsize 12 textsize 12 } Fl_Value_Input m_InHeaderPageDimY { label {Y:} callback {this->OnHeaderPageInputChange();} tooltip {The dimension of the image volume, in voxels} xywh {275 105 70 25} labelsize 12 textsize 12 } Fl_Value_Input m_InHeaderPageDimZ { label {Z:} callback {this->OnHeaderPageInputChange();} tooltip {The dimension of the image volume, in voxels} xywh {365 105 70 25} labelsize 12 textsize 12 } } Fl_Group {} { label {Voxel spacing:} open xywh {30 140 405 35} labelsize 12 align 20 } { Fl_Value_Input m_InHeaderPageSpacingX { label {X:} callback {this->OnHeaderPageInputChange();} tooltip {Voxel size in each dimension (in mm)} xywh {185 145 70 25} labelsize 12 maximum 100 step 0.01 value 1 textsize 12 } Fl_Value_Input m_InHeaderPageSpacingY { label {Y:} callback {this->OnHeaderPageInputChange();} tooltip {Voxel size in each dimension (in mm)} xywh {275 145 70 25} labelsize 12 maximum 100 step 0.01 value 1 textsize 12 } Fl_Value_Input m_InHeaderPageSpacingZ { label {Z:} callback {this->OnHeaderPageInputChange();} tooltip {Voxel size in each dimension (in mm)} xywh {365 145 70 25} labelsize 12 maximum 100 step 0.01 value 1 textsize 12 } } Fl_Group {} { label {Voxel representation:} open xywh {30 180 360 35} labelsize 12 align 20 } { Fl_Choice m_InHeaderPageVoxelType { callback {this->OnHeaderPageInputChange();} open tooltip {Data type of the voxels} xywh {185 185 195 25} down_box BORDER_BOX labelsize 12 align 20 textsize 12 } { MenuItem {} { label {8 bit, unsigned (uchar)} xywh {5 5 100 20} labelsize 12 } MenuItem {} { label {8 bit, signed (char)} xywh {15 15 100 20} labelsize 12 } MenuItem {} { label {16 bit, unsigned (ushort)} xywh {25 25 100 20} labelsize 12 } MenuItem {} { label {16 bit, signed (short)} xywh {35 35 100 20} labelsize 12 } MenuItem {} { label {32 bit, unsigned (uint)} xywh {45 45 100 20} labelsize 12 } MenuItem {} { label {32 bit, signed (int)} xywh {55 55 100 20} labelsize 12 } MenuItem {} { label {32 bit, floating point (float)} xywh {65 65 100 20} labelsize 12 } MenuItem {} { label {64 bit, floating point (double)} xywh {75 75 100 20} labelsize 12 } } } Fl_Group {} { label {Byte alignment:} open xywh {30 220 360 35} labelsize 12 align 20 } { Fl_Choice m_InHeaderPageByteAlign { callback {this->OnHeaderPageInputChange();} open tooltip {Format in which multi-byte values are stored in the image} xywh {185 225 195 25} down_box BORDER_BOX labelsize 12 align 20 textsize 12 } { MenuItem {} { label {Big Endian (UNIX, Mac)} xywh {15 15 100 20} labelsize 12 } MenuItem {} { label {Little Endian (Intel, AMD)} xywh {25 25 100 20} labelsize 12 } } } Fl_Group {} { label {Button Group} open xywh {190 285 280 45} labeltype NO_LABEL } { Fl_Button m_BtnHeaderPageNext { label {&Next >} callback {this->OnHeaderPageNext();} xywh {290 295 80 25} box PLASTIC_UP_BOX shortcut 0x8006e color 180 labelfont 1 labelsize 12 } Fl_Button m_BtnHeaderPageBack { label {< Back} callback {this->OnHeaderPageBack();} xywh {200 295 80 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label Cancel callback {this->OnCancel();} xywh {380 295 80 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } } } Fl_Group m_PageDICOM { xywh {0 0 480 340} hide deactivate } { Fl_Group {} { label {DICOM Input Options:} open xywh {10 5 370 40} labeltype EMBOSSED_LABEL align 20 } {} Fl_Group {} { label {Button Group} open xywh {180 270 280 45} labeltype NO_LABEL } { Fl_Button m_BtnDICOMPageNext { label {&Next >} callback {this->OnDICOMPageNext();} xywh {280 280 80 25} box PLASTIC_UP_BOX shortcut 0x8006e color 180 labelfont 1 labelsize 12 } Fl_Button m_BtnDICOMPageBack { label {< Back} callback {this->OnDICOMPageBack();} xywh {190 280 80 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label Cancel callback {this->OnCancel();} xywh {370 280 80 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } } Fl_Check_Button {} { label { Load by sequence (all slices with the sequence id below)} xywh {25 50 405 25} down_box DOWN_BOX value 1 labelsize 12 } Fl_Check_Button {} { label { Load by filename order} xywh {25 125 405 25} down_box DOWN_BOX labelsize 12 deactivate } Fl_Check_Button {} { label { Load all slices in the directory} xywh {25 200 405 25} down_box DOWN_BOX labelsize 12 deactivate } Fl_Choice m_InDICOMPageSequenceId { label {Sequence id:} open xywh {165 80 270 25} down_box BORDER_BOX labelsize 12 textsize 12 } {} Fl_Value_Input {} { label {First Slice:} xywh {230 160 65 25} labelsize 12 maximum 10000 step 1 value 1 textsize 12 deactivate } Fl_Value_Input {} { label {Last Slice:} xywh {370 160 65 25} labelsize 12 maximum 10000 step 1 value 1 textsize 12 deactivate } } Fl_Group m_PageSummary { label Summary open xywh {0 0 470 330} box PLASTIC_DOWN_BOX labelfont 2 labelsize 10 } { Fl_Group {} { label {Image Summary:} xywh {10 15 370 40} labeltype EMBOSSED_LABEL align 20 } {} Fl_Group {} { label {Button Group} xywh {190 285 280 45} labeltype NO_LABEL } { Fl_Button {} { label {&Finish} callback {this->OnSummaryPageFinish();} xywh {290 295 80 25} box PLASTIC_UP_BOX shortcut 0x80066 color 180 labelfont 1 labelsize 12 } Fl_Button {} { label {< Back} callback {this->OnSummaryPageBack();} xywh {200 295 80 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label Cancel callback {this->OnCancel();} xywh {380 295 80 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } } Fl_Output m_OutSummaryFileName { label {File name:} xywh {100 55 350 20} color 54 labelsize 12 textsize 12 } Fl_Output {m_OutSummaryDimensions[0]} { label {Dimensions:} xywh {100 80 55 20} labelsize 12 textsize 12 class SNAPFormattedOutput } Fl_Output {m_OutSummaryDimensions[1]} { xywh {155 80 55 20} labelsize 12 textsize 12 class SNAPFormattedOutput } Fl_Output {m_OutSummaryDimensions[2]} { xywh {210 80 55 20} labelsize 12 textsize 12 class SNAPFormattedOutput } Fl_Output m_OutSummaryOrientation { label {Orientation:} tooltip {Note: origin information is not compatible with SPM/MRICro for Analyze files} xywh {345 80 105 20} color 54 labelsize 12 textsize 12 } Fl_Output {m_OutSummarySpacing[0]} { label {Spacing:} selected xywh {100 105 55 20} labelsize 12 textsize 12 code0 {\#include "SNAPFormattedOutput.h"} class SNAPFormattedOutput } Fl_Output {m_OutSummarySpacing[1]} { xywh {155 105 55 20} labelsize 12 textsize 12 class SNAPFormattedOutput } Fl_Output {m_OutSummarySpacing[2]} { xywh {210 105 55 20} labelsize 12 textsize 12 class SNAPFormattedOutput } Fl_Output {m_OutSummaryOrigin[0]} { label {Origin:} xywh {100 130 55 20} labelsize 12 textsize 12 class SNAPFormattedOutput } Fl_Output {m_OutSummaryOrigin[1]} { xywh {155 130 55 20} labelsize 12 textsize 12 class SNAPFormattedOutput } Fl_Output {m_OutSummaryOrigin[2]} { xywh {210 130 55 20} labelsize 12 textsize 12 class SNAPFormattedOutput } Fl_Output m_OutSummaryByteOrder { label {Byte order:} xywh {345 105 105 20} color 54 labelsize 12 textsize 12 } Fl_Output m_OutSummaryPixelType { label {Data type:} xywh {345 130 105 20} color 54 labelsize 12 textsize 12 } Fl_Value_Output m_OutSummarySize { label {Size in KB:} xywh {345 155 105 20} color 54 labelsize 12 maximum 1e+08 textsize 12 } Fl_Group {} { label {Image Header:} open xywh {95 180 355 105} box PLASTIC_DOWN_BOX color 7 labelsize 12 align 5 } { Fl_Box m_TableSummaryMetaData { xywh {100 185 345 95} selection_color 246 code0 {\#include "MetaDataTable.h"} class MetaDataTable } } } } } Fl_Window m_WinOutput { label {ITK-SNAP Save Image Wizard} xywh {809 564 470 330} type Double modal visible } { Fl_Wizard m_WizOutput { xywh {-5 -10 505 395} } { Fl_Group m_PageSaveFile { label {File Selection} xywh {0 0 470 330} box PLASTIC_DOWN_BOX labelfont 2 labelsize 10 } { Fl_Group m_InSaveFile { label {Choose a file name to save the image:} xywh {10 15 370 40} labeltype EMBOSSED_LABEL align 20 } {} Fl_Input m_InSaveFilePageBrowser { label {Filename:} callback {this->OnSaveFilePageFileInputChange();} tooltip {Enter the filename of the image you want to save, or select one using the 'Browse' and 'History' buttons} xywh {30 100 405 25} labelsize 12 align 5 when 1 textsize 12 } Fl_Button {} { label {&Browse...} callback {this->OnSaveFilePageBrowse();} xywh {265 130 80 25} box PLASTIC_UP_BOX shortcut 0x80062 labelsize 12 } Fl_Menu_Button m_InSaveFilePageHistory { label History callback {this->OnSaveFilePageFileHistoryChange();} open xywh {355 130 80 25} box PLASTIC_UP_BOX selection_color 181 labelsize 12 align 20 textsize 12 } {} Fl_Choice m_InSaveFilePageFormat { label {Image file format:} callback {this->OnSaveFilePageFileFormatChange();} open tooltip {Use this dropdown box to select an image format if one is not selected for you automatically. Normally, ITK-SNAP will figure out the file format once you select an image file using the 'Browse' button.} xywh {30 190 210 25} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } { MenuItem {} { label {Select a format...} xywh {0 0 100 20} labelsize 12 hide } } Fl_Group {} { label {Button Group} xywh {190 285 280 45} labeltype NO_LABEL } { Fl_Button m_BtnSaveFilePageNext { label {&Save} callback {this->OnSaveFilePageSave();} xywh {290 295 80 25} box PLASTIC_UP_BOX shortcut 0x80073 color 180 labelfont 1 labelsize 12 deactivate hotspot } Fl_Button {} { label Cancel callback {this->OnSaveCancel();} xywh {380 295 80 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } } } } } } } itksnap/UserInterface/ImageIOWizard/ImageIOWizardBase.h0000644000076500000240000000476011107537226022350 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageIOWizardBase.h,v $ Language: C++ Date: $Date: 2008/11/15 12:20: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. =========================================================================*/ #ifndef __ImageIOWizardBase_h_ #define __ImageIOWizardBase_h_ /** * \class ImageIOWizardBase * The base class for the Image IO Wizard window. This class declares * callback functions and the child of ImageIOWizard implements them */ class ImageIOWizardBase { public: virtual ~ImageIOWizardBase() {} virtual void OnCancel() = 0; virtual void OnFilePageBrowse() = 0; virtual void OnFilePageNext() = 0; virtual void OnFilePageFileInputChange() = 0; virtual void OnFilePageFileHistoryChange() = 0; virtual void OnFilePageFileFormatChange() = 0; virtual void OnHeaderPageNext() = 0; virtual void OnHeaderPageBack() = 0; virtual void OnHeaderPageInputChange() = 0; virtual void OnDICOMPageNext() = 0; virtual void OnDICOMPageBack() = 0; virtual void OnSummaryPageFinish() = 0; virtual void OnSummaryPageBack() = 0; // Save related functions virtual void OnSaveFilePageFileInputChange() = 0; virtual void OnSaveFilePageFileFormatChange() = 0; virtual void OnSaveFilePageFileHistoryChange() = 0; virtual void OnSaveFilePageBrowse() = 0; virtual void OnSaveFilePageSave() = 0; virtual void OnSaveCancel() = 0; }; #endif itksnap/UserInterface/ImageIOWizard/ImageIOWizardLogic.cxx0000755000076500000240000007476111560267766023135 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageIOWizardLogic.cxx,v $ Language: C++ Date: $Date: 2011/05/04 15:25:42 $ Version: $Revision: 1.8 $ 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 "FL/Fl.H" #include "FL/Fl_Native_File_Chooser.H" #include "FL/filename.H" #include "FL/fl_ask.H" #include "FL/Fl_Text_Buffer.H" #include #include #include #include #include #include #include "ImageIOWizardLogic.h" #include "itkOrientedImage.h" #include "itkImageIOBase.h" #include "itkIOCommon.h" #include "itkMetaDataObject.h" #include "itkGDCMSeriesFileNames.h" #include using std::map; ImageIOWizardLogic ::ImageIOWizardLogic() { // Initialize the DICOM directory lister m_DICOMLister = itk::GDCMSeriesFileNames::New(); // Initialize the text buffers // m_SummaryTextBuffer = new Fl_Text_Buffer(); // Initialize the callback pointer m_Callback = NULL; } void ImageIOWizardLogic ::MakeWindow() { // Call the parent method ImageIOWizard::MakeWindow(); // Initialize the file save dialog box based on the allowed file types for(unsigned int i = 0; i < GuidedNativeImageIO::FORMAT_COUNT; i++) { // Create an appropriate description FileFormat fmt = static_cast(i); GuidedNativeImageIO::FileFormatDescriptor fd = GuidedNativeImageIO::GetFileFormatDescriptor(fmt); StringType text = fd.name + " File"; // Add a menu option to the save menu, disabling it if it's unsupported m_InFilePageFormat->add(text.c_str(),0,NULL,NULL, this->CanLoadFileFormat((FileFormat) i) ? 0 : FL_MENU_INACTIVE); // Add a menu option to the save menu, disabling it if it's unsupported m_InSaveFilePageFormat->add(text.c_str(),0,NULL,NULL, this->CanSaveFileFormat((FileFormat) i) ? 0 : FL_MENU_INACTIVE); } m_InSaveFilePageFormat->value(0); // Add the buffer to the summary text widget // m_OutSummaryMetaData->buffer(m_SummaryTextBuffer); } void ImageIOWizardLogic ::GoBack(Fl_Group *current) { // The link to the last page Fl_Group *last = NULL; // In the input is NULL, get the current page from the wizard widget current = (current == NULL) ? (Fl_Group *)m_WizInput->value() : current; if (current == m_PageHeader || current == m_PageDICOM) { last = m_PageFile; } else if (current == m_PageSummary) { last = m_PageHeader; } else { assert(0 == "Next page not valid at this position in the wizard"); } if (!last->active()) { GoBack(last); } else { m_WizInput->value(last); } } void ImageIOWizardLogic ::GoForward(Fl_Group *current) { // The link to the next page Fl_Group *next = NULL; // In the input is NULL, get the current page from the wizard widget current = (current == NULL) ? (Fl_Group *)m_WizInput->value() : current; // Follow the sequence of the pages if(current == m_PageFile) { if(m_PageHeader->active()) next = m_PageHeader; else if(m_PageDICOM->active()) next = m_PageDICOM; else next = m_PageSummary; } else if (current == m_PageHeader || current == m_PageDICOM) { next = m_PageSummary; } else { assert(0 == "Next page not valid"); } // Check if the page is active if (!next->active()) { GoForward(next); return; } // Flip to the new page m_WizInput->value(next); // Call the enter-page method if (next == m_PageHeader) { OnHeaderPageEnter(); } else if (next == m_PageDICOM) { OnDICOMPageEnter(); } else if (next == m_PageSummary) { OnSummaryPageEnter(); } } ImageIOWizardLogic::StringType ImageIOWizardLogic ::GetFilePattern(bool forLoading) { // String containing the whole patterns StringType pattern = ""; bool patternNeedsTab = false; // String containing the "All Image Files" pattern StringType allImageFiles = "All Image Files\t*.{"; bool allImageFilesNeedsComma = false; // 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((forLoading && this->CanLoadFileFormat(fmt)) || (!forLoading && this->CanSaveFileFormat(fmt))) { // Add comma to allImageFiles if(allImageFilesNeedsComma) allImageFiles += ","; else allImageFilesNeedsComma = true; // Add extension to all image files allImageFiles += fd.pattern; // Add a tab to the pattern if(patternNeedsTab) pattern += "\n"; else patternNeedsTab = true; // Construct the pattern pattern += fd.name; pattern += " Files\t*.{"; pattern += fd.pattern; pattern += "}"; } } // Finish the all image pattern allImageFiles += "}\n"; // Compete the pattern pattern = allImageFiles + pattern; return pattern; } ImageIOWizardLogic::FileFormat ImageIOWizardLogic ::DetermineFileFormatFromFileName(bool forLoading, const char *testFile) { // Iterate over the known file types 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((forLoading && this->CanLoadFileFormat(fmt)) || (!forLoading && this->CanSaveFileFormat(fmt))) { // Create a matching pattern StringType pattern = "*.{" + fd.pattern + "}"; // Check if the filename matches the pattern if(fl_filename_match(testFile,pattern.c_str())) return fmt; } } // Failed: return illegal pattern return GuidedNativeImageIO::FORMAT_COUNT; } bool ImageIOWizardLogic ::CanLoadFileFormat(FileFormat irisNotUsed(format)) const { return true; } bool ImageIOWizardLogic ::CanSaveFileFormat(FileFormat format) const { GuidedNativeImageIO::FileFormatDescriptor fd = GuidedNativeImageIO::GetFileFormatDescriptor(format); return fd.can_write; } void ImageIOWizardLogic ::OnFilePageBrowse() { // Get the path and pattern for reading in the file StringType pattern = this->GetFilePattern(true); const char *path = m_InFilePageBrowser->value(); path = strlen(path) ? path : NULL; // Configure a file dialog const char *fName = NULL; Fl_Native_File_Chooser chooser; chooser.type(Fl_Native_File_Chooser::BROWSE_FILE); chooser.title("Load Image"); chooser.preset_file(path); chooser.filter(pattern.c_str()); if (chooser.show() == 0) { fName = chooser.filename(); } // Bring up th choice dialog if (fName && strlen(fName)) { // Set the new filename m_InFilePageBrowser->value(fName); // Reset the format drop-down box to a null value m_InFilePageFormat->value(0); // Run the filename change event OnFilePageFileInputChange(); } } void ImageIOWizardLogic ::OnFilePageFileFormatChange() { // Activate the next button if there is a format selected if(m_InFilePageFormat->value() > 0) m_BtnFilePageNext->activate(); else m_BtnFilePageNext->deactivate(); // If the user selects 'raw', update the format FileFormat format = (FileFormat) (m_InFilePageFormat->value() - 1); if (format == GuidedNativeImageIO::FORMAT_RAW) m_PageHeader->activate(); else m_PageHeader->deactivate(); // If the user selects 'dicom' enable the dicom page if(format == GuidedNativeImageIO::FORMAT_DICOM) m_PageDICOM->activate(); else m_PageDICOM->deactivate(); } void ImageIOWizardLogic ::OnFilePageFileHistoryChange() { // Copy the history to the file box m_InFilePageBrowser->value( m_InFilePageHistory->mvalue()->label()); // Update everything OnFilePageFileInputChange(); } void ImageIOWizardLogic ::OnFilePageFileInputChange() { // Clear the registry m_Registry.Clear(); // Check the length of the input const char *text = m_InFilePageBrowser->value(); if (text != NULL && strlen(text) > 0) { // Try to load the registry associated with this filename if(m_Callback) m_Callback->FindRegistryAssociatedWithImage( m_InFilePageBrowser->value(), m_Registry); // If the registry contains a file format, override with that FileFormat fmt = m_GuidedIO.GetFileFormat(m_Registry, GuidedNativeImageIO::FORMAT_COUNT); // Try to select a file format accoring to the file name if(fmt == GuidedNativeImageIO::FORMAT_COUNT) fmt = DetermineFileFormatFromFileName(true, text); // If the filename does not match any format, we do not change the // format choice box in case that the user has already set it manually if(fmt < GuidedNativeImageIO::FORMAT_COUNT) m_InFilePageFormat->value((int)fmt+1); // Run the format change event OnFilePageFileFormatChange(); } else { m_BtnFilePageNext->deactivate(); } } void ImageIOWizardLogic ::OnFilePageNext() { // Check if a file has been specified assert(m_InFilePageBrowser->value() && strlen(m_InFilePageBrowser->value()) > 0); // Check that a format has been specified assert(m_InFilePageFormat->value() > 0); // Get the selected format and place it in the registry FileFormat format = (FileFormat) (m_InFilePageFormat->value() - 1); m_GuidedIO.SetFileFormat(m_Registry, format); // If file format is raw or dicom, go to the next page, o.w., load image switch(format) { case GuidedNativeImageIO::FORMAT_RAW: GoForward(); break; case GuidedNativeImageIO::FORMAT_DICOM: if(ProcessDICOMDirectory()) GoForward(); break; default: if(DoLoadImage()) GoForward(); } } void ImageIOWizardLogic ::OnCancel() { // Set the status to negative: nothing was loaded m_ImageLoaded = false; // Hide this window m_WinInput->hide(); } void ImageIOWizardLogic ::OnHeaderPageNext() { // Make sure we're not here by mistake assert(m_InFilePageFormat->value() - 1 == (int) GuidedNativeImageIO::FORMAT_RAW); // Set up the registry with the specified values m_Registry["Raw.HeaderSize"] << (unsigned int) m_InHeaderPageHeaderSize->value(); // Set the dimensions m_Registry["Raw.Dimensions"] << Vector3i( (int) m_InHeaderPageDimX->value(), (int) m_InHeaderPageDimY->value(), (int) m_InHeaderPageDimZ->value()); // Set the spacing m_Registry["Raw.Spacing"] << Vector3d( m_InHeaderPageSpacingX->value(), m_InHeaderPageSpacingY->value(), m_InHeaderPageSpacingZ->value()); // Set the endianness m_Registry["Raw.BigEndian"] << ( m_InHeaderPageByteAlign->value() == 0 ); // Set the pixel type int iPixType = ((int)m_InHeaderPageVoxelType->value()); GuidedNativeImageIO::RawPixelType pixtype = (iPixType < 0) ? GuidedNativeImageIO::PIXELTYPE_COUNT : (GuidedNativeImageIO::RawPixelType) iPixType; m_GuidedIO.SetPixelType(m_Registry, pixtype); // Do the loading if(DoLoadImage()) GoForward(); } bool ImageIOWizardLogic ::ProcessDICOMDirectory() { // Get the directory tree from the file StringType dirname = itksys::SystemTools::FileIsDirectory(m_InFilePageBrowser->value()) ? m_InFilePageBrowser->value() : itksys::SystemTools::GetParentDirectory(m_InFilePageBrowser->value()); // Put up a wait cursor m_WinInput->cursor(FL_CURSOR_WAIT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Try to list the DICOM files in the directory try { m_DICOMLister->SetDirectory(dirname.c_str()); m_InFilePageBrowser->value(dirname.c_str()); } catch(...) { fl_alert("Error listing DICOM files in directory: \n %s",dirname.c_str()); } // Restore the cursor m_WinInput->cursor(FL_CURSOR_DEFAULT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Get the list of series in the directory if(m_DICOMLister->GetSeriesUIDs().size() == 0) { fl_alert("No DICOM series found in the directory: \n%s",dirname.c_str()); return false; } else return true; } void ImageIOWizardLogic ::OnDICOMPageEnter() { // Get the list of sequence ids const std::vector &uids = m_DICOMLister->GetSeriesUIDs(); // Add the ids to the menu m_InDICOMPageSequenceId->clear(); for(unsigned int i = 0; i < uids.size(); i++) m_InDICOMPageSequenceId->add(uids[i].c_str()); // See if one of the sequences in the registry matches StringType last = m_Registry["DICOM.SequenceId"]["NULL"]; const Fl_Menu_Item *lastpos = m_InDICOMPageSequenceId->find_item(last.c_str()); if(lastpos) m_InDICOMPageSequenceId->value(lastpos); else m_InDICOMPageSequenceId->value(0); } void ImageIOWizardLogic ::OnDICOMPageNext() { // The user will have selected some DICOM series. All we have to do is // specify the series in the registry and load the file m_Registry["DICOM.SequenceId"] << m_InDICOMPageSequenceId->mvalue()->label(); // In addition, generate an explicit array of filenames for this sequence id. // This will allow the DICOM loader to load files without having to list the // directory again const std::vector &files = m_DICOMLister->GetFileNames( StringType(m_InDICOMPageSequenceId->mvalue()->label())); m_Registry.Folder("DICOM.SliceFiles").PutArray(files); // Try loading the file now if(DoLoadImage()) GoForward(); } void ImageIOWizardLogic ::OnDICOMPageBack() { GoBack(); } bool ImageIOWizardLogic ::DoLoadImage() { bool rc; // Show a wait cursor m_WinInput->cursor(FL_CURSOR_WAIT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Try to load a file try { // Since we want to store the image IO, we need to use these two calls // instead of just calling ReadImage with the registry m_GuidedIO.ReadNativeImage(m_InFilePageBrowser->value(), m_Registry); /* if(this->IsNativeFormatSupported()) { RescaleNativeImageToScalar rescale; m_Image = rescale(&m_GuidedIO); m_NativeScale = rescale.GetNativeScale(); m_NativeShift = rescale.GetNativeShift(); } else if(m_GuidedIO.GetNumberOfComponentsInNativeImage() == 3) { CastNativeImageToRGB cast; m_Image = cast(&m_GuidedIO); m_NativeScale = 1.0; m_NativeShift = 0.0; } else { CastNativeImageToScalar cast; m_Image = cast(&m_GuidedIO); m_NativeScale = 1.0; m_NativeShift = 0.0; } // Disconnect the image from the reader to free up memory (?) m_GuidedIO.DeallocateNativeImage(); */ // Check if the image is really valid rc = CheckImageValidity(); } catch(itk::ExceptionObject &exc) { // Show the error fl_alert("Error reading image: %s.",exc.GetDescription()); rc = false; } // Fix the cursor m_WinInput->cursor(FL_CURSOR_DEFAULT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Check if the image is valid (subclasses can perform extra tasks here) return rc; } void ImageIOWizardLogic ::OnHeaderPageInputChange() { // Header input has changed if (m_InHeaderPageDimX->value() >= 1 && m_InHeaderPageDimY->value() >= 1 && m_InHeaderPageDimZ->value() >= 1 && m_InHeaderPageSpacingX->value() > 0 && m_InHeaderPageSpacingY->value() > 0 && m_InHeaderPageSpacingZ->value() > 0) { m_BtnHeaderPageNext->activate(); } else { m_BtnHeaderPageNext->deactivate(); } } void ImageIOWizardLogic ::OnHeaderPageBack() { GoBack(); } void ImageIOWizardLogic ::OnFilePageEnter() { } void ImageIOWizardLogic ::OnHeaderPageEnter() { // Use the values from the registry to set up the header page m_InHeaderPageHeaderSize->value(m_Registry["Raw.HeaderSize"][0]); // Set the dimensions Vector3i dims = m_Registry["Raw.Dimensions"][Vector3i(0)]; m_InHeaderPageDimX->value(dims[0]); m_InHeaderPageDimY->value(dims[1]); m_InHeaderPageDimZ->value(dims[2]); // Set the spacing Vector3d spacing = m_Registry["Raw.Spacing"][Vector3d(1.0)]; m_InHeaderPageSpacingX->value(spacing[0]); m_InHeaderPageSpacingY->value(spacing[1]); m_InHeaderPageSpacingZ->value(spacing[2]); // Set the data type RawPixelType pixtype = m_GuidedIO.GetPixelType(m_Registry, GuidedNativeImageIO::PIXELTYPE_UCHAR); m_InHeaderPageVoxelType->value((int)pixtype); // Set the endianness if(m_Registry["Raw.BigEndian"][true]) m_InHeaderPageByteAlign->value(0); else m_InHeaderPageByteAlign->value(1); } template bool try_print_metadata(std::ostream &sout, itk::MetaDataDictionary &mdd, std::string key) { AnyType value = 0; if(itk::ExposeMetaData(mdd, key, value)) { sout << key << " = " << value << std::endl; return true; } else return false; } void ImageIOWizardLogic ::OnSummaryPageEnter() { const char *boTypes[] = {"Big Endian", "Little Endian","Order Not Applicable"}; // The object better not be NULL! if(m_GuidedIO.IsNativeImageLoaded()) { // Native image itk::ImageBase<3>::Pointer native = m_GuidedIO.GetNativeImage(); // A stream to simplify converting to string IRISOStringStream sout; // Print file name m_OutSummaryFileName->value(m_GuidedIO.GetFileNameOfNativeImage().c_str()); // Print file dimensions, spacing and origin for(unsigned int i = 0; i < 3; i++) { m_OutSummaryDimensions[i]->value(native->GetBufferedRegion().GetSize()[i]); m_OutSummarySpacing[i]->value(native->GetSpacing()[i]); m_OutSummaryOrigin[i]->value(native->GetOrigin()[i]); } // Print file size in bytes m_OutSummarySize->value((int)(m_GuidedIO.GetFileSizeOfNativeImage() / (1024.0))); // Print out the orientation information sout.str(""); vnl_matrix dir = native->GetDirection().GetVnlMatrix(); std::string rai = ImageCoordinateGeometry::ConvertDirectionMatrixToClosestRAICode(dir); if(ImageCoordinateGeometry::IsDirectionMatrixOblique(dir)) sout << "Oblique (closest to " << rai << ")"; else sout << rai; m_OutSummaryOrientation->value(sout.str().c_str()); // TODO: This is a workaround on an itk bug with RawImageIO if(m_GuidedIO.GetComponentTypeInNativeImage() != itk::ImageIOBase::UNKNOWNCOMPONENTTYPE) { // There actually is a type in the IO object m_OutSummaryPixelType->value( m_GuidedIO.GetComponentTypeAsStringInNativeImage().c_str()); } else { m_OutSummaryPixelType->value(m_InHeaderPageVoxelType->text()); } // Print the byte order m_OutSummaryByteOrder->value( boTypes[(unsigned int)(m_GuidedIO.GetByteOrderInNativeImage() - ImageIOType::BigEndian)]); // Populate the metadata table m_TableSummaryMetaData->SetInputImage(m_GuidedIO.GetNativeImage()); m_TableSummaryMetaData->SetColumnWidth(345); // Dump the contents of the meta data dictionary /* m_SummaryTextBuffer->text(""); MetaDataDictionary &mdd = native->GetMetaDataDictionary(); for( MetaDataDictionary::ConstIterator itMeta = mdd.Begin(); itMeta != mdd.End(); ++itMeta) { // Get the metadata as a generic object std::string key = itMeta->first, v_string; std::ostringstream sout; if(itk::ExposeMetaData(mdd, key, v_string)) { // For some weird reason, some of the strings returned by this method // contain '\0' characters. We will replace them by spaces std::ostringstream tmpout(""); for(unsigned int i = 0; i < v_string.length(); i++) if(v_string[i] >= ' ') tmpout << v_string[i]; v_string = tmpout.str(); // Make sure the value has more than blanks if(v_string.find_first_not_of(" ") != v_string.npos) sout << key << " = " << v_string << std::endl; } else { bool rc = false; if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) rc |= try_print_metadata(sout, mdd, key); if(!rc) { sout << key << " of unsupported type " << itMeta->second->GetMetaDataObjectTypeName() << std::endl; } } m_SummaryTextBuffer->append(sout.str().c_str()); } */ } else { m_OutSummaryFileName->value("Error loading image."); m_OutSummarySize->value(0); m_OutSummaryOrientation->value("n/a"); m_OutSummaryPixelType->value("n/a"); m_OutSummaryByteOrder->value("n/a"); // m_SummaryTextBuffer->text(""); for(size_t i = 0; i < 3; i++) { m_OutSummaryDimensions[i]->value(0); m_OutSummarySpacing[i]->value(0); m_OutSummaryOrigin[i]->value(0); } } } void ImageIOWizardLogic ::OnSummaryPageFinish() { // Perform a final validity / sanity check if (CheckFinalValidity()) { // Set the status to positive, the image has been loaded! m_ImageLoaded = true; // Save the registry produced in this wizard if(m_Callback) m_Callback->UpdateRegistryAssociatedWithImage( m_InFilePageBrowser->value(), m_Registry); // Hide this window m_WinInput->hide(); } } ImageIOWizardLogic::~ImageIOWizardLogic() { // delete m_SummaryTextBuffer; } bool ImageIOWizardLogic ::NonInteractiveInputWizard(const char *file) { // Indicate no file loaded yet m_ImageLoaded = false; // Set the GUI filename input with the argument m_InFilePageBrowser->value(file); // Clear the registry m_Registry.Clear(); // Try to read the file if(file != NULL && strlen(file) > 0) { if(m_Callback) m_Callback->FindRegistryAssociatedWithImage(file, m_Registry); // If the registry contains a file format, override with that FileFormat fmt = m_GuidedIO.GetFileFormat(m_Registry, GuidedNativeImageIO::FORMAT_COUNT); // Try to select a file format according to the file name if(fmt == GuidedNativeImageIO::FORMAT_COUNT) fmt = DetermineFileFormatFromFileName(true, file); switch(fmt) { case GuidedNativeImageIO::FORMAT_RAW: case GuidedNativeImageIO::FORMAT_DICOM: std::cerr << "Loading RAW or DICOM data from command line is not supported" << std::endl; m_ImageLoaded = false; break; default: m_ImageLoaded = DoLoadImage(); } } return m_ImageLoaded; } bool ImageIOWizardLogic ::DisplayInputWizard(const char *file, const char *type) { // Set up the custom title if type is provided if(type) { std::string title = "Wizard for loading a "; title += type; title += " image"; m_WinInput->copy_label(title.c_str()); std::string topic = "Select a "; topic += type; topic += " image to load:"; m_InFilePage->copy_label(topic.c_str()); } // The loaded flag is false m_ImageLoaded = false; // Point the wizard to the first page m_WizInput->value(m_PageFile); // Clear the file name box // TODO: Implement a history list if(file) m_InFilePageBrowser->value(file); OnFilePageFileInputChange(); // Show the input window m_WinInput->show(); // Loop until the window has been closed while (m_WinInput->visible()) Fl::wait(); // Whether or not the load has been succesfull return m_ImageLoaded; } void ImageIOWizardLogic ::OnSummaryPageBack() { GoBack(); } bool ImageIOWizardLogic ::DisplaySaveWizardImpl(const char *file, const char *type) { // Set up the custom title if type is provided if(type) { std::string title = "Wizard for saving a "; title += type; title += " image"; m_WinOutput->copy_label(title.c_str()); std::string topic = "Choose a file name to save the "; topic += type; topic += " image:"; m_InSaveFile->copy_label(topic.c_str()); } // Clear the saved flag m_ImageSaved = false; // Clear the file name box if(file) m_InFilePageBrowser->value(file); OnSaveFilePageFileInputChange(); // Show the input window m_WinOutput->show(); // Loop until the window has been closed while (m_WinOutput->visible()) Fl::wait(); // Clear the image pointer delete m_SaveCallback; // Whether or not the load has been succesfull return m_ImageSaved; } void ImageIOWizardLogic ::OnSaveFilePageFileInputChange() { // Clear the registry m_Registry.Clear(); // Check the length of the input const char *text = m_InSaveFilePageBrowser->value(); if (text != NULL && strlen(text) > 0) { // Try to load the registry associated with this filename if(m_Callback) m_Callback->FindRegistryAssociatedWithImage( m_InSaveFilePageBrowser->value(), m_Registry); // If the registry contains a file format, override with that FileFormat fmt = m_GuidedIO.GetFileFormat(m_Registry, GuidedNativeImageIO::FORMAT_COUNT); // Try to select a file format accoring to the file name if(fmt == GuidedNativeImageIO::FORMAT_COUNT) fmt = DetermineFileFormatFromFileName(true, text); // If the filename does not match any format, we do not change the // format choice box in case that the user has already set it manually if(fmt < GuidedNativeImageIO::FORMAT_COUNT) m_InSaveFilePageFormat->value((int)fmt+1); // Run the format change event OnSaveFilePageFileFormatChange(); } else { m_BtnSaveFilePageNext->deactivate(); } } void ImageIOWizardLogic ::OnSaveFilePageFileHistoryChange() { // Copy the history to the file box m_InSaveFilePageBrowser->value( m_InSaveFilePageHistory->mvalue()->label()); // Update everything OnSaveFilePageFileInputChange(); } void ImageIOWizardLogic ::OnSaveFilePageFileFormatChange() { // Activate the next button if there is a format selected if(m_InSaveFilePageFormat->value() > 0) m_BtnSaveFilePageNext->activate(); else m_BtnSaveFilePageNext->deactivate(); } void ImageIOWizardLogic ::OnSaveFilePageBrowse() { // Get the path and pattern for reading in the file const char *path = m_InSaveFilePageBrowser->value(); path = strlen(path) ? path : NULL; // Get a pattern StringType pattern = this->GetFilePattern(false); // Create a file chooser const char *fName = NULL; Fl_Native_File_Chooser chooser; chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); chooser.title("Save Image As"); chooser.options(Fl_Native_File_Chooser::NEW_FOLDER); chooser.preset_file(path); chooser.filter(pattern.c_str()); if (chooser.show() == 0) { fName = chooser.filename(); } if (fName && strlen(fName)) { // Set the new filename m_InSaveFilePageBrowser->value(fName); // Reset the format drop-down box to a null value m_InSaveFilePageFormat->value(0); // Run the filename change event OnSaveFilePageFileInputChange(); } } void ImageIOWizardLogic ::OnSaveFilePageSave() { // There better be a format selected assert(m_InSaveFilePageFormat->value() > 0); // Get the selected format and place it in the registry FileFormat format = (FileFormat) (m_InSaveFilePageFormat->value() - 1); m_GuidedIO.SetFileFormat(m_Registry, format); // Put up a waiting cursor m_WinOutput->cursor(FL_CURSOR_WAIT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Try to save the image using the current format try { // Try to save the image m_SaveCallback->Save( m_InSaveFilePageBrowser->value(), m_Registry); m_ImageSaved = true; // Save the registry produced in this wizard if(m_Callback) m_Callback->UpdateRegistryAssociatedWithImage( m_InFilePageBrowser->value(), m_Registry); // Hide the dialog m_WinOutput->hide(); } catch(itk::ExceptionObject &exc) { fl_alert("Error saving file: %s",exc.GetDescription()); } // Restore the cursor m_WinOutput->cursor(FL_CURSOR_DEFAULT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); } void ImageIOWizardLogic ::OnSaveCancel() { // Just close the window m_WinOutput->hide(); } void ImageIOWizardLogic ::SetHistory(const HistoryType &history) { // Store the history m_History = history; // Clear the history drop box m_InFilePageHistory->clear(); m_InSaveFilePageHistory->clear(); // Add the history if(history.size() > 0) { // Add each item to the history menu (history is traversed // backwards) for(HistoryType::reverse_iterator it=m_History.rbegin(); it!=m_History.rend();it++) { // FLTK's add() treats slashes as submenu separators, hence this code m_InFilePageHistory->replace( m_InFilePageHistory->add("dummy"),it->c_str()); m_InSaveFilePageHistory->replace( m_InSaveFilePageHistory->add("dummy"),it->c_str()); } // Activate the history menu m_InFilePageHistory->activate(); m_InSaveFilePageHistory->activate(); } else { // Deactivate history m_InFilePageHistory->deactivate(); m_InSaveFilePageHistory->deactivate(); } } itksnap/UserInterface/ImageIOWizard/ImageIOWizardLogic.h0000644000076500000240000002311111232103342022505 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ImageIOWizardLogic.h,v $ Language: C++ Date: $Date: 2009/07/23 15:50:58 $ Version: $Revision: 1.9 $ 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 __ImageIOWizardLogic_h_ #define __ImageIOWizardLogic_h_ #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include #include #endif #include #include #include #include "ImageIOWizard.h" #include "FL/fl_draw.H" #include "itkSmartPointer.h" #include "ImageCoordinateGeometry.h" #include "Registry.h" #include #include #include "GuidedNativeImageIO.h" class Fl_Text_Buffer; namespace itk { template class Size; template class ImageBase; class ImageIOBase; class GDCMSeriesFileNames; } /** * \class ImageInfoCallbackInterface * \brief A virtual class that is used to provide image-specific information to * the ImageIOWizardLogic object */ class ImageInfoCallbackInterface { public: virtual ~ImageInfoCallbackInterface() {} virtual bool FindRegistryAssociatedWithImage( const char *file, Registry ®istry) = 0; virtual void UpdateRegistryAssociatedWithImage( const char *file, Registry ®istry) = 0; }; /** * \class ImageIOWizardLogic * The implementation of the Image IO Wizard UI class. This class defines the * callbacks in the user interface for the class. It is templated over the type * of the input that is to be procured. */ class ImageIOWizardLogic : public ImageIOWizard { public: // Image IO type definition typedef itk::ImageIOBase ImageIOType; typedef itk::SmartPointer ImageIOPointer; // Other typedefs typedef std::string StringType; typedef std::vector HistoryType; /** A constructor */ ImageIOWizardLogic(); /** A descructor */ virtual ~ImageIOWizardLogic(); // Callback methods overridden from the Base class virtual void OnCancel(); virtual void OnFilePageBrowse(); virtual void OnFilePageNext(); virtual void OnFilePageFileInputChange(); virtual void OnFilePageFileFormatChange(); virtual void OnFilePageFileHistoryChange(); virtual void OnHeaderPageNext(); virtual void OnHeaderPageBack(); virtual void OnHeaderPageInputChange(); virtual void OnDICOMPageNext(); virtual void OnDICOMPageBack(); virtual void OnSummaryPageFinish(); virtual void OnSummaryPageBack(); // Functions called on entering wizard input pages virtual void OnFilePageEnter(); virtual void OnHeaderPageEnter(); virtual void OnDICOMPageEnter(); virtual void OnSummaryPageEnter(); // Save related functions virtual void OnSaveFilePageFileInputChange(); virtual void OnSaveFilePageFileFormatChange(); virtual void OnSaveFilePageFileHistoryChange(); virtual void OnSaveFilePageBrowse(); virtual void OnSaveFilePageSave(); virtual void OnSaveCancel(); // Custom initialization code virtual void MakeWindow(); /** * Has the image been loaded successfully? */ bool IsImageLoaded() { return m_ImageLoaded; } /** * Unload the image to free memory (do this after retrieving the image for * use elsewhere) */ void ReleaseImage() { m_GuidedIO.DeallocateNativeImage(); m_ImageLoaded = false; } /** * A method to initialize the pages in the wizard. Child classes should override * this and enable/disable irrelevant pages and fields. The method takes as a * parameter an ImageWrapper, to which it makes changes. * * The method returns true if an image was loaded with success, false in case of cancellation */ virtual bool DisplayInputWizard(const char *file, const char *type = NULL); /** * A method to save an image using the wizard (at this point it's just a one * page wizard. This method is templated over the image type. This is a half-ass * solution, but it's better than having the whole wizard templated over the image * type. */ template bool DisplaySaveWizard( itk::OrientedImage *image, const char *file, const char *type = NULL) { m_SaveCallback = new TypedSaveCallback(this, image); return DisplaySaveWizardImpl(file, type); } // Non-interactive wizard virtual bool NonInteractiveInputWizard(const char *file); /** * Get the filename that was loaded */ const char *GetFileName() { return m_InFilePageBrowser->value(); } /** Get the filename that was saved */ const char *GetSaveFileName() { return m_InSaveFilePageBrowser->value(); } /** Set the history list of recently opened files */ void SetHistory(const HistoryType &history); /** Set the callback object for retrieving historical information about * previously loaded images */ void SetImageInfoCallback(ImageInfoCallbackInterface *iCallback) { this->m_Callback = iCallback; } /** Get the native image IO */ GuidedNativeImageIO *GetNativeImageIO() { return &m_GuidedIO; } protected: // Base class for save callback system. class TypedSaveCallbackBase { public: TypedSaveCallbackBase() {} virtual ~TypedSaveCallbackBase() {} virtual void Save(const char *fname, Registry &folder) = 0; }; // Templated class for save callback system. template class TypedSaveCallback : public TypedSaveCallbackBase { public: typedef itk::OrientedImage ImageType; TypedSaveCallback(ImageIOWizardLogic *client, ImageType *image) : m_Client(client), m_Image(image) {} void Save(const char *fname, Registry &folder) { m_Client->GetNativeImageIO()->SaveImage(fname, folder, m_Image.GetPointer()); } private: ImageIOWizardLogic *m_Client; typename ImageType::Pointer m_Image; }; /** An image being loaded */ // ImagePointer m_Image; /** The image IO mechanism */ // ImageIOPointer m_ImageIO; /** Has the image been loaded? */ bool m_ImageLoaded; /** Has the image been saved? */ bool m_ImageSaved; /** A history of recently opened files */ HistoryType m_History; /** A callback for retrieving image information */ ImageInfoCallbackInterface *m_Callback; /** DICOM file names lister */ itk::SmartPointer m_DICOMLister; /** The registry associated with the image currently being considered for loading */ Registry m_Registry; /** A guided image IO object used to load images */ GuidedNativeImageIO m_GuidedIO; /** Text buffer for one of the summary widgets */ Fl_Text_Buffer *m_SummaryTextBuffer; // -- Stuff dealing with file formats -- typedef GuidedNativeImageIO::FileFormat FileFormat; typedef GuidedNativeImageIO::RawPixelType RawPixelType; /** Extensions for different file formats */ StringType m_FileFormatPattern[GuidedNativeImageIO::FORMAT_COUNT]; /** Brief descriptions of different file formats */ StringType m_FileFormatDescription[GuidedNativeImageIO::FORMAT_COUNT]; /** * Allow children to specify which file formats they can and can't save */ virtual bool CanSaveFileFormat(FileFormat format) const; /** * Allow children to specify which file formats they can and can't load */ virtual bool CanLoadFileFormat(FileFormat format) const; /** * This method should return a string containing the patterns of files * that this dialog is allowed to read */ StringType GetFilePattern(bool forLoading); /** * This method checks which of the supported file formats matches a filename * entered by the user */ FileFormat DetermineFileFormatFromFileName(bool forLoading, const char *testFileName); /** * A method that controls how we propagate forward and back in the dialog. * Fires an assertion if called from where you can't go back or forward. Inactive * pages are simply skipped. */ void GoForward(Fl_Group *current = NULL); void GoBack(Fl_Group *current = NULL); // This is called to load the image with a possible custom IO for raw images bool DoLoadImage(); // List files in the dicom directory bool ProcessDICOMDirectory(); // Check image validity after the initial load virtual bool CheckImageValidity() { return true; } // Check if the image is valid virtual bool CheckFinalValidity() { return (m_GuidedIO.GetNativeImage()); } // The body of the save method (not templated) virtual bool DisplaySaveWizardImpl(const char *file, const char *type = NULL); TypedSaveCallbackBase *m_SaveCallback; }; #endif // __ImageIOWizardLogic_h_ itksnap/UserInterface/ImageIOWizard/RestrictedImageIOWizardLogic.cxx0000755000076500000240000001333411455626760025147 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RestrictedImageIOWizardLogic.cxx,v $ Language: C++ Date: $Date: 2010/10/14 16:21: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. =========================================================================*/ #include "RestrictedImageIOWizardLogic.h" #include "FL/fl_ask.H" RestrictedImageIOWizardLogic ::RestrictedImageIOWizardLogic() { m_NumberOfComponentsRestriction = 0; m_MainImage = 0; } bool RestrictedImageIOWizardLogic ::DisplayInputWizard(const char *file, const char *type) { // If there is no main image specified, the restriction is ignored if(m_MainImage) { // Get the size and spacing of the main image SizeType requiredSize = m_MainImage->GetBufferedRegion().GetSize(); const double *requiredSpacing = m_MainImage->GetSpacing().GetDataPointer(); // Prepare the header page of the wizard UI this->m_InHeaderPageDimX->value(requiredSize[0]); this->m_InHeaderPageDimY->value(requiredSize[1]); this->m_InHeaderPageDimZ->value(requiredSize[2]); this->m_InHeaderPageSpacingX->value(requiredSpacing[0]); this->m_InHeaderPageSpacingY->value(requiredSpacing[1]); this->m_InHeaderPageSpacingZ->value(requiredSpacing[2]); } // Call the parent's method return Superclass::DisplayInputWizard(file, type); } bool RestrictedImageIOWizardLogic ::CheckImageValidity() { // Get the native image pointer ImageBaseType *native = this->m_GuidedIO.GetNativeImage(); // If the main image is specified, compare size, origin, etc to it if(m_MainImage) { // TODO: Move this code to IRISApplications and call from here and from // command-line loading. SizeType requiredSize = m_MainImage->GetBufferedRegion().GetSize(); SizeType loadedSize = native->GetBufferedRegion().GetSize(); // Check whether or not the image size matches the 'forced' image size if(!(requiredSize == loadedSize)) { // Bark at the user fl_alert( "The size of the image you are attempting to load does not match " "the size of the main image already loaded."); return false; } // Check if there is a discrepancy in the header fields. This will not // preclude the user from loading the image, but it will generate a // warning, hopefully leading users to adopt more flexible file formats bool match_spacing = true, match_origin = true, match_direction = true; for(unsigned int i = 0; i < 3; i++) { if(m_MainImage->GetSpacing()[i] != native->GetSpacing()[i]) match_spacing = false; if(m_MainImage->GetOrigin()[i] != native->GetOrigin()[i]) match_origin = false; for(size_t j = 0; j < 3; j++) { double diff = fabs(m_MainImage->GetDirection()(i,j) - native->GetDirection()(i,j)); if(diff > 1.0e-4) match_direction = false; } } if(!match_spacing || !match_origin || !match_direction) { // Come up with a warning message std::string object, verb; if(!match_spacing && !match_origin && !match_direction) { object = "spacing, origin and orientation"; } else if (!match_spacing && !match_origin) { object = "spacing and origin"; } else if (!match_spacing && !match_direction) { object = "spacing and orientation"; } else if (!match_origin && !match_direction) { object = "origin and orientation";} else if (!match_spacing) { object = "spacing"; } else if (!match_direction) { object = "orientation";} else if (!match_origin) { object = "origin"; } // Create an alert box fl_choice( "There is a mismatch between the header of the image that you are\n" "loading and the header of the main image currently open in SNAP.\n\n" "The images have different %s. \n\n" "SNAP will ignore the header information in the image you are loading.\n", "Ok", NULL, NULL, object.c_str()); // Make the header of the image match that of the main image native->SetOrigin(m_MainImage->GetOrigin()); native->SetSpacing(m_MainImage->GetSpacing()); native->SetDirection(m_MainImage->GetDirection()); } } if(m_NumberOfComponentsRestriction > 0) { if(native->GetNumberOfComponentsPerPixel() != m_NumberOfComponentsRestriction) { fl_alert( "The number of components per pixel (%d) in the image you are \n" "attempting to load does not match the required number (%d).", (int) native->GetNumberOfComponentsPerPixel(), (int) m_NumberOfComponentsRestriction); return false; } } return true; } itksnap/UserInterface/ImageIOWizard/RestrictedImageIOWizardLogic.h0000644000076500000240000000540411231677520024560 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RestrictedImageIOWizardLogic.h,v $ Language: C++ Date: $Date: 2009/07/22 21:06:24 $ Version: $Revision: 1.6 $ 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 __RestrictedImageIOWizardLogic_h_ #define __RestrictedImageIOWizardLogic_h_ #include "ImageIOWizardLogic.h" #include "ImageWrapper.h" /** * \class RestrictedImageIOWizardLogic.h * \brief A wizard for loading and saving images whose size and spacing are * restricted by another (in our case, greyscale) image */ class RestrictedImageIOWizardLogic : public ImageIOWizardLogic { public: typedef itk::ImageBase<3> ImageBaseType; typedef ImageIOWizardLogic Superclass; typedef itk::Size<3> SizeType; /** Constructor */ RestrictedImageIOWizardLogic(); /** Some extra work is done before displaying the wizard in this method */ virtual bool DisplayInputWizard(const char *file, const char *type = NULL); /** Set the main image that provides some metadata for loading the * segmentation image */ irisSetMacro(MainImage,ImageBaseType *); /** Set a restriction on the number of components */ irisSetMacro(NumberOfComponentsRestriction, size_t); protected: // Validity of the image is checked by comparing the image size to the // size of the greyscale image virtual bool CheckImageValidity(); // The image orientation is set to match the orientation of the source image virtual void GuessImageOrientation() {}; // Restriction on the number of components size_t m_NumberOfComponentsRestriction; private: /** Main image template */ ImageBaseType *m_MainImage; }; #endif itksnap/UserInterface/MainComponents/0000755000076500000240000000000011560342171017235 5ustar paulystaffitksnap/UserInterface/MainComponents/AppearanceDialogUI.fl0000644000076500000240000005416311553107611023206 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0300 header_name {.h} code_name {.cxx} class AppearanceDialogUI {open : AppearanceDialogUIBase } { Function {MakeWindow()} {open } { Fl_Window m_WinDisplayOptions { label {Display Options} open xywh {410 378 420 415} type Double box PLASTIC_DOWN_BOX align 0 code0 {\#include "AppearanceDialogUIBase.h"} code1 {\#include } non_modal visible } { Fl_Tabs {} {open xywh {10 10 400 360} box PLASTIC_THIN_UP_BOX color 41 labelsize 12 align 0 } { Fl_Group {} { label General open selected xywh {10 35 400 335} labelsize 12 } { Fl_Check_Button m_ChkOptionsSliceThumbnailOn { label {Show thumbnail when the slice does not fit in the window} xywh {20 50 350 25} down_box DOWN_BOX value 1 labelsize 12 } Fl_Group {} {open xywh {30 70 355 55} box PLASTIC_DOWN_BOX } { Fl_Value_Input m_InOptionsSliceThumbnailPercent { label {Thumbnail size, relative to slice (%): } xywh {330 75 50 20} labelsize 11 minimum 5 maximum 100 step 5 value 30 textsize 11 } Fl_Value_Input m_InOptionsSliceThumbnailMaxSize { label {Maximum thumbnail size (pixels): } xywh {330 100 50 20} labelsize 11 minimum 10 maximum 400 step 10 value 160 textsize 11 } } Fl_Check_Button m_ChkOptionsSliceLinkedZoom { label {By default, use the same zoom in all slice views (linked zoom)} xywh {20 160 375 25} down_box DOWN_BOX labelsize 12 } Fl_Check_Button m_ChkOptionsSliceMultisessionZoom { label {By default, synchronize zoom with other ITK-SNAP sessions} xywh {20 180 375 25} down_box DOWN_BOX labelsize 12 } Fl_Check_Button m_ChkOptionsSliceMultisessionPan { label {By default, synchronize panning with other ITK-SNAP sessions} xywh {20 200 375 25} down_box DOWN_BOX labelsize 12 } Fl_Check_Button m_ChkOptionsFloatingPointWarning { label {Issue warning when loading floating point images} xywh {20 220 375 25} down_box DOWN_BOX labelsize 12 } Fl_Check_Button m_ChkOptionsAutoCheckForUpdate { label {Check if newer software version is available at startup} xywh {20 240 375 25} down_box DOWN_BOX labelsize 12 } Fl_Button {} { label {&Apply} callback {OnSliceDisplayApplyAction();} xywh {260 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80061 color 181 labelfont 1 labelsize 12 } Fl_Button {} { label {&Reset} callback {OnSliceDisplayResetAction()} xywh {335 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80072 color 181 labelsize 12 } Fl_Check_Button m_ChkOptionsHiddenFeatures { label {Enable undocumented features (for developers only)} xywh {20 275 375 25} down_box DOWN_BOX labelsize 12 } Fl_Check_Button m_ChkOptionsSliceAutoPan { label {Pan automatically when cursor is moved near window border} xywh {20 140 375 25} down_box DOWN_BOX labelsize 12 } } Fl_Group {} { label Layout open xywh {10 35 400 335} labelsize 12 hide } { Fl_Group {} { label {Available layouts:} open xywh {190 85 205 85} box PLASTIC_DOWN_BOX labelsize 12 align 5 } { Fl_Group {} { xywh {195 90 195 75} } { Fl_Round_Button {m_BtnOptionsViews2D[0]} { callback {this->OnSliceAnatomyOptionsChange(0);} xywh {205 95 50 30} type Radio down_box ROUND_DOWN_BOX value 1 align 24 hotspot } Fl_Round_Button {m_BtnOptionsViews2D[1]} { callback {this->OnSliceAnatomyOptionsChange(1);} xywh {205 130 50 30} type Radio down_box ROUND_DOWN_BOX align 24 } Fl_Round_Button {m_BtnOptionsViews2D[2]} { callback {this->OnSliceAnatomyOptionsChange(2);} xywh {270 95 50 30} type Radio down_box ROUND_DOWN_BOX align 24 } Fl_Round_Button {m_BtnOptionsViews2D[3]} { callback {this->OnSliceAnatomyOptionsChange(3);} xywh {270 130 50 30} type Radio down_box ROUND_DOWN_BOX align 24 } Fl_Round_Button {m_BtnOptionsViews2D[4]} { callback {this->OnSliceAnatomyOptionsChange(4);} xywh {335 95 50 30} type Radio down_box ROUND_DOWN_BOX align 24 } Fl_Round_Button {m_BtnOptionsViews2D[5]} { callback {this->OnSliceAnatomyOptionsChange(5);} xywh {335 130 50 30} type Radio down_box ROUND_DOWN_BOX align 24 } } Fl_Group {} { label A open xywh {225 95 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label C open xywh {240 110 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label S open xywh {240 95 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label { } open xywh {225 110 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label A open xywh {225 130 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label C open xywh {240 130 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label S open xywh {240 145 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label { } open xywh {225 145 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label S open xywh {290 95 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label A open xywh {305 95 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label C open xywh {305 110 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label { } open xywh {290 110 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label S open xywh {290 130 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label C open xywh {305 130 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label A open xywh {305 145 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label { } open xywh {290 145 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label C open xywh {355 95 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label A open xywh {370 95 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label S open xywh {370 110 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label { } open xywh {355 110 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label C open xywh {355 130 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label S open xywh {370 130 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label A open xywh {370 145 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} Fl_Group {} { label { } open xywh {355 145 15 15} box BORDER_BOX color 30 labelfont 1 labelsize 12 labelcolor 7 align 16 } {} } Fl_Group {} { label {Additional options:} open xywh {190 200 205 73} box PLASTIC_DOWN_BOX labelsize 12 align 5 } { Fl_Check_Button m_ChkOptionsViews2DNoseLeft { label {Display anterior to the left} tooltip {Select whether patient's anterior is displayed to the left or to the right in the sagittal view.} xywh {200 208 175 18} down_box DOWN_BOX value 1 labelsize 12 } Fl_Check_Button m_ChkOptionsViews2DRightIsLeft { label {Patient right is screen left} tooltip {Use this checkbox to switch between radiological and neurological conventions.} xywh {200 228 175 18} down_box DOWN_BOX value 1 labelsize 12 } Fl_Check_Button m_ChkOptionsViews2DLinearInterpolation { label {Use linear interpolation} tooltip {By default, SNAP uses nearest neighbor interpolation to render the grey image. This makes voxel edges visible. Select this to use linear interpolation, which blurs voxel edges.} xywh {200 247 175 18} down_box DOWN_BOX labelsize 12 } } Fl_Group {} { label {Current screen layout:} xywh {20 85 155 168} box PLASTIC_DOWN_BOX labelsize 12 align 5 } { Fl_Output {m_OutDisplayOptionsPanel[0]} { label {2D View 1} xywh {30 97 60 60} box BORDER_BOX color 30 selection_color 0 labelsize 11 align 2 textsize 12 textcolor 7 } Fl_Output {m_OutDisplayOptionsPanel[1]} { label {2D View 2} xywh {105 97 60 60} box BORDER_BOX color 30 selection_color 0 labelsize 11 align 2 textsize 12 textcolor 7 } Fl_Output {m_OutDisplayOptionsPanel[2]} { label {2D View 3} xywh {105 177 60 60} box BORDER_BOX color 30 selection_color 0 labelsize 11 align 2 textsize 12 textcolor 7 } Fl_Output {} { label {3D View} xywh {30 177 60 60} box BORDER_BOX color 30 selection_color 0 labelsize 11 align 2 textsize 12 textcolor 7 } } Fl_Group {} { label {2D View Layout:} xywh {20 60 375 5} box ENGRAVED_FRAME labeltype EMBOSSED_LABEL align 5 hide } {} Fl_Button {} { label {&Apply} callback {OnScreenLayoutApplyAction();} xywh {260 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80061 color 181 labelfont 1 labelsize 12 } Fl_Button {} { label {&Reset} callback {OnScreenLayoutResetAction();} xywh {335 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80072 color 181 labelsize 12 } } Fl_Group {} { label Appearance open xywh {10 35 400 335} labelsize 12 hide } { Fl_Choice m_InUIElement { label {Select a user interface element to edit:} callback {OnUIElementSelection(o->value())} open xywh {25 75 370 25} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } { MenuItem {} { label {Anatomical coordinate markers} xywh {20 20 100 20} labelsize 12 } MenuItem {} { label Crosshairs xywh {0 0 100 20} labelsize 12 } MenuItem {} { label {Region of interest} xywh {10 10 100 20} labelsize 12 } MenuItem {} { label {Paintbrush Outline} xywh {10 10 100 20} labelsize 12 } MenuItem {} { label Rulers xywh {20 20 100 20} labelsize 12 } MenuItem {} { label {Window background} xywh {50 50 100 20} labelsize 12 divider } MenuItem {} { label {3D window background} xywh {50 50 100 20} labelsize 12 } MenuItem {} { label {3D window crosshairs} xywh {20 20 100 20} labelsize 12 } MenuItem {} { label {3D window image cube} xywh {30 30 100 20} labelsize 12 } MenuItem {} { label {3D window region of interest} xywh {40 40 100 20} labelsize 12 divider } MenuItem {} { label {Zoom thumbnail} xywh {60 60 100 20} labelsize 12 } MenuItem {} { label {Zoom thumbnail crosshairs} xywh {70 70 100 20} labelsize 12 divider } MenuItem {} { label {Polygon outline (drawing mode)} xywh {80 80 100 20} labelsize 12 } MenuItem {} { label {Polygon outline (edit mode)} xywh {100 100 100 20} labelsize 12 } MenuItem {} { label {Polygon completion line} xywh {90 90 100 20} labelsize 12 } } Fl_Group {} {open xywh {25 110 370 215} box PLASTIC_DOWN_BOX } { Fl_Box m_InColorNormal { label {Color in normal state:} callback {OnUIElementUpdate()} xywh {35 135 170 90} box BORDER_BOX labelsize 12 align 5 class Fl_Color_Chooser } Fl_Box m_InColorActive { label {Color in active state:} callback {OnUIElementUpdate()} xywh {215 135 170 90} box BORDER_BOX labelsize 12 align 5 deactivate class Fl_Color_Chooser } Fl_Value_Input m_InLineThickness { label {Line thickness:} callback {OnUIElementUpdate()} xywh {155 235 45 25} labelsize 12 textsize 12 deactivate } Fl_Value_Input m_InFontSize { label {Font size:} callback {OnUIElementUpdate()} xywh {155 295 45 25} labelsize 12 textsize 12 deactivate } Fl_Value_Input m_InDashSpacing { label {Dash spacing} callback {OnUIElementUpdate()} xywh {155 265 45 25} labelsize 12 textsize 12 deactivate } Fl_Check_Button m_InVisible { label Visible callback {OnUIElementUpdate()} xywh {215 235 60 20} down_box DOWN_BOX labelsize 12 deactivate } Fl_Check_Button m_InAlphaBlending { label {Alpha blending} callback {OnUIElementUpdate()} xywh {275 235 105 20} down_box DOWN_BOX labelsize 12 deactivate } } Fl_Button {} { label {&Apply} callback {OnElementAppearanceApplyAction();} xywh {180 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80061 color 181 labelfont 1 labelsize 12 } Fl_Button {} { label {&Reset} callback {OnElementAppearanceResetAction();} tooltip {Restore the default settings for the current user interface element} xywh {255 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80072 color 181 labelsize 12 } Fl_Button {} { label {Reset All} callback {OnElementAppearanceResetAllAction();} tooltip {Restore the default settings for all user interface elements} xywh {330 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80072 color 181 labelsize 12 } Fl_Check_Button m_ChkOptionsHideOverlays { label {Hide all overlays} callback {OnHideOverlaysAction()} xywh {20 335 125 25} down_box DOWN_BOX labelsize 12 } } Fl_Group {} { label {3D Rendering} open xywh {10 35 400 335} labelsize 12 hide } { Fl_Group {} { label {3D Rendering Options:} xywh {20 60 375 5} box ENGRAVED_FRAME labeltype EMBOSSED_LABEL align 5 hide } {} Fl_Check_Button m_InUseGaussianSmoothing { label {Gaussian Smoothing (slow, increases display quality)} xywh {20 50 355 20} down_box DOWN_BOX value 1 color 0 selection_color 136 labelsize 12 } Fl_Group {} {open xywh {30 70 355 30} box PLASTIC_DOWN_BOX } { Fl_Value_Input m_InRenderOptionsGaussianError { label {Max. approx. error:} xywh {330 75 50 20} labelsize 11 value 0.05 textsize 11 } Fl_Value_Input m_InRenderOptionsGaussianStandardDeviation { label {Standard deviation (mm):} xywh {165 75 50 20} labelsize 11 textsize 11 } } Fl_Check_Button m_InUseDecimate { label {Decimate 3D mesh (reduces number of polygons)} xywh {20 110 365 20} down_box DOWN_BOX value 1 selection_color 136 labelsize 12 } Fl_Group {} {open xywh {30 130 355 85} box PLASTIC_DOWN_BOX } { Fl_Value_Input m_InRenderOptionsDecimateReductions { label {Target reductions:} xywh {165 135 50 20} labelsize 11 textsize 11 } Fl_Value_Input m_InRenderOptionsDecimateErrorIncrement { label {Error increment:} xywh {330 135 50 20} labelsize 11 textsize 11 } Fl_Value_Input m_InRenderOptionsDecimateInitialError { label {Initial error:} xywh {165 155 50 20} labelsize 11 textsize 11 } Fl_Value_Input m_InRenderOptionsDecimateAspectRatio { label {Aspect ratio:} xywh {330 155 50 20} labelsize 11 textsize 11 } Fl_Value_Input m_InRenderOptionsDecimateIterations { label {Maximum iterations:} xywh {165 175 50 20} labelsize 11 minimum 1 maximum 10 step 1 value 1 textsize 11 } Fl_Value_Input m_InRenderOptionsDecimateFeatureAngle { label {Feature angle:} xywh {330 175 50 20} labelsize 11 textsize 11 } Fl_Check_Button m_InRenderOptionsDecimateTopology { label {Preserve Topology} xywh {250 195 135 20} down_box DOWN_BOX labelsize 11 } } Fl_Check_Button m_InUseMeshSmoothing { label {Smoothen 3D mesh (slower, increases mesh quality)} xywh {20 225 355 20} down_box DOWN_BOX value 1 selection_color 136 labelsize 12 } Fl_Group {} {open xywh {30 245 355 70} box PLASTIC_DOWN_BOX } { Fl_Value_Input m_InRenderOptionsMeshSmoothConvergence { label {Convergence:} xywh {165 250 50 20} labelsize 11 textsize 11 } Fl_Value_Input m_InRenderOptionsMeshSmoothFeatureAngle { label {Feature angle:} xywh {330 250 50 20} labelsize 11 textsize 11 } Fl_Value_Input m_InRenderOptionsMeshSmoothRelaxation { label {Relaxation:} xywh {330 270 50 20} labelsize 11 textsize 11 } Fl_Check_Button m_InRenderOptionsMeshSmoothFeatureEdge { label {Feature Edge Smoothing} xywh {105 295 140 20} down_box DOWN_BOX labelsize 11 } Fl_Check_Button m_InRenderOptionsMeshSmoothBoundarySmoothing { label {Boundary Smoothing} xywh {250 295 135 20} down_box DOWN_BOX labelsize 11 } Fl_Value_Input m_InRenderOptionsMeshSmoothIterations { label {Iterations:} xywh {165 270 50 20} labelsize 11 textsize 11 } } Fl_Button {} { label {&Apply} callback {On3DRenderingApplyAction();} xywh {260 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80061 color 181 labelfont 1 labelsize 12 } Fl_Button {} { label {&Reset} callback {On3DRenderingResetAction();} xywh {335 335 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX shortcut 0x80072 color 181 labelsize 12 } } } Fl_Button {} { label Close callback {this->OnCloseAction();} xywh {105 380 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label {Export...} callback {this->OnExportAction();} tooltip {Save display options to a file, allowing you to load it later or to send it to another ITK-SNAP user.} xywh {180 380 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label {Import...} callback {this->OnImportAction();} tooltip {Load ITK-SNAP display options from a file.} xywh {255 380 65 25} box PLASTIC_UP_BOX down_box PLASTIC_UP_BOX color 180 labelsize 12 } } } } itksnap/UserInterface/MainComponents/AppearanceDialogUIBase.h0000644000076500000240000000446511453766570023645 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: AppearanceDialogUIBase.h,v $ Language: C++ Date: $Date: 2010/10/09 04:20:08 $ 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 __AppearanceDialogUIBase_h_ #define __AppearanceDialogUIBase_h_ class AppearanceDialogUIBase { public: virtual ~AppearanceDialogUIBase() {} virtual void OnUIElementUpdate() = 0; virtual void OnUIElementSelection(int value) = 0; virtual void OnSliceAnatomyOptionsChange(unsigned int order) = 0; virtual void OnCloseAction() = 0; virtual void OnExportAction() = 0; virtual void OnImportAction() = 0; virtual void OnSliceDisplayApplyAction() = 0; virtual void OnSliceDisplayResetAction() = 0; virtual void OnScreenLayoutApplyAction() = 0; virtual void OnScreenLayoutResetAction() = 0; virtual void On3DRenderingApplyAction() = 0; virtual void On3DRenderingResetAction() = 0; virtual void OnElementAppearanceResetAllAction() = 0; virtual void OnElementAppearanceResetAction() = 0; virtual void OnElementAppearanceApplyAction() = 0; virtual void OnOptionsExternalUpdate() = 0; virtual void OnHideOverlaysAction() = 0; }; #endif itksnap/UserInterface/MainComponents/AppearanceDialogUILogic.cxx0000644000076500000240000005067511457377610024404 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: AppearanceDialogUILogic.cxx,v $ Language: C++ Date: $Date: 2010/10/19 20:28:56 $ Version: $Revision: 1.14 $ 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 "AppearanceDialogUILogic.h" #include "SNAPAppearanceSettings.h" #include "UserInterfaceBase.h" #include "GlobalState.h" #include "IRISApplication.h" #include "SystemInterface.h" #include "FL/Fl_Native_File_Chooser.H" #include "FL/fl_ask.H" #include using namespace std; const int AppearanceDialogUILogic ::m_MapMenuToElementIndex[] = { SNAPAppearanceSettings::MARKERS, SNAPAppearanceSettings::CROSSHAIRS, SNAPAppearanceSettings::ROI_BOX, SNAPAppearanceSettings::PAINTBRUSH_OUTLINE, SNAPAppearanceSettings::RULER, SNAPAppearanceSettings::BACKGROUND_2D, SNAPAppearanceSettings::BACKGROUND_3D, SNAPAppearanceSettings::CROSSHAIRS_3D, SNAPAppearanceSettings::IMAGE_BOX_3D, SNAPAppearanceSettings::ROI_BOX_3D, SNAPAppearanceSettings::ZOOM_THUMBNAIL, SNAPAppearanceSettings::CROSSHAIRS_THUMB, SNAPAppearanceSettings::POLY_DRAW_MAIN, SNAPAppearanceSettings::POLY_EDIT, SNAPAppearanceSettings::POLY_DRAW_CLOSE }; AppearanceDialogUILogic ::AppearanceDialogUILogic() { m_DefaultAppearance = new SNAPAppearanceSettings(); } AppearanceDialogUILogic ::~AppearanceDialogUILogic() { delete m_DefaultAppearance; } void AppearanceDialogUILogic ::Register(UserInterfaceBase *parent) { m_Parent = parent; m_Appearance = parent->GetAppearanceSettings(); m_GlobalState = parent->GetDriver()->GetGlobalState(); } // Close callbacks void AppearanceDialogUILogic ::OnCloseAction() { m_WinDisplayOptions->hide(); } void AppearanceDialogUILogic ::OnExportAction() { // Prompt for the filename Fl_Native_File_Chooser chooser; chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); chooser.title("Export Display Options to .txt file"); chooser.filter("Text Files\t*.txt"); // chooser.directory(...) chooser.preset_file("itksnap_dispopts.txt"); const char *fChosen = NULL; if (chooser.show() == 0) fChosen = chooser.filename(); if(fChosen) { Registry reg; m_Appearance->SaveToRegistry(reg.Folder("UserInterface.AppearanceSettings")); reg.WriteToFile(fChosen); } } void AppearanceDialogUILogic ::OnImportAction() { // Prompt for the filename Fl_Native_File_Chooser chooser; chooser.type(Fl_Native_File_Chooser::BROWSE_FILE); chooser.title("Import Display Options from .txt file"); chooser.filter("Text Files\t*.txt"); // chooser.directory(...) chooser.preset_file("itksnap_dispopts.txt"); const char *fChosen = NULL; if (chooser.show() == 0) fChosen = chooser.filename(); if(fChosen) { try { Registry reg(fChosen); m_Appearance->LoadFromRegistry(reg.Folder("UserInterface.AppearanceSettings")); // Redraw the current control OnUIElementSelection(m_InUIElement->value()); // Redraw the windows m_Parent->RedrawWindows(); // Place the options in the registry m_Appearance->SaveToRegistry( m_Parent->GetSystemInterface()->Folder("UserInterface.AppearanceSettings")); } catch(Registry::IOException &exc) { fl_alert("Unable to open settings file: %s", exc.c_str()); } catch(Registry::SyntaxException &exc) { fl_alert("Unable to parse settings: %s", exc.c_str()); } } } // General slice options void AppearanceDialogUILogic ::OnSliceDisplayApplyAction() { // Store the appearance options m_Appearance->SetFlagDisplayZoomThumbnail( m_ChkOptionsSliceThumbnailOn->value() != 0); m_Appearance->SetFlagAutoPan( m_ChkOptionsSliceAutoPan->value() != 0); m_Appearance->SetFlagLinkedZoomByDefault( m_ChkOptionsSliceLinkedZoom->value() != 0); m_Appearance->SetFlagMultisessionZoomByDefault( m_ChkOptionsSliceMultisessionZoom->value() != 0); m_Appearance->SetFlagMultisessionPanByDefault( m_ChkOptionsSliceMultisessionPan->value() != 0); m_Appearance->SetFlagFloatingPointWarningByDefault( m_ChkOptionsFloatingPointWarning->value() != 0); m_Appearance->SetFlagEnableAutoCheckForUpdateByDefault( m_ChkOptionsAutoCheckForUpdate->value() != 0); m_Appearance->SetFlagEnableHiddenFeaturesByDefault( m_ChkOptionsHiddenFeatures->value() != 0); m_Appearance->SetZoomThumbnailSizeInPercent( m_InOptionsSliceThumbnailPercent->value()); m_Appearance->SetZoomThumbnailMaximumSize( (int) m_InOptionsSliceThumbnailMaxSize->value()); // Place the options in the registry m_Appearance->SaveToRegistry( m_Parent->GetSystemInterface()->Folder("UserInterface.AppearanceSettings")); // Handle the hidden features button m_Parent->OnHiddenFeaturesToggleAction(); // Redraw the UI windows m_Parent->RedrawWindows(); } void AppearanceDialogUILogic ::OnSliceDisplayResetAction() { // Restore the appearance options m_Appearance->SetFlagDisplayZoomThumbnail( m_DefaultAppearance->GetFlagDisplayZoomThumbnail()); m_Appearance->SetFlagLinkedZoomByDefault( m_DefaultAppearance->GetFlagLinkedZoomByDefault()); m_Appearance->SetFlagMultisessionZoomByDefault( m_DefaultAppearance->GetFlagMultisessionZoomByDefault()); m_Appearance->SetFlagMultisessionPanByDefault( m_DefaultAppearance->GetFlagMultisessionPanByDefault()); m_Appearance->SetFlagFloatingPointWarningByDefault( m_DefaultAppearance->GetFlagFloatingPointWarningByDefault()); m_Appearance->SetFlagEnableHiddenFeaturesByDefault( m_DefaultAppearance->GetFlagEnableHiddenFeaturesByDefault()); m_Appearance->SetFlagAutoPan( m_DefaultAppearance->GetFlagAutoPan()); m_Appearance->SetZoomThumbnailSizeInPercent( m_DefaultAppearance->GetZoomThumbnailSizeInPercent()); m_Appearance->SetZoomThumbnailMaximumSize( m_DefaultAppearance->GetZoomThumbnailMaximumSize()); // Place the options in the registry m_Appearance->SaveToRegistry( m_Parent->GetSystemInterface()->Folder("UserInterface.AppearanceSettings")); // Redraw the UI windows m_Parent->RedrawWindows(); // Refill the options FillAppearanceSettings(); } // View arrangement callbacks void AppearanceDialogUILogic ::OnScreenLayoutApplyAction() { // Search for the selected orientation toggle unsigned int i; for(i=0;i<6;i++) if(m_BtnOptionsViews2D[i]->value()) break; // Make sure something is selected if(i == 6) return; // Update the selected layout m_Appearance->SetSliceLayout( (SNAPAppearanceSettings::UISliceLayout) i); m_Appearance->SetFlagLayoutPatientAnteriorShownLeft( m_ChkOptionsViews2DNoseLeft->value() == 0 ? false : true); m_Appearance->SetFlagLayoutPatientRightShownLeft( m_ChkOptionsViews2DRightIsLeft->value() == 0 ? false : true); // Finally handle the linear interpolation option m_Appearance->SetGreyInterpolationMode( m_ChkOptionsViews2DLinearInterpolation->value() ? SNAPAppearanceSettings::LINEAR : SNAPAppearanceSettings::NEAREST); // Place the options in the registry m_Appearance->SaveToRegistry( m_Parent->GetSystemInterface()->Folder("UserInterface.AppearanceSettings")); // Get the RAI codes string rai[3]; m_Appearance->GetAnatomyToDisplayTransforms(rai[0], rai[1], rai[2]); // Update geometry only if one of these three RAIs is different from the current for(size_t j = 0; j < 3; j++) if(m_Parent->GetDriver()->GetDisplayToAnatomyRAI(j) != rai[j]) { m_Parent->GetDriver()->SetDisplayToAnatomyRAI( rai[0].c_str(), rai[1].c_str(), rai[2].c_str()); m_Parent->OnImageGeometryUpdate(); break; } // Update the user interface m_Parent->RedrawWindows(); } void AppearanceDialogUILogic ::OnScreenLayoutResetAction() { for(size_t i=0; i < 6; i++) m_BtnOptionsViews2D[i]->value(i==0 ? 1 : 0); m_ChkOptionsViews2DNoseLeft->value(1); m_ChkOptionsViews2DRightIsLeft->value(1); m_ChkOptionsViews2DLinearInterpolation->value(0); OnSliceAnatomyOptionsChange(0); OnScreenLayoutApplyAction(); } void AppearanceDialogUILogic ::OnSliceAnatomyOptionsChange(unsigned int order) { // Depending on the order, put appropriate text const char *axial = "Axial", *coronal = "Coronal", *sagittal = "Sagittal"; const char *v1=NULL, *v2=NULL, *v3=NULL; switch(order) { case SNAPAppearanceSettings::LAYOUT_ACS: v1 = axial; v2 = coronal; v3 = sagittal; break; case SNAPAppearanceSettings::LAYOUT_ASC: v1 = axial; v2 = sagittal; v3 = coronal; break; case SNAPAppearanceSettings::LAYOUT_CAS: v1 = coronal; v2 = axial; v3 = sagittal; break; case SNAPAppearanceSettings::LAYOUT_CSA: v1 = coronal; v2 = sagittal; v3 = axial; break; case SNAPAppearanceSettings::LAYOUT_SAC: v1 = sagittal; v2 = axial; v3 = coronal; break; case SNAPAppearanceSettings::LAYOUT_SCA: v1 = sagittal; v2 = coronal; v3 = axial; break; }; // Place the text in the boxes m_OutDisplayOptionsPanel[0]->value(v1); m_OutDisplayOptionsPanel[1]->value(v2); m_OutDisplayOptionsPanel[2]->value(v3); } // 3D Rendering callbacks void AppearanceDialogUILogic ::On3DRenderingApplyAction() { // Get the current mesh options MeshOptions mops; // Set the Gaussian properties mops.SetGaussianStandardDeviation( m_InRenderOptionsGaussianStandardDeviation->value()); mops.SetUseGaussianSmoothing( m_InUseGaussianSmoothing->value()); mops.SetGaussianError( m_InRenderOptionsGaussianError->value()); // Triangle Decimation mops.SetUseDecimation( m_InUseDecimate->value()); mops.SetDecimateAspectRatio( m_InRenderOptionsDecimateAspectRatio->value()); mops.SetDecimateErrorIncrement( m_InRenderOptionsDecimateErrorIncrement->value()); mops.SetDecimateFeatureAngle( m_InRenderOptionsDecimateFeatureAngle->value()); mops.SetDecimateInitialError( m_InRenderOptionsDecimateInitialError->value()); mops.SetDecimateMaximumIterations( (unsigned int)m_InRenderOptionsDecimateIterations->value()); mops.SetDecimatePreserveTopology( m_InRenderOptionsDecimateTopology->value()); mops.SetDecimateTargetReduction( m_InRenderOptionsDecimateReductions->value()); // Mesh Smoothing mops.SetUseMeshSmoothing( m_InUseMeshSmoothing->value()); mops.SetMeshSmoothingBoundarySmoothing( m_InRenderOptionsMeshSmoothBoundarySmoothing->value()); mops.SetMeshSmoothingConvergence( m_InRenderOptionsMeshSmoothConvergence->value()); mops.SetMeshSmoothingFeatureAngle( m_InRenderOptionsMeshSmoothFeatureAngle->value()); mops.SetMeshSmoothingFeatureEdgeSmoothing( m_InRenderOptionsMeshSmoothFeatureEdge->value()); mops.SetMeshSmoothingIterations( (unsigned int)m_InRenderOptionsMeshSmoothIterations->value()); mops.SetMeshSmoothingRelaxationFactor( m_InRenderOptionsMeshSmoothRelaxation->value()); // Save the mesh options m_GlobalState->SetMeshOptions(mops); // Enable the update button on the 3D window m_Parent->OnIRISMeshDisplaySettingsUpdate(); } void AppearanceDialogUILogic ::On3DRenderingResetAction() { // Reset the options in the system to defaults MeshOptions mops; m_GlobalState->SetMeshOptions(mops); // Refill the options FillRenderingOptions(); } // Element appearance callbacks void AppearanceDialogUILogic ::OnUIElementUpdate() { // Nothing for now } void AppearanceDialogUILogic ::OnUIElementSelection(int value) { // Must have a value! if(value < 0) return; // Find out the which element was selected int iElement = m_MapMenuToElementIndex[value]; // Make sure a legit element was found if(iElement == SNAPAppearanceSettings::ELEMENT_COUNT) return; // Set the user interface properties SNAPAppearanceSettings::Element &e = m_Appearance->GetUIElement(iElement); m_InColorNormal->rgb( e.NormalColor[0], e.NormalColor[1], e.NormalColor[2] ); m_InColorActive->rgb( e.ActiveColor[0], e.ActiveColor[1], e.ActiveColor[2] ); m_InLineThickness->value( e.LineThickness ); m_InDashSpacing->value( e.DashSpacing ); m_InFontSize->value( e.FontSize ); m_InVisible->value( e.Visible ); m_InAlphaBlending->value( e.AlphaBlending ); // Create an array of widgets for cleaner code Fl_Widget *w[] = { m_InColorNormal, m_InColorActive, m_InLineThickness, m_InDashSpacing, m_InFontSize, m_InVisible, m_InAlphaBlending }; // Set the active/inactive status for(unsigned int iWidget = 0; iWidget < SNAPAppearanceSettings::FEATURE_COUNT; iWidget++) { if(m_Appearance->IsFeatureApplicable(iElement, iWidget)) { w[iWidget]->activate(); if(iWidget < 2) w[iWidget]->show(); } else { w[iWidget]->deactivate(); if(iWidget < 2) w[iWidget]->hide(); } } } void AppearanceDialogUILogic ::OnElementAppearanceResetAllAction() { // Reset each element for(unsigned int i = 0; i < SNAPAppearanceSettings::ELEMENT_COUNT; i++) m_Appearance->SetUIElement(i,m_DefaultAppearance->GetUIElement(i)); // Redraw the current control OnUIElementSelection(m_InUIElement->value()); // Redraw the windows m_Parent->RedrawWindows(); // Place the options in the registry m_Appearance->SaveToRegistry( m_Parent->GetSystemInterface()->Folder("UserInterface.AppearanceSettings")); } void AppearanceDialogUILogic ::OnElementAppearanceResetAction() { // Must have a value! if(m_InUIElement->value() < 0) return; // Find out the which element was selected int iElement = m_MapMenuToElementIndex[m_InUIElement->value()]; // Make sure a legit element was found if(iElement == SNAPAppearanceSettings::ELEMENT_COUNT) return; // Reset the element m_Appearance->SetUIElement( iElement,m_DefaultAppearance->GetUIElement(iElement)); // Redraw the controls OnUIElementSelection(m_InUIElement->value()); // Redraw the windows m_Parent->RedrawWindows(); // Place the options in the registry m_Appearance->SaveToRegistry( m_Parent->GetSystemInterface()->Folder("UserInterface.AppearanceSettings")); } void AppearanceDialogUILogic ::OnElementAppearanceApplyAction() { // Must have a value! if(m_InUIElement->value() < 0) return; // Find out the which element was selected int iElement = m_MapMenuToElementIndex[m_InUIElement->value()]; // Make sure a legit element was found if(iElement == SNAPAppearanceSettings::ELEMENT_COUNT) return; // Set the element's properties SNAPAppearanceSettings::Element &e = m_Appearance->GetUIElement(iElement); e.NormalColor[0] = (float) m_InColorNormal->r(); e.NormalColor[1] = (float) m_InColorNormal->g(); e.NormalColor[2] = (float) m_InColorNormal->b(); e.ActiveColor[0] = (float) m_InColorActive->r(); e.ActiveColor[1] = (float) m_InColorActive->g(); e.ActiveColor[2] = (float) m_InColorActive->b(); e.LineThickness = (float) m_InLineThickness->value(); e.DashSpacing = (float) m_InDashSpacing->value(); e.FontSize = (int) m_InFontSize->value(); e.AlphaBlending = m_InAlphaBlending->value() != 0; e.Visible = m_InVisible->value() != 0; // Redraw the windows m_Parent->RedrawWindows(); // Place the options in the registry m_Appearance->SaveToRegistry( m_Parent->GetSystemInterface()->Folder("UserInterface.AppearanceSettings")); } void AppearanceDialogUILogic ::ShowDialog() { // Reset to options OnOptionsExternalUpdate(); // Show the dialog m_WinDisplayOptions->show(); } void AppearanceDialogUILogic ::OnOptionsExternalUpdate() { // Fill out the panels of the dialog FillAppearanceSettings(); FillSliceLayoutOptions(); FillRenderingOptions(); // Describe the currently selected UI element OnUIElementSelection( m_InUIElement->value() ); } void AppearanceDialogUILogic ::FillRenderingOptions() { // Get the current mesh options MeshOptions mops = m_GlobalState->GetMeshOptions(); // Set the Gaussian properties m_InRenderOptionsGaussianStandardDeviation->value( mops.GetGaussianStandardDeviation()); m_InUseGaussianSmoothing->value( mops.GetUseGaussianSmoothing()); m_InRenderOptionsGaussianError->value( mops.GetGaussianError()); // Triangle Decimation m_InUseDecimate->value( mops.GetUseDecimation()); m_InRenderOptionsDecimateAspectRatio->value( mops.GetDecimateAspectRatio()); m_InRenderOptionsDecimateErrorIncrement->value( mops.GetDecimateErrorIncrement()); m_InRenderOptionsDecimateFeatureAngle->value( mops.GetDecimateFeatureAngle()); m_InRenderOptionsDecimateInitialError->value( mops.GetDecimateInitialError()); m_InRenderOptionsDecimateIterations->value( (double)mops.GetDecimateMaximumIterations()); m_InRenderOptionsDecimateTopology->value( mops.GetDecimatePreserveTopology()); m_InRenderOptionsDecimateReductions->value( mops.GetDecimateTargetReduction()); // Mesh Smoothing m_InUseMeshSmoothing->value( mops.GetUseMeshSmoothing()); m_InRenderOptionsMeshSmoothBoundarySmoothing->value( mops.GetMeshSmoothingBoundarySmoothing()); m_InRenderOptionsMeshSmoothConvergence->value( mops.GetMeshSmoothingConvergence()); m_InRenderOptionsMeshSmoothFeatureAngle->value( mops.GetMeshSmoothingFeatureAngle()); m_InRenderOptionsMeshSmoothFeatureEdge->value( mops.GetMeshSmoothingFeatureEdgeSmoothing()); m_InRenderOptionsMeshSmoothIterations->value( (double)mops.GetMeshSmoothingIterations()); m_InRenderOptionsMeshSmoothRelaxation->value( mops.GetMeshSmoothingRelaxationFactor()); } void AppearanceDialogUILogic ::FillSliceLayoutOptions() { // Select the appropriate display layout size_t layout_index = (size_t ) m_Appearance->GetSliceLayout(); for(size_t i = 0; i < 6; i++) m_BtnOptionsViews2D[i]->value(i == layout_index ? 1 : 0); // Set the checkbox values accordingly m_ChkOptionsViews2DNoseLeft->value( m_Appearance->GetFlagLayoutPatientAnteriorShownLeft() ? 1 : 0); m_ChkOptionsViews2DRightIsLeft->value( m_Appearance->GetFlagLayoutPatientRightShownLeft() ? 1 : 0); m_ChkOptionsViews2DLinearInterpolation->value( m_Appearance->GetGreyInterpolationMode() == SNAPAppearanceSettings::LINEAR ? 1 : 0); // Update the display as if the user changed it OnSliceAnatomyOptionsChange(layout_index); } void AppearanceDialogUILogic ::FillAppearanceSettings() { // Propagate the settings to the controls m_ChkOptionsSliceThumbnailOn->value( m_Appearance->GetFlagDisplayZoomThumbnail() ? 1 : 0); m_ChkOptionsSliceAutoPan->value( m_Appearance->GetFlagAutoPan() ? 1 : 0); m_ChkOptionsSliceLinkedZoom->value( m_Appearance->GetFlagLinkedZoomByDefault() ? 1 : 0); m_ChkOptionsSliceMultisessionZoom->value( m_Appearance->GetFlagMultisessionZoomByDefault() ? 1 : 0); m_ChkOptionsSliceMultisessionPan->value( m_Appearance->GetFlagMultisessionPanByDefault() ? 1 : 0); m_ChkOptionsFloatingPointWarning->value( m_Appearance->GetFlagFloatingPointWarningByDefault() ? 1 : 0); m_ChkOptionsAutoCheckForUpdate->value( m_Appearance->GetFlagEnableAutoCheckForUpdateByDefault() ? 1 : 0); m_ChkOptionsHiddenFeatures->value( m_Appearance->GetFlagEnableHiddenFeaturesByDefault() ? 1 : 0); m_InOptionsSliceThumbnailPercent->value( m_Appearance->GetZoomThumbnailSizeInPercent()); m_InOptionsSliceThumbnailMaxSize->value( (double) m_Appearance->GetZoomThumbnailMaximumSize()); // Overall visibility m_ChkOptionsHideOverlays->value( m_Appearance->GetOverallVisibility() ? 0 : 1); } void AppearanceDialogUILogic ::OnHideOverlaysAction() { m_Appearance->SetOverallVisibility( m_ChkOptionsHideOverlays->value() > 0 ? false : true); m_Parent->RedrawWindows(); } itksnap/UserInterface/MainComponents/AppearanceDialogUILogic.h0000644000076500000240000000637011453766570024025 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: AppearanceDialogUILogic.h,v $ Language: C++ Date: $Date: 2010/10/09 04:20:08 $ 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 __AppearanceDialogUILogic_h_ #define __AppearanceDialogUILogic_h_ #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif #include "AppearanceDialogUI.h" class UserInterfaceBase; class SNAPAppearanceSettings; class GlobalState; class AppearanceDialogUILogic : public AppearanceDialogUI { public: AppearanceDialogUILogic(); virtual ~AppearanceDialogUILogic(); void Register( UserInterfaceBase *parent ); /* Show the dialog */ void ShowDialog(); // Close callbacks void OnCloseAction() ; // Import/export void OnImportAction(); void OnExportAction(); // General slice options void OnSliceDisplayApplyAction(); void OnSliceDisplayResetAction(); // View arrangement callbacks void OnScreenLayoutApplyAction(); void OnScreenLayoutResetAction(); void OnSliceAnatomyOptionsChange(unsigned int order) ; // 3D Rendering callbacks void On3DRenderingApplyAction(); void On3DRenderingResetAction(); // Element appearance callbacks void OnUIElementUpdate() ; void OnUIElementSelection(int value) ; void OnElementAppearanceResetAllAction(); void OnElementAppearanceResetAction(); void OnElementAppearanceApplyAction(); void OnOptionsExternalUpdate(); void OnHideOverlaysAction(); private: // Referece to the parent UserInterfaceBase *m_Parent; // Pointer to the appearance settings, from parent. SNAPAppearanceSettings *m_Appearance; // Default appearance settings SNAPAppearanceSettings *m_DefaultAppearance; // global state pointer GlobalState *m_GlobalState; // Fill some global options void FillSliceLayoutOptions(); void FillRenderingOptions(); void FillAppearanceSettings(); // Apply the global options void ApplyRenderingOptions(); void ApplySliceLayoutOptions(); void ApplyAppearanceSettings(); // A mapping from SNAP appearance elements to menu indices const static int m_MapMenuToElementIndex[]; }; #endif itksnap/UserInterface/MainComponents/Artwork/0000755000076500000240000000000011560342171020666 5ustar paulystaffitksnap/UserInterface/MainComponents/Artwork/crosshair.gif0000644000076500000240000000201710534177577023372 0ustar paulystaffGIF89a))!!!!!!)))11)191991BB9BB9JJJRRJRZJZZRccZkkZsscs{ks{ƥƭέֵ֭!,)) H*\ȰÇ#JHE.F̨!9d(r$F$Q\ɲ$K.5Ic͚oA$>@AF( \Ă >L W4JAԐ@ wA"M8N,YA$01 ( "K8p@ <(w e `@]MH.!jȓ+_μ;itksnap/UserInterface/MainComponents/Artwork/crosshair3D.gif0000644000076500000240000000203510534177577023561 0ustar paulystaffGIF89a))!!!!!!)))11)191991BB9BB9JJJRRJRZJZZRccZkkZsscs{ks{ƥƭέֵ֭!,)) H*\ȰÇ#JHE.F̨!9d(r$F$QT$K._ "͛4a @O8o^)`ƏHt@Q (QC&Y,@pƚL, :VMp`0 T5EfH@b` h)rDE$"M8(b&hA%( ( BA AE%hB57q&2d(Ѵسk2 ;itksnap/UserInterface/MainComponents/Artwork/crosshair3Dtiny.gif0000644000076500000240000000167210534177577024473 0ustar paulystaffGIF89a!!!!!)11)191991BB9BB9JJBJJBRRJZZRccZkkcsscs{k{{s{ƥƥέέֵ!, LȰ  4`*p@A A\ !(xh,`84`4( !i j $ г忋NUN`<>10 ;itksnap/UserInterface/MainComponents/Artwork/CVS/0000755000076500000240000000000011560342171021321 5ustar paulystaffitksnap/UserInterface/MainComponents/Artwork/CVS/Entries0000644000076500000240000000223211560342171022654 0ustar paulystaff/crosshair.gif/1.1/Sat Dec 2 04:22:23 2006/-kb/ /crosshair3D.gif/1.1/Sat Dec 2 04:22:23 2006/-kb/ /crosshair3Dtiny.gif/1.1/Sat Dec 2 04:22:23 2006/-kb/ /formula01.gif/1.1/Sat Dec 2 04:22:23 2006/-kb/ /formula02.gif/1.1/Sat Dec 2 04:22:23 2006/-kb/ /formula03.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /itkLogoSmallTransparentBackground.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /logo.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /logo_new.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /outputintensity.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /paintbrush.gif/1.1/Tue Dec 5 20:39:09 2006/-kb/ /poly.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /ra.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /rb.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /rc.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /rotate3d.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /rotate3dTiny.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /scalpel.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /screencapture.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /screencapture2.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /snake.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /spray.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /zoom.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ /zoom3d.gif/1.1/Sat Dec 2 04:22:26 2006/-kb/ D itksnap/UserInterface/MainComponents/Artwork/CVS/Entries.Log0000644000076500000240000000001711560342171023373 0ustar paulystaffA D/source//// itksnap/UserInterface/MainComponents/Artwork/CVS/Repository0000644000076500000240000000005511560342171023423 0ustar paulystaffitksnap/UserInterface/MainComponents/Artwork itksnap/UserInterface/MainComponents/Artwork/CVS/Root0000644000076500000240000000010011560342171022156 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/MainComponents/Artwork/formula01.gif0000644000076500000240000000370610534177577023211 0ustar paulystaffGIF89a 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/UserInterface/MainComponents/Artwork/formula02.gif0000644000076500000240000000257010534177577023210 0ustar paulystaffGIF89a999RRRkkk{{{!,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/UserInterface/MainComponents/Artwork/formula03.gif0000644000076500000240000000431310534177602023173 0ustar paulystaffGIF89aj 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&&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/UserInterface/MainComponents/Artwork/logo.gif0000644000076500000240000002650710534177602022334 0ustar paulystaffGIF89aNeJRP>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/UserInterface/MainComponents/Artwork/logo_new.gif0000644000076500000240000006674110534177602023211 0ustar paulystaffGIF89a?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 >>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~! ,)) H*\ȰÇ#JHŋ3jȱ7aӼi`-UӘ"Yr-:Ŭ@~jνO;itksnap/UserInterface/MainComponents/Artwork/poly.gif0000644000076500000240000000216710534177602022353 0ustar paulystaffGIF89a))!!!!!!!)!)))11)991991BB9JJBJJBRRJRZJZZRccRkkZkkZsscsscs{c{{k{{ksss{ƥƥέέֵ֭!,)) H*\ȰÇ#JHŋ x 訰B,L$!FԈ@ HŊ 8vİ?#@pP $'H("X(aA O#p 8(!Hp@a @<@! xHI%(S4X;itksnap/UserInterface/MainComponents/Artwork/scalpel.gif0000644000076500000240000000212410534177602023004 0ustar paulystaffGIF89a))!!!!!!)))11)191991BB9BJ9JJBJJBRRJZZRccRckZkkZssc{{k{{ks{{{ƥƭέֵ֭!,)) H*\ȰÇ#JHŋ3j,"ƅ+04@ 0x,9p \ `ſ(&\ $KP Ul8`  `r H`Cɲ*G hYQL #nLa!G 4b€ qJ:رMÿ VD`@9(rF F`a@0d9 ;QV6QD/ |H $Z 8_0ADʉ8 + Aa% ".L @4g4("ǂ@ 2R\? ‰gc%tа.*p;gE`A&r8pY{40   p?$tB ] 8(P H HU @RAJܖ  @  X@(.0 '` hB'p}@1|p #@(9 1l5矀*;itksnap/UserInterface/MainComponents/Artwork/source/0000755000076500000240000000000011560342171022166 5ustar paulystaffitksnap/UserInterface/MainComponents/Artwork/source/CVS/0000755000076500000240000000000011560342171022621 5ustar paulystaffitksnap/UserInterface/MainComponents/Artwork/source/CVS/Entries0000644000076500000240000000007211560342171024154 0ustar paulystaff/snaplogo_gimp.xcf.gz/1.1/Sat Dec 2 04:22:26 2006/-kb/ D itksnap/UserInterface/MainComponents/Artwork/source/CVS/Repository0000644000076500000240000000006411560342171024723 0ustar paulystaffitksnap/UserInterface/MainComponents/Artwork/source itksnap/UserInterface/MainComponents/Artwork/source/CVS/Root0000644000076500000240000000010011560342171023456 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/MainComponents/Artwork/source/snaplogo_gimp.xcf.gz0000644000076500000240000026122410534177602026161 0ustar paulystaffT1Csnaplogo3.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

    ]*?7 \II)&22,,:19uOvᓗ/rJ [v団R2HK_1<[6ȫIla ."*#fW\Cal0&pRv6dK~-xHve P7ƥ2Uqɽ)1F&b#ہRF|:#1vw{ƌǹbk4g;]#1ý= Cz9:c=%hz8>Fi&l s5oO<Ԭ)PNwӞ;eQ"c@~? ;xRШ.P >a۸Ν1lc#Gx;+d*؜%>.ޞF&V#}7?A ++Kx98!WdbiCuUovr{`' X^Y'g ԄW{eYQ0}1I6 ܽ2!p+c0r|?]#9$?47< 9x 3}6 +B~43]j't72zOέw^Ο_r[t ۷EO.L[!iyh{5?m޲%l[쌰?A憛QԠh劕?l5"-+~kTž]k~x)E;lEH5&*62b3Cɱ~*vnˡOX[p9?vkDB|M?|^Bb֎~"iBwfGmܹ2h-UKGGO{XӪ99 +sfnږ]1ݾۮ*[9gOeݺm\E kve Ƕ5kQ(ECb R+G.mil>}!g /?m/>,LIܕ_\?uĵ->}uir0;5!:J/}`G Ɣgo]%5=%;;5W֞gB_[.uQ\y/+2ʐuwL0HJ%Y**|y6]bX 'pF<{ۊwCsv*NISfqXx).VT/[_437:Ԟl~bDZ5k~شyVcqkVFlQٙȘSvEc?Q=<.51prGJwGdU$[Ӌ( 7|A#G-DzRس'٬ l.kҼ4ًǔKlګ'rmzB5cn^ۿ;}}řQ?lHr<ρXU*7\8r%)۶Eˊ-y-/tWTN/=)5u7|k6st*WYgC4m -7J ]?܈ufaJˤ476p{wGFl[z|HEU^f|ѩmm͗/ݾzęWdb`%hů:z[ա&;ݢ F6ݖ{}}Wwp`W'_uzSMgvd+WN,qS|r dU0V"IlW_!cofdEYlUI[FF/1qv-K9jy2_J5dq}Az'dFe{m:֮U$%JkF}NV搫qIȥ36^7g{ ֠?D]`1*א=:g46pVF=N-5\NJ5:k*e0HZ|p)==mE&?}L\h=(wyyY3 [i 793rQϔi?g1QD)8zx un .݉3+2-?=F4#(8eHVc3G_O7o{8U%aK>uf6Ii# 1AEޛdK ӺK%⣏]: "6eɎ&ӊ(q$.^PiG8V$ |gvff{!Ѧ,Ix?hfJzJb8Ej"j,G %`G)#Џeufa¨*Î-i^,"## #yg:fb%FIAQkCdbj,-P=ʊCE;ζP~mOQ]E"s'g[S;=U8.:g/60 .tSIF[Ksyڝm 13Q㼝mqt"&q̋n6^SzY^ϤVʉ{wrH$p>םNewF{:9yss>d7~ܰ  24D)"=cb9rqF˔}<@[%8 ֎V"8|H2[FjHp~f[%]2ޅYB䂾< a_٠߆E+cWL}`E:1,Eb# GPyO~E_O5~s7QU(rA1(hE,oks8J48(FLD:%Fpj81v1 搎 fNRDIOo W"NDnI,~enC'Ɓz6v>W~ N SOFp7=1UX x(Ǻ"$  q@cLᯰtcG߾#\%{Ƌ0>ąiGEH{8bR3 >ճ!e- 7Ӷx7Cmԟ+* `dF|_[ψvdDk_eDQ6-g8G.r8qphNel[> Uf8yYQI%6JX'8ӔSR-eJqG];kOG 2 fh.[BA3N^pL KT3w+#y[Vl@9K@OVVZ`sK^m)*r5#EҢOǕC'-$fWɄ"TmPHqq+߹%"|UI"U.-,Œ:#,R5H|z$6(Z[bir vc(b+ ;%ahZi];Rs#+AM{;6LKJ%UE%w`۹y hu5xoIUUaPCV^bVpWJi WYWᵣYmڊj*l>7f{rnHNfV9KNgjh +e<\Iz3.~wS_+Ϻ|.wm ^aۖ$3+e0faZ!R8RԔ/ > ew`\Rntr4y*?N,`rgǍ׏,M;-,Тy~ 1%0/͓}їODK=v'CLh>&zJ`ZDU9?O0pWrQz̔opa?rE cK)(>)3Wp3 <'HL%Fbi|ŸO]ZF񆗗W7"n07R{g<ݷ@pM +g ֠`NC {izH#E ># Oo~Ai?fXhnЁ}8O0ebe^\Տ2(#bJ#gM-28,9euF3YyD“T+&w`>ɣAXy^A1vj!ƊRһpcizx>@˥Og?cipy_YV "N=spy^N1Sg[w~?v,?OY r guBѲ|J 3tA0(P`VaW)UH;r@N4G(iQV#8 WM']?[\Y fLL8t2S21[ 31x2Nm] 1ք3T~4b 0Gpm/\ƈ:2Mi&1 DjI 3$=+-ON0BQz' 0JodU8E΢|}kT|\DsL 즦hQ rokwA#;,XTZZ{[$.yEa Ygu@x k*2*3T\Y n`e&<\;w2z *\ۀm+(BZTXF]~cMQ R"]ՌXSWVR\j1G5- %kB0(Ur u"fo~u*"6*""Ҋz$^Ay݆ a;p՝'/D!˙[Y{Zs+4o]G5M!*l[kK^Xrj͹mu7:,U*K:i;VVPv߬sp↟q6q2AN4Ⱥ(ˋM*@6 i < tKCGQEmFx$+*-/Aσ5wwdW%AO6w˿ixȹo5 /ygm, `uv".e-l,?lځ'yGYv]\W_4HA`9MIh?h!jooΊJL-QL:7%wб%Y`!R#ώYAf(--eAf;tERin>m#zkbf;.\|sLrn_Y!gq*! >xٶU =GpO'g/@' purY4AN=۽`BN|e'O4vz`'dҋy|" o{͜8~t>BMN[V%@9yXdNߢFY9}ށUb-_+4{ڼFlVdTl?I'O8e^}`*{Јmz_ُU餔Kh(?0~S=Q6AijLOތ$7'ɓΚ8>{ƃ<[$Y䛏HؙSBi#ųsB#Gɸ9U+x]6m=bnnf.OmD)Q+6B[8rq9~%J8%X!Q_,K)[UYPӷH6!=rÐ~cϕ!Q/@: gLbVS梩2nhPTnbQO>eˑ]>Ír53/D0}v(v `QfjaqoE i+ z5< (6k@c%3hԪ8_c䡱r4s5e-;Y5mTu9cGK&7%f"Fbn:q\dS3a[mVBoJ2 SJJe xAALxq:qy H nCdktp0QkDȎ ݢi,wpCP@).7;wy4QKpyණ n@T|m:F>,|g7{9Atn=p@^c|T@aHO~ A_w;bЉ6#c,Co5#ď|S9S1B>dT8 }8kpHA?qyL}8`r#\QiSdSnk8mc)1`D<DMaPP QOr번f0׮ln~ʠebހd)yU{m(НTsRi{7E=EI){GzQCPoK˩ڬkm:Q]LRe`'Twz:Է)h鲒lʺW]W opL幦_K9E-Uk4ɉ.Тvg>Ԭ'^LebhU潑Iswsw@N{6CYr|5/VaYuJZM3p.Ʊ *K [~FRnTfEUefoe?Ey0J *+Ze(5X [wEo"$ cZ96#<.?7*S71y3]NMj>5:+5fv7yXUqm_DF1Wwt=n ];E'HHU||ciH۱"B;"/8TKt|L먈]϶\vޜ+Nd7o=q<ZEϊT<";Uaѩ8QWk ʲѮ.-݅Uo0Ad,+d,-څ$&" yx`syez[*/'*\;[~ Z۸~9@?/i릵+B,2"||0 *"W,[~U>DBp˿n}ѝEY޾>`~/> -w+-E1iW/\MLCܝu =%Z[ܨ|ͦ_C_ED=}$[wډ!kGɵ͎xj<,o1ٝ6Z8zc|4۳{-%x{~\y˺y\asT*(ԷvF+yI̵&y^֮peJ ".m/|r{`dJ"?xs'1h+(/3[Z'4'9s(/)*!zKRt9\&aϛcS a]eYy#NME#1xˀ4F@?)e_ i^r~Hw;غ;Mg敩;b34%.y{ًr8:7/7"0`31 aygM1nq^62z6yor\Os`VtSx1xSܟ[4|f̻ }^Y~g@'fOtYf>k6e?H9y̟]O;s)Sg& 7{jIAA'~bˆ@S]njq5}%]&-őĞ/s, I)8ei0n㥵;+x4E|]e.2M',|r.E뽅.cSo&;ޝ5c֮~L'F]NC.bb/s:[O1uhho:.x9'nSRxNy-i<kvn3#B3"u?Fl4x]\s&`XxR`?,^Hg#w71cfBǹ55q_]T*Xr'?CJqqIfDs.}2R+JyZr%_Ej^[IPk 21#nA 8pDhňa,F;|  ;aܣS t>fa6Ett{$1f<-N;8'C@6_ @v/8R_[?1[p_f d+3L4vH!4Ogzypt2LiSnu/CIߺyXB!\m߰5𸗏d$}&ƶ땀j3VB2laj6nKSf7W-DN)%ʃxw1`X=2f)[MxR'E4zH.&zWzx% Dop-2p2.X=O<W׍~ Chd Eږ߆HGʟ8|SY)oK AY_o35CwʽJۀu(F=8kqbgX>?*Nv]go"w/"wA ~(B#vx8` !HAc=19vb41lM)1J`{Fd "!e1A%Xta1QaJ2t/cK`lsnL_ a_k &)-]!b(ydKl 1;bwrC"Z)ATXaL""& H/m`&0FxI)21>y#;  ^^D%6N*6%#tK"]{)"NV1%usĨ tr<Mh\vNQ^zՓywT*(ІRڏi:+"̣bF7RJb/A Jk.)V+x7t=thyO)򊼌JT{(_pUSNO'etGKmh;5mwE pRzݬJ2':5i:Z މ+GgՓHDP]=}dA`Q:A<;"GD_k뾤̈ ۰b[3se6;*LڞS}ڵ^aF텫ϔfo[үbٖtsxwm!FwɣQo^J۾mwNAe6ckۻ\ϾN;q͋o#r|°=9iɉut|})./,0WW﹬VpԥM)}tS-%%>KV[wD^?z٪<Scg>:@gO+vE$nؐHӵE 8_^]R^):69+}wRb9Xc["7/vWDj,: ZJ8;ք&GO^^^XrY1QkE"ݕ@KBgpo]~c°RT-keFCNR1UcVÌR߮/sG/QZŧQ=GS#Ԇ?(%>Q֚bCXA)!)%#ߓXjSa;jqQ1IcT:aR 8Fġ<$J".@0i5{ vڬO08'q+~Q=ZyC9U\qʜ]*F)P)rL{Ye_#pL!Rrdp6Ʈ4s=e DSu9rv\\xD5N€ O+,۾aOq*.avTE%睂h(փ@'E)W(.[|U z.Fur hh-bkttTPŜ1Z3^qH%M[('FIwbd_3Mr֨c'$;;ˀT=bX'su\motT09MϋuP$#:ܔd"cA0EBjh:>e",Bnn1ʏ n$DxP 2&+Dzbj?Շ}?2[;+1Q ֺ(ʁ nP(KCfTޚ~j(R[}!jK*(++ NnXI!+NKV7-[ǎ kNOmud񏤶Oknמ<ܖiP؟]'?(*u玈~j_;6^+w:%ۓ@#7G hV?K?tSn QI2 Ks[Nٟ()s}/k5NhhhVrDVa΄[cVº/? bWe}ҏ>Rr@3!;jWZJ\DyvJ@/,*/ EoKJom K_-QN^_tu"s.̕c4⚑(0] Me;cۃ2z+Ju5d,){D\XTDOeN/K~#zJu~+boru\r]b+ v$uwGoxH{\2互S*8i2KJ /V¹kdN֞.ROxpVYb6+S~cuᨲXzKVRYZvb R'y*ˢ V%2CmDJ!^M8bysOpTtp<F7 8Q̛^FdF*,~g>sڌS<A/0q3w?zrz6dwzmEXϞ6w֜{TuaD>PY>8=cm枰}Z&씃Niٸ{fz^~L=V 8=#lc^::=|OyW0-@w|_#q\cwYlf3,i?'C-4wlΏc4,9s<\a~& ZkÈwS%*KTol=Glc~ і!0/ F`*,!*,|ٕCPa$Oa{)+:H{ytCZE2׻H9ƃw+8I" $[[n#s{d_%g$2132?8n܁^yigbaØ0311|Zpf5rf ƘMbl駍t8ƞ@e(ufFV#np>W1 !3xAp À;mܪ{䁛u??`nu37vYwAU?ό8 =p0<ȈN1:XΈˆz'3bTFƈMc>##ƈGe8J̈ͺ!:7j$%r5RT]GZM&MsX;ju)OwnkE!G{:Yc Mk iڏ77pg6Dumɒ|t/?c߁RÄ|W]f8J3 څf&1:Ù%FK6c*0/4h'` ɪ[QHt&kf#d>q^hA0X _bBߑ{A:fiKϠ2| ;!!м357ΑQDp4 CӯckIϟⱄBfԻ/>*ޙ6L$Ϛ ;>~__(C?G MbDDd~xB'OF":u=1,0~H߇ ^n٘4x; k8M6M#?>i"}XP=+!"fc<cѠp¹C"oLd*s=ކm|&q"CR#8h6bp>JERmwāh(A鏘!bEE[)' b|ǟ6g,sh1sX`sxt V7.y$߸L%a p&I_:iU,-i).\DD _́&()\ٷgY*H_^io>"|6A6y`IQ[*W~X/@Js1c֮%KbdI/!Ke߬]M0|,yKBBt؅ /JHGߐ T!}g-I!`*/Jg I#TkыfENEinV*t^NZP#A4BvDb[~txK#"!3{ W3mZMHnBnw]%G@>­X5mEvn;܂E$1B93$+j|х nq&[=*:O},S7h JVtrBJ Ɂ)Jt.ٶ_ED$I^Oay)H/".Hŋ}7/x _YrW*1qGxwaPG@]5+|H|W:qkSɩĝ&bx.U/dBԠZ@*ɂjacaɨli\BrdQ'hG7jjG5b )xQ3fqXN >jݢEuS@cJUuLL#=5!ZB2p.d=A{?ڃ@[{KPpcNc Eu4NJu> a>O3o1&Kh1l m hP7Ց a Ď f+#8na/"^@$z '8 ?()F2a>Ȁ^&U60І:CHtEQ 5$PcdA|iPY7ǐB?Z;h-Pmpf ΀ YlXB;:? Ros:Zn1XREbZXҟi6`@D P7WZCG*6 APiEԌAD_ ޥD ^e!buըQA d#FG]W&P 6  1a#uQTjcfL|R`BAAH0K3A TVEqzQs}F0%J'NB i/S_b&kЖrEd,VӞX.ZїT'صvn8*A+A`%цUo@T̢fRkWZXXiFtNU<Gxt<Gǿ1:V~3"#ƕPˆ3.Ě"3#\<6 N5hZ !vk*,& e9 Dg`I3ys pCBBt݇6QҴbZMjk>S-yK7՘/7hiz/l=.${{gr噞ݻI^!]{PK$gZ]GޛM ГhI;q9z.6D} 7h\ሡI%[Nb҉B-~\k$~]CZ ݭ㺴֭/,X::ꛦ+n('1z4(={t4i0v4; >r^ n%{6 fBrПAu 9h!RJSufѡ3*Nl (AJ|41&l>Am|`2B, # P>nKx{'ޗt`;``4B=«Et 8"'æ翚fK/tk#f-EjghX6qDۻ 9kn%` "?SKX50P~51' PVO*EBb*^L8R`Kg\ #꾄]%ܸ܀\G(վ;^\J܈[f :'Z]"QbBuF5ꁛ6oe.5y%԰91ŷx®vE2'7I>`%$w.V:,58k/}+ Ω9tn6h=!jڶ,qUU9wRHEJ RD!HA""ht >DZh4ABAR|ҤCěr}ͅ.Z|/Yg̞{mcogGWNT\k`ե+>$;PyrŌ_E~|Ub'u#c w+Qq+COƎi:$kG9%Kppسyt>3Ur uPCAA<@w]mr!9{\J_&뼆$䖁wwi@6-mT-iͺ \iJKbeӴ{H8+%߮/Bd}WYuU[uU[uU[)+—#]=G{ꕁ:u8Ҹwh{pj_xI%$E>"¬5 JW[Պ{Xi3iڶOe9W\ bX~(Ì>-炮K%C*X EQ%X3vfWl?FmocئjB?=~%*>JPǹd)KԬIqBB|I!L~ 3m'}JGgӻ: 3WV*#x}>I)b[El5M ?1PP}y0%u kBƬeMwkϓYe{.,?w=;ޅze]:f5r^9Lb [6w-a,.-v.Uk0VNW'Gm8l)Z1/޲2^澲 Fu;4Zg(ނrrPΤAZcvO"biGbN vS_2_V1s Fn5fmM1XGvM\",]:U-hT?mL꼀{Us$7[)DknԛHK3/O̟]ps>b:c:c:c:gzT35?Ss8ÄHQIH• W,:~SnxdeJO ).M2+O>"Tq'o}i#~sn)nX o_M`#N"C%otw|GK/欎AD Mt۪x`ܓ7=Il'6a 04= ME*BjA\pB=z 3|`TgEja-j*zj+TVq@M{ӻ#Uq? O;]$zA[u|@vm ^kjGwn #yWf9P'pc)MMNMnMV/ ++v;-벒7i̬]܅(w8{; u$#l3zBtCnI&a-CsN`gɴGfٿepE?jk yR-lD>Ew56ױ` 沺` 4K&Du&Ku Yi|vI)C͜kbfI-gqw6~n?,:ͺE եںrk VB-w*A9t}5m5 2kPÆ#ȓiז: D sT!UFh+(`+LGs-X*Uo20e] )Z359G! k-U[' v] 3OyׄU5b\W5$bPQ=E(>bL;Vl`f12?ɔb(4VsyX_0H +zpqSYMiXV!ucbI_pj^/:_8b;DNu:ܥ jq\wp789ZBm\JFl}s~t[}?ZGK)/^nߏ~hI9 g^7{r 9u-qY R_,x|cUN :f܄I^˂f`2KzilTV аs,n~rؾfZ68|ϧP|Az7#,@J dlsvAN CpMG5#\.uLE#!ITF7Nlݾs&،;Y6/ð=A$,,X 0˶iڕKO <>(QK} ݋d)z`I/aNS C;'=-^wd$b'^DtB)rSW,MvA&u AECHzL&Λ7wάoNzXhPMM&@L *b"뇇id|-{!9Q;CK< |b>]5 }Dc_c>!I-W&]Z7wuq ^Т1ZbZ.>&r&G ȏZ,TIxt}88(h@(n( Ob7knώ^h\פYbrJZzFV7`i8Hhٗ0'jeǍSLO#~"=BI @_شQ|tDhLOZ" a$#p|W?'n1t|07`"祁f]glk>t?=mmeHGHgzgğ-VýfJ BBG%4C-ckD~/zm֎f,jl݂R=熽˯FN}="i@oў^v#8u:Xw߼]tl:!P$ΙsJg`7PkM`,EUPFUGVly2ʋ(&#Й8W؋9N:O0R?hc^ןo0#oPa'51k>`z"uבGt)E_&%AKPsQ]r=740Q/o9 5108"zQ %/0omò3H\[(pnoωN}kwcѬm16ƜciFG:u-Џ]=C_|wg,Kc}$u=$cVG'V005~#"ЀygG$¤} 3s1Ԗ4o鴂mFOS~X&.M`{j+*`XH.+TgzQPr,cYM_L*V?2ҸXA0!)ϡwhX*;e?)Gy9`D:|`ê`;DC>۝7P9Ef ORS'}{5K)G|2Fr!5U)EՏOGq/Ĺԃ🱽jc_EYe/5\߶UUB-*(oBz.NYe,^ 45"ydA_m@Ǫzl5{Vq#-(S IE۬kX44#1eXmd֝ёtkҦf;@8Q/_h$a+;UFVB0I\2V'cab&?F>h.HHrB?Va|ؘcZ`,TcTĨT[4 C~׎pQR cQjm5< դG sLΪmGW5 w,v&M ("~HD X8<Ģ!x!ԓXSC$C*q#<r*kVuI u=e'~,[D,! k@: ka p2F,[MQ?Hd!̂ J54t@!@`$$a#.q qB@t7}I[V֌.1^{_6KY;yq66k/75r/{KzTMwWw}?o#.@6Gp+x$_@63gExc9{m:f~`E;Wv\E=; 7_͛e'&ւN}`AA;sV_O'tW+vJBޝvpA}k=\JX۷;Xy\/YׂuUsu\ty?2]{K`G |1h{̇]C_܆zN 7[[Zpv۶wA?WCS5ͽ?c͚WkēA7#ؼnZC!&bî.x'W[mBu\5sӟ}q%VPǀ+ zAEa4ɶ@>݋ (Ǧyj:sK rr~emȽ)h"n&Dd;rG#7B7_ZtF6`Y;וxs\VtY'\w#p]o}85v\ۑ+#?%G|p)l!}|_y \v@7Z[v#:zs 6t<WpeF̞۷"^/oNKk 'Zj >"YCmdKb&e=l|y{-ܻdJ͜hE_D ~:^O5;mu:c3'ڇGq`okמ3H]ZKށ--kZu+г?2EX{:|> 6Yնbm~y7  Ɔ pw-_ܶiЪZ>[]W%u\b*-#Wnm~#s=cK+h; 0}~ޫደkaA?lq}ַ@0}`T,}|$ot-jH%v"swŷqE |sK缕@`g!ȵWCd1:>)Y=לYP\Y@3]\+:S1E{ hТYo.ڭ9?mGzM+K3Ly(|Y֜/o@Om28gnY18ys,1wl{|cx^xZMALt׬6Gy/!YipuPMY;JŃZ~#LnY6!KWBx/7\o.4X "P"~e&8m;U~wg;oHh\M.E"a;nC 1/w,[I}=l{|PX׹$ ȶv~t_K*X&aYL#o\ Կn_v>-l۰ې!Q`j]xg>P¡$4w\eʴȱ,MW݋[楚:&r=p"Mc/~-+@|n| wk kg 5?_im#򍧧 OFᎭ˷U2䪭ȿ Eכg /E:Zg;A5iԜhε̾b9c-nްo眫²޿syMkڣ|4XҖ!C/0r7 =X8wSGN O!? zwt%͙1Zku#;@E˴Evc }œ} ;7oӋҢ"޹1yBlqmްGul}?:wG~ |wt.ֵMWş]mvۖ׮㻳ztѡRj`1< 6@ѼOZNxk>%+VwAXSkA6H+d.pZ&ZvV|khǯȂS˟)prTwӒR7ߵ~S6Ap6ۻv햣<~ mQyr),񕈁_҅AkWk l $q?Ng*ܾ"~0no\/"H e_ks6dK+0\HNAޞg}볯~"iWs]i2c@_y=PW[|\2@m\>I@h 6䣩?q՜VoJx J|) g{5en;|]=P{8p͠Yz6x]χ``o⫫!XÓ!q9d{zhr>o)Ng3v:k9vp f+;Y;rSZ X!~wZ T@6qXw3!] 3/JvOy;kd׷w97S`e\[KPGip{cGt}td+,yF4~hY-`;:-ufUJۅap=83 Ȟ߆=ȥ>~hn8=|(vad*hW`ꥻDv7(XwSG g;@pkcdpu86AwmDw"m/ i{}i{HwG H׏!?| i t#1"W_t*Xve8cX:?R($O~CQfi"N$8cY(ob1;$ '#w"؄ݔDl2'i9nX ee֘8u<;`"7QR0!dĞ4FCE(R,"Zaa 6,5''>YɞdStPVI)p Ǚn^?q YU4f-t-+X(JU R3;Ot<3ŚK-O: %!4+J8^S$3"k. ZҲ"&DT-M'rFaXIvI&ʖx$Y eh5"Ų4P0cNdS'K6'5X)pfl!V39I%i5 d,bI&7,Yh4IV:J9`A3C*a5dB2D YEETYB1[VuR,Y , c62<80KO3(,LPENLJDvQ!!4$YɰNS R}}\\Qp5ihWO kTfDmGޯ,?d3G銜ڿ?}uX),:[B(P-7 3+>dCe\V4]fŪTW*JRe#8W0Z.鼖ϳ{1i" },FЪJe؂Tmh\R#ahڪ6)NB|(GykEESO4˒Ny4KXP 8Z)D,gڲ1}) TRAhļ֓!2[i6ut,JT$\cDɐӸDX%#[#'4A͏[D,TL"&VKGۖ4`[XcSSecZ&#I=B$Dž i1$9! DVMg2LVx,2ARvfyfQuITuFѨd+6FOb)FE{1Yd+-Fب-9Sehfq2Xu8:+t/(TN9x<Hү '.JF$bs 16HKtU'K[{q 5D,CfT4E؅DmJȦT0̪* d-s&AsFDH<_e2-̕ .ɸSRU5 \KktF qpO^J"a 2Fc91|?53J7 =AbL"EAI]u C*>̣68:Wb{82%z-R6INtҨYTg@hZhe"Cb"*KxR@1`]zz30fjĈư4I^QŤ(1IP*sLx.JsCW ˠt^\RDq/lNi2"/$a"ٜ.r [T bR$5)0VWΌUƩ1Yr$cd V2%DɮQƋqQ!q7s fT)xcĢS[/cZC_MPIJ-LddZ5\d0aecH3]+INiQ5_"&E"Vs*T *f1D$fV}ǀty(4!|HOsFNGs98##$SSʲǎ| b* և%[Zx^H$i%5cvAK#q`IjhԒZo|Ӓ13,N5yETz3uut.:} C^u{튌HőT 7AM|^"ձ0\0Tn)JEqjwJ6-_4Z"FM.&.n_aPCJYcW5 lq-zJs{(iJ;Y3NfȎ\wq A @ŜTh^aרnwq $X͒PaxBTkEK7EYC/Wyhh)ekNJ5x򸍏)3#.n[Q;3&\-,QQ~߫d3.nOa묑$rVYQk :Ja(nan5Re/fFYA7qbllxt,wWݾ)R\M?|I/W^9QMU)۷|SJv@q$sLPqϞ<}SGk2 1k)fj$V: ݾ%! a@B€0 ! a@B€0 ! a@B€0 ! a@B€|a@zrk.<74.<74.<74.<74.<74.<74.<74.<74.<74.<73.<70.<70.<70.<70NM+hA tQ &?BֈlR^:LFrUK.7ʩ3͚8 H\QiGOwCxRA PHCEg9!SLe .0feLZ"i`} h:G%^R%j>[d 8pRU4i%dB36lϚ dI,*!D֒)YQ9O橌栠!j*bg¨.huLjmzN^/`h2.S2XQ;Yu9 L.4瀘crⶢWOrH'%dqQ*Ĉ,xM$0^d R1+$@lOФH'j N)+X* C$|lS4&:[Jaxab~E/`ɞ8ʬx(d9 4 &CecOMei\ ؃,b^|c R%$X ^@d$G{Ӡ *0bb3HBw`^ ["ݾ% ["ݾ% ["ݾ% ["ݾ3 ""AۻDKAۻ|A˻DKB€0 ! a@B€0 ! a@B€0 ! a@B€0 ! <Àɏ v|ߌL|np\|np\|np\|np\|np' ~p/p' ~p/j' ~p/j' ~p/j' ~p/j' ~p/j'IB~d/d' J@~d/d' IB~d/d| v-ǐn| v-ǐn| v-ǐn| v-ǐn| ׂ|KI۷DMKI۴DMKI۴$ $! I@HB$ $! I@HB$ $! I@HB$ $! I@HB' kM kM kM kM kM kM kM kM jwApη "~h/b7>h/h'~h/h' ~h/h' ~hOh# ~h/h' |:c}o Dxq?^O47 Dxq?^O47 Dxq?^ $]O"?]O"%$]O"%$]O"%$]"咭# ^"% m]"% m]B< !y@B< !y@B< !y@B< !yL~f}(3h k k k k k k k ύ SGB3 $x1? [f'$ b~B/f'$dZ(? xa?9^ON6 xa?9^ON6 xa?9^ON6 xa?9^ONA?Q^OT6 Dxa?QGT6 Dxa?Q^OT6 #%8m]#%8m]#%8m]#%8m]>8]#!n|/Ǒn|/Ǒn< !y@B< !y@B< !y@B< !y@>< .>77.>77.>77.>77.>77.>77.>77.>77 sͬPOpApApApف/v'+ b~؁p'(  w{gYVEῪiE|ADH1@fPPFA4F|1D ȤkWZ[q jVj0{ݽ}:HP}Hj5 AE HP$(@R Pj@5 AE HP$(@QI PȀ(d@R2 )  HJB$%@!I PȀ(d@R27uj7uj7uj7uj7uj7uj7uj7uj7uj7uh7ub7ub7ubYD%                              aa &)R!@RARԡCRaER!GRHRԡJRaLR!NRORpy>^ދ~Un^~Un^~Uh^~Uh^~Uh^~Uh^~Uh^ó;Y븞;|Y#;Yor]gOzkg#z0yg Y;YT; YÌ;Yĺ; Ydv8Y!Now{dv8Y!Now{]dv78Y!Nowsdv78YN,N,N,Ն[:[:[:[:[:[:[:[:[:[:, w^~UZ^0 @*0k*0k*0k*0k*0kIP(`@0 i 4 H$ @IP(f@3 i4 Hb$ @1IP̀(f@3 i4 Hb$ @1IP̀(f@3 i4 PMׯT$ @qJ܀(no!o!o!o!o!o!o!o!o!o!o!o!o!o!pa*p~Uh^~Uh^~Ub^%~Ub^%~Ub^%~Ub^%~Ub^%$@  PĀ(b@1 `E H0"$@   p P(\@. ` H0$@ p P(\@. ` H0$@ p P(\@$. kI8$@p P_C֩_C֩_C֩ xݻV_C^\5d\5d\5^C_C_C_C_C_C%$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $fqګJ ګ`_{w^ W_{WBh^~Uh^~Uh^~Uh@b4 X H,$@A PЀ(h@b4 24 L H&$@A PЀd(h@24 L H& @A PЀd(h@24 L H&$@AP(`@0 y+:+:+:+rCCCCC,CG{x\Dֹ_Dֹ_Dֹ_Dֹ_Dֹ%, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,沀_~W_~A*3˯2*3˯2.1*3k2*3k2*3 p H8b$@1 P̀(f@3 p @8AP؀(l@6 | H>$@aP؀(l@6 | H>$@aP؀(l7~Ʈ$ |H>B$@YgYgYgYgYYYYYYyipupupepٽpi-I"DTyMZfa-o y-XωXȉMXݛXmXXXXX=X͘X]XX{}Xt XmXf-X_XX '0me)})&lg˜ G&`| %e( J.(]PtA J.(]PtA ztAgwg~ O3t~ Okwx~ O{~ Ow~ Ocx_xr ɾcx՘mnQt2?7imt3?[1<.͏Vj~ OYcxYT<*GţQxT<*?;}/&a9 ȱX7@ĺrl. cnu؆ Ǧdyآ, dž OתX rf97ȱY7@Ϻr, cQnKuX0 iyh9VȨxT<*GţQxT<*~=**5ӽk{Ljϯ՞_3ݫ=fW{~t^5ӽk{Ljϯ_3K=fz~t/^5ӽk{LRϯ_3Kqt+^#Hx$< G#H[-xw/ts{9ܽ^yw=/|K/}_c{nmz>|~s~ww~ד?]~>~oO<>;}qݞ׶'i{=ߞm_=ߞlG|j{>=۞ݻ?) w>zso<52A1ۨ>itksnap/UserInterface/MainComponents/Artwork/spray.gif0000644000076500000240000000233510534177602022523 0ustar paulystaffGIF89a))!!!!!!)))11)19)991991BB9BB9JJBJJBRRJRRJZZRccZkkZsscsscs{c{{k{{ksss{ƥΥέέֵ֭!,)) H*\ȰÇ#JHŋ@0xň L8I+bR   h؀!8 0È/*R@\pi paK(8P0Vl(@"`H`  @_  'Z\@@ T0@"Xh)0#1`AD  lH& x@ uyqWB}0ɥ8{&aj3}pPH8aA@$QJ@t= 6F(Vha;itksnap/UserInterface/MainComponents/Artwork/zoom3d.gif0000644000076500000240000000256110534177602022601 0ustar paulystaffGIF89a))!!!!!!!)!)))11)191991BB9BB9JJBJJBRRJRRJRZJZZRccRckRkkZkkZsscsscs{c{{k{{ksss{{ƥƥέέֵ֭!,)) H*\ȰÇ#JH !8 i l )D",dl at`P tQ/7B RPaD6bXP̘Qa@[%(b.,0cWZ1HIk0#Ƃ'$V)Ā8B3{ a V|i( h,. hPCA@ED D @( DL@`M[`FA( j-L3h#`dD 4P#S,P@v @  %B`@W' p?N.@ 10W1@dTC  l ` #L@ LX9( 2d6T (Nc P$L0@GQDexB   8̖!pf1M * /$ 2.W覫;itksnap/UserInterface/MainComponents/CVS/0000755000076500000240000000000011560342171017670 5ustar paulystaffitksnap/UserInterface/MainComponents/CVS/Entries0000644000076500000240000000457511560342171021237 0ustar paulystaff/AppearanceDialogUI.fl/1.14/Mon Apr 18 19:22:49 2011// /AppearanceDialogUIBase.h/1.4/Sat Oct 9 04:20:08 2010// /AppearanceDialogUILogic.cxx/1.14/Tue Oct 19 20:28:56 2010// /AppearanceDialogUILogic.h/1.4/Sat Oct 9 04:20:08 2010// /HelpViewer.fl/1.1/Sat Dec 2 04:22:22 2006// /HelpViewerBase.h/1.2/Sun Dec 30 04:05:17 2007// /HelpViewerLogic.cxx/1.3/Sun Dec 30 04:05:17 2007// /HelpViewerLogic.h/1.2/Sun Dec 30 04:05:17 2007// /LabelEditorUI.fl/1.4/Mon Apr 18 19:22:49 2011// /LabelEditorUIBase.h/1.4/Mon Nov 17 19:38:23 2008// /LabelEditorUILogic.cxx/1.10/Thu Jul 1 21:40:24 2010// /LabelEditorUILogic.h/1.4/Mon Nov 17 19:38:23 2008// /LayerInspectorUI.fl/1.19/Tue Jun 15 16:27:44 2010// /LayerInspectorUIBase.h/1.6/Wed Sep 16 20:03:13 2009// /LayerInspectorUILogic.cxx/1.27/Mon Apr 18 15:06:07 2011// /LayerInspectorUILogic.h/1.14/Mon Apr 18 15:06:07 2011// /PreprocessingUI.fl/1.3/Fri Jan 23 16:30:54 2009// /PreprocessingUIBase.h/1.2/Sun Dec 30 04:05:17 2007// /PreprocessingUILogic.cxx/1.7/Tue Oct 19 19:16:30 2010// /PreprocessingUILogic.h/1.3/Fri Jan 23 20:09:38 2009// /ReorientImageUI.fl/1.1/Sat Nov 15 12:20:38 2008// /ReorientImageUIBase.h/1.2/Mon Nov 17 19:47:41 2008// /ReorientImageUILogic.cxx/1.5/Fri Oct 30 16:48:22 2009// /ReorientImageUILogic.h/1.3/Fri Oct 30 16:48:24 2009// /ResizeRegionDialog.fl/1.1/Sat Dec 2 04:22:22 2006// /ResizeRegionDialogBase.h/1.2/Sun Dec 30 04:05:17 2007// /ResizeRegionDialogLogic.cxx/1.2/Sun Dec 30 04:05:17 2007// /ResizeRegionDialogLogic.h/1.2/Sun Dec 30 04:05:17 2007// /RestoreSettingsDialog.fl/1.3/Mon Apr 18 19:22:49 2011// /RestoreSettingsDialogBase.h/1.2/Sun Dec 30 04:05:17 2007// /RestoreSettingsDialogLogic.cxx/1.2/Sun Dec 30 04:05:17 2007// /RestoreSettingsDialogLogic.h/1.2/Sun Dec 30 04:05:17 2007// /SimpleFileDialog.fl/1.1/Sat Dec 2 04:22:22 2006// /SimpleFileDialogBase.h/1.2/Sun Dec 30 04:05:17 2007// /SimpleFileDialogLogic.cxx/1.5/Mon May 31 19:52:37 2010// /SimpleFileDialogLogic.h/1.3/Mon May 25 17:09:44 2009// /SnakeParametersUI.fl/1.3/Mon Apr 18 19:22:49 2011// /SnakeParametersUIBase.h/1.2/Sun Dec 30 04:05:18 2007// /SnakeParametersUILogic.cxx/1.10/Tue Oct 19 19:15:14 2010// /SnakeParametersUILogic.h/1.3/Fri Jan 23 20:09:38 2009// /UserInterface.fl/1.83/Mon Apr 18 19:22:49 2011// /UserInterfaceBase.h/1.45/Mon Apr 18 15:06:07 2011// /UserInterfaceLogic.cxx/1.124/Wed May 4 15:25:42 2011// /UserInterfaceLogic.h/1.56/Mon Apr 18 17:55:20 2011// D itksnap/UserInterface/MainComponents/CVS/Entries.Log0000644000076500000240000000002011560342171021734 0ustar paulystaffA D/Artwork//// itksnap/UserInterface/MainComponents/CVS/Repository0000644000076500000240000000004511560342171021771 0ustar paulystaffitksnap/UserInterface/MainComponents itksnap/UserInterface/MainComponents/CVS/Root0000644000076500000240000000010011560342171020525 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/MainComponents/HelpViewer.fl0000644000076500000240000000237110534177576021654 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0104 header_name {.h} code_name {.cxx} class HelpViewer {open : { private HelpViewerBase } } { Function {MakeWindow()} {open } { Fl_Window m_WinHelp { label {SNAP/IRIS Help Window} open xywh {133 98 612 710} type Double color 53 hide code0 {\#include "HelpViewerBase.h"} } { Fl_Help_View m_BrsHelp { callback {OnLinkAction();} selected xywh {5 5 600 660} box BORDER_BOX } Fl_Button {} { label {&Contents} callback {OnContentsAction()} xywh {140 675 75 25} box PLASTIC_UP_BOX shortcut 0x80063 color 180 labelsize 12 } Fl_Button m_BtnBack { label {@<- Back} callback {OnBackAction();} xywh {225 675 75 25} box PLASTIC_UP_BOX shortcut 0xff08 color 180 labelsize 12 deactivate } Fl_Button m_BtnForward { label {@-> &Forward} callback {OnForwardAction();} xywh {310 675 80 25} box PLASTIC_UP_BOX shortcut 0x80066 color 180 labelsize 12 deactivate } Fl_Button m_BtnClose { label Close callback {OnCloseAction();} xywh {400 675 70 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } } } } itksnap/UserInterface/MainComponents/HelpViewerBase.h0000644000076500000240000000326410735614375022273 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: HelpViewerBase.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 __HelpViewerBase_h_ #define __HelpViewerBase_h_ class HelpViewerBase { public: virtual ~HelpViewerBase () {} virtual void OnLinkAction() = 0; virtual void OnBackAction() = 0; virtual void OnForwardAction() = 0; virtual void OnCloseAction() = 0; virtual void OnContentsAction() = 0; }; #endif // __HelpViewerBase_h_ itksnap/UserInterface/MainComponents/HelpViewerLogic.cxx0000644000076500000240000001037210735614375023027 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: HelpViewerLogic.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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. =========================================================================*/ #include "HelpViewerLogic.h" #include #include using namespace std; // Callback for when links are clicked const char *HelpViewerLogicLinkCallback(Fl_Widget *w, const char *uri) { // Only deal with visited html const char *ext = fl_filename_ext(uri); if(strcmp(ext,".html") != 0 && strcmp(ext,".HTML") != 0) return uri; // Pass on to the class HelpViewerLogic *hvl = static_cast(w->user_data()); return hvl->LinkCallback(uri); } HelpViewerLogic ::HelpViewerLogic() { // Make sure that Fl is set up for GIF/PNG images fl_register_images(); // Clear the linked list m_Iterator = m_LinkList.begin(); } HelpViewerLogic ::~HelpViewerLogic() { } void HelpViewerLogic ::ShowHelp(const char *url) { // Put the url into the history list PushURL(url); // Configure the browser m_BrsHelp->link(HelpViewerLogicLinkCallback); m_BrsHelp->user_data(this); m_BrsHelp->load(url); // Show the window if(!m_WinHelp->shown()) m_WinHelp->show(); } const char * HelpViewerLogic ::LinkCallback(const char *url) { // Put the url into the history list PushURL(url); // Return the URL unchanged return url; } void HelpViewerLogic ::PushURL(const char *url) { // If the link is the same as our current location, don't do anything if(m_Iterator != m_LinkList.end() && 0 == strcmp(m_Iterator->c_str(),url)) return; // Clear the forward stack if(m_Iterator != m_LinkList.end()) m_LinkList.erase(++m_Iterator,m_LinkList.end()); // Add the new link to the list m_LinkList.push_back(string(url)); // Point to the end of the list m_Iterator = m_LinkList.end(); m_Iterator--; // Disable the forward button m_BtnForward->deactivate(); // Activate the back button if there is a back link if(m_Iterator != m_LinkList.begin()) m_BtnBack->activate(); else m_BtnBack->deactivate(); } void HelpViewerLogic ::OnLinkAction() { } void HelpViewerLogic ::OnBackAction() { // Can't be at the head of the list assert(m_Iterator != m_LinkList.begin()); // Go back with the iterator --m_Iterator; // Enable the forward button m_BtnForward->activate(); // Perhaps disable the back button if(m_Iterator == m_LinkList.begin()) m_BtnBack->deactivate(); // Show the current link m_BrsHelp->load(m_Iterator->c_str()); } void HelpViewerLogic ::OnForwardAction() { // Can't be at the end of the list assert(++m_Iterator != m_LinkList.end()); // Enable the back button m_BtnBack->activate(); // Perhaps disable the forward button LinkIterator itTest = m_Iterator; if(++itTest == m_LinkList.end()) m_BtnForward->deactivate(); // Show the current link m_BrsHelp->load(m_Iterator->c_str()); } void HelpViewerLogic ::OnCloseAction() { m_WinHelp->hide(); } void HelpViewerLogic ::OnContentsAction() { // Go to the contents page ShowHelp(m_ContentsLink.c_str()); } itksnap/UserInterface/MainComponents/HelpViewerLogic.h0000644000076500000240000000466210735614375022461 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: HelpViewerLogic.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 __HelpViewerLogic_h_ #define __HelpViewerLogic_h_ #include "HelpViewer.h" #include "SNAPCommonUI.h" #include /** * \class HelpViewerLogic * \brief UI logic for a help viewer window */ class HelpViewerLogic : public HelpViewer { public: HelpViewerLogic(); virtual ~HelpViewerLogic(); /** Set the 'Contents' link */ irisSetStringMacro(ContentsLink); /** Display the help window on a particular page */ void ShowHelp(const char *url); void OnLinkAction(); void OnBackAction(); void OnForwardAction(); void OnCloseAction(); void OnContentsAction(); // Callback for when a link is followed const char *LinkCallback(const char *uri); private: // The list of visited links (back and forth) typedef std::list LinkListType; LinkListType m_LinkList; // The current place on the list typedef LinkListType::iterator LinkIterator; LinkIterator m_Iterator; // Contents page std::string m_ContentsLink; // A method to push a URL onto the history stack void PushURL(const char *url); }; #endif // __HelpViewerLogic_h_ itksnap/UserInterface/MainComponents/LabelEditorUI.fl0000644000076500000240000001722211553107611022210 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0300 header_name {.h} code_name {.cxx} class LabelEditorUI {open : {private LabelEditorUIBase} } { Function {MakeWindow()} {open } { Fl_Window m_WinMain { label {Segmentation Label Editor - ITK-SNAP} open xywh {811 254 440 405} type Double code0 {\#include "LabelEditorUIBase.h"} visible } { Fl_Group {} { label {Available labels:} open xywh {10 25 200 320} box BORDER_BOX selection_color 0 labelfont 1 labelsize 12 align 5 } { Fl_Browser m_BrsLabelList { callback {OnLabelSelectAction()} xywh {15 35 190 305} type Hold labelsize 12 textfont 4 textsize 11 } } Fl_Tabs {} { label {Selected Label:} open xywh {220 25 210 320} box PLASTIC_THIN_UP_BOX selection_color 53 labelfont 1 labelsize 12 align 5 } { Fl_Group {} { label Appearance open selected xywh {220 50 210 295} labelsize 12 } { Fl_Input m_InLabelName { label {Description:} callback {OnLabelPropertyChange()} xywh {225 75 200 20} color 53 labelsize 12 align 5 when 1 textsize 12 } Fl_Value_Slider m_InLabelOpacity { label {Opacity:} callback {OnLabelPropertyChange()} xywh {225 240 200 20} type Horizontal color 53 selection_color 12 labelsize 12 align 5 value 0.2 } Fl_Group m_GrpLabelColor { label {Color:} callback {OnLabelPropertyChange()} open xywh {225 120 200 95} box PLASTIC_DOWN_BOX labelsize 12 align 5 code0 {\#include } class Fl_Color_Chooser } {} Fl_Group {} { label {Visibility:} open xywh {225 285 200 50} box PLASTIC_DOWN_BOX labelsize 12 align 5 } { Fl_Check_Button m_ChkVisibility { label {Hide label in all windows} callback {OnLabelPropertyChange()} xywh {235 310 180 20} down_box DOWN_BOX selection_color 180 labelsize 12 } Fl_Check_Button m_ChkMeshVisibility { label {Hide label in the 3D window} callback {OnLabelPropertyChange()} xywh {235 290 180 20} down_box DOWN_BOX selection_color 180 labelsize 12 } } } Fl_Group {} { label {Label Id} open xywh {220 50 210 295} labelsize 12 hide } { Fl_Group {} { label {Label's position in the list:} open xywh {225 75 200 85} box PLASTIC_DOWN_BOX labelsize 12 align 5 } { Fl_Button {} { label {Move up} callback {OnMoveUpAction()} xywh {250 125 75 25} box PLASTIC_UP_BOX color 48 labelsize 12 } Fl_Button {} { label {Move down} callback {OnMoveDownAction()} xywh {335 125 75 25} box PLASTIC_UP_BOX color 48 labelsize 12 } Fl_Value_Input m_InLabelId { label {Numeric Id:} xywh {310 90 50 25} labelsize 12 when 4 textsize 12 } Fl_Button m_BtnSetId { label Set callback {OnSetIdAction()} xywh {365 90 45 25} box PLASTIC_UP_BOX color 48 labelsize 12 } } Fl_Check_Button m_ChkPromptId { label {Allow me to enter label ids when adding new labels} xywh {225 205 195 20} down_box DOWN_BOX selection_color 180 labelsize 12 align 149 } } } Fl_Button {} { label New callback {OnNewAction()} xywh {10 370 65 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label Delete callback {OnDeleteAction()} xywh {160 370 65 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label Duplicate callback {OnDuplicateAction()} xywh {85 370 65 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label Close callback {OnCloseAction()} xywh {360 370 70 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 } Fl_Button {} { label {Tools...} callback {OnToolsDialogAction()} xywh {235 370 65 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Group {} { label {Note: changes to label ids and deletion of labels can not be undone!} open xywh {10 345 420 20} labelsize 10 align 20 } {} } Fl_Window m_WinTools { label {Label Tools - ITK-SNAP} open xywh {826 625 295 270} type Double modal visible } { Fl_Button m_BtnToolsApply { label Apply callback {OnToolsApplyAction()} xywh {80 235 65 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 } Fl_Button {} { label Close callback {OnToolsCloseAction();} xywh {155 235 65 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Choice m_InToolsOperation { label {Operation:} callback {OnToolsOperationChange();} open xywh {15 25 265 20} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } { MenuItem {} { label {Replace label by another} xywh {5 5 30 20} labelsize 12 } MenuItem {} { label {Topology-preserving merge} xywh {10 10 30 20} labelsize 12 } MenuItem {} { label {Enforce well-composed property} xywh {20 20 30 20} labelsize 12 } } Fl_Wizard m_WizToolControls {open xywh {15 55 265 160} box PLASTIC_DOWN_BOX } { Fl_Group {m_GrpToolPage[0]} {open xywh {15 55 265 160} labelsize 11 align 21 } { Fl_Choice {m_InToolPageLabel[0]} { label {Source label:} open xywh {30 135 235 20} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } {} Fl_Choice {m_InToolPageLabel[1]} { label {Target label:} open xywh {30 180 235 20} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } {} Fl_Group {} { label {This operation simply relabels all voxels assigned the source label with the target label.} open xywh {20 60 255 50} labelsize 11 align 21 } {} } Fl_Group {m_GrpToolPage[1]} {open xywh {15 55 265 160} labelsize 11 align 21 hide } { Fl_Choice {m_InToolPageLabel[2]} { label {Source label:} open xywh {30 135 235 20} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } {} Fl_Choice {m_InToolPageLabel[3]} { label {Target label:} open xywh {30 180 235 20} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } {} Fl_Group {} { label {This tool grows the source label into the target label preserving the topology of the source label.} open xywh {20 60 255 50} labelsize 11 align 21 } {} } Fl_Group {m_GrpToolPage[2]} {open xywh {15 55 265 160} labelsize 11 align 21 hide } { Fl_Choice {m_InToolPageLabel[4]} { label {Target label:} open xywh {30 135 235 20} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } {} Fl_Group {} { label {This tool makes the segmentation for the target label well-composed. Well-composed segmentations have non-ambiguous topology and lead to better meshes.} open xywh {20 60 255 50} labelsize 11 align 21 } {} } } } } } itksnap/UserInterface/MainComponents/LabelEditorUIBase.h0000644000076500000240000000413011110344057022617 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LabelEditorUIBase.h,v $ Language: C++ Date: $Date: 2008/11/17 19:38:23 $ 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 __LabelEditorUIBase_h_ #define __LabelEditorUIBase_h_ /** * \class LabelEditorUIBase * \brief Base class for Label editor UI logic */ class LabelEditorUIBase { public: virtual ~LabelEditorUIBase() {} // Callbacks virtual void OnNewAction() = 0; virtual void OnDuplicateAction() = 0; virtual void OnDeleteAction() = 0; virtual void OnSetIdAction() = 0; virtual void OnMoveUpAction() = 0; virtual void OnMoveDownAction() = 0; virtual void OnCloseAction() = 0; virtual void OnLabelSelectAction() = 0; virtual void OnLabelPropertyChange() = 0; virtual void OnToolsDialogAction() = 0; virtual void OnToolsApplyAction() = 0; virtual void OnToolsCloseAction() = 0; virtual void OnToolsOperationChange() = 0; }; #endif itksnap/UserInterface/MainComponents/LabelEditorUILogic.cxx0000644000076500000240000004323111413205510023356 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LabelEditorUILogic.cxx,v $ Language: C++ Date: $Date: 2010/07/01 21:40:24 $ Version: $Revision: 1.10 $ 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 "LabelEditorUILogic.h" #include "UserInterfaceLogic.h" #include "GlobalState.h" #include "IRISApplication.h" #include "IRISImageData.h" #include #include #include #include LabelEditorUILogic ::LabelEditorUILogic() { m_Parent = NULL; m_Driver = NULL; m_GlobalState = NULL; } LabelEditorUILogic ::~LabelEditorUILogic() { } void LabelEditorUILogic ::Register(UserInterfaceBase *parent) { m_Parent = parent; m_Driver = parent->GetDriver(); m_GlobalState = m_Driver->GetGlobalState(); } const ColorLabel & LabelEditorUILogic ::GetColorLabel(size_t iLabel) { return m_Driver->GetColorLabelTable()->GetColorLabel(iLabel); } void LabelEditorUILogic ::SetColorLabel(size_t iLabel, const ColorLabel &xLabel) { m_Driver->GetColorLabelTable()->SetColorLabel(iLabel, xLabel); } void LabelEditorUILogic ::DisplayWindow() { m_WinMain->show(); } void LabelEditorUILogic ::OnCloseAction() { m_WinMain->hide(); } void LabelEditorUILogic ::OnLabelListUpdate(unsigned int iCurrentLabel) { // Clear the list of labels m_BrsLabelList->clear(); // We will also find the first valid label size_t iFirstValidLabel = 0; // Get the list of labels from the system and populate the browser for(size_t i = 1; i < MAX_COLOR_LABELS; i++) { ColorLabel cl = GetColorLabel(i); if(cl.IsValid()) { // Create a label string std::ostringstream oss; oss << std::setw(3) << i << ": " << cl.GetLabel(); // Add to the list the label and and the id (as a special data) m_BrsLabelList->add(oss.str().c_str(), (void *)(i)); // Set the first valid label if(iFirstValidLabel == 0) iFirstValidLabel = i; } } // Set the current label's display, or if 0 is passed in initialize // at the very first label SetEditorLabel(iCurrentLabel > 0 ? iCurrentLabel : iFirstValidLabel); } void LabelEditorUILogic ::SetEditorLabel(unsigned int iLabel) { // The label must be valid! assert(GetColorLabel(iLabel).IsValid()); // Find the label in the browser, and select it for(size_t i = 1; i <= static_cast(m_BrsLabelList->size()); i++) { size_t id = (size_t) m_BrsLabelList->data(i); if(id == iLabel) { m_BrsLabelList->select(i); break; } } // Get the label object ColorLabel cl = GetColorLabel(iLabel); // Fill out the form m_InLabelName->value(cl.GetLabel()); m_InLabelOpacity->value(cl.GetAlpha() / 255.0 ); m_GrpLabelColor->rgb( cl.GetRGB(0)/255.0, cl.GetRGB(1)/255.0, cl.GetRGB(2)/255.0 ); m_InLabelId->value(iLabel); m_ChkVisibility->value(!cl.IsVisible()); m_ChkMeshVisibility->value(!cl.IsVisibleIn3D()); } int LabelEditorUILogic ::FindSpaceForNewLabel() { // Find a place to stick in the new label size_t iLabel = 0; for(size_t i = 1; i < MAX_COLOR_LABELS; i++) if(!GetColorLabel(i).IsValid()) { iLabel = i; break; } // Make sure we actually found a space if(iLabel == 0) { fl_alert( "It is not possible to define more than %d labels in SNAP. \n" "Delete some of the existing labels to make room for the \n" "label that you wish to add. \n", MAX_COLOR_LABELS); return 0; } if(m_ChkPromptId->value()) { // Show the prompt asking for the label Id std::ostringstream oss; oss << iLabel; std::string sMessage = ""; bool flagChosen = false; while(!flagChosen) { const char *sAnswer = fl_input("What id would you like to assign to this label?",oss.str().c_str()); // What if they click cancel? if(!sAnswer) return 0; try { int iAnswer = atoi(sAnswer); if(iAnswer <= 0 || iAnswer > MAX_COLOR_LABELS) fl_message("ERROR: You must enter a number between 1 and %d!\n", MAX_COLOR_LABELS); else if(GetColorLabel(iAnswer).IsValid()) fl_message("ERROR: That label is already in use!\n"); else { iLabel = iAnswer; flagChosen = true; } } catch(...) { fl_message("ERROR: You must enter a number between 1 and %d!\n", MAX_COLOR_LABELS); } } } // If the space is not the last one in the list, alert the user that we // are sticking the label in the first available slot else if(iLabel < static_cast(m_BrsLabelList->size())) { fl_message( "The label will be assigned the first available ID, which is %d\n" "To assign a different ID to the label or to move it to the \n" "bottom of the list of labels, use the 'Label Id' tab \n", static_cast(iLabel)); } return iLabel; } void LabelEditorUILogic ::OnNewAction() { // Find the space for this new label unsigned int iLabel = FindSpaceForNewLabel(); if(iLabel == 0) return; // Get the default color label for this position ColorLabel cl = m_Driver->GetColorLabelTable()->GetDefaultColorLabel(iLabel); cl.SetValid(true); // Set the label SetColorLabel(iLabel, cl); // Update the state of the widget OnLabelListUpdate(iLabel); // Update the list in the parent UI m_Parent->OnLabelListUpdate(); } void LabelEditorUILogic ::OnDuplicateAction() { // Only valid if a label is selected assert(m_BrsLabelList->value() > 0); // Find the space for this new label unsigned int iNewLabel = FindSpaceForNewLabel(); if(iNewLabel == 0) return; // Get the copy label (currently selected) size_t iCurrentLabel = (size_t) m_BrsLabelList->data(m_BrsLabelList->value()); // Get the old and the new label ColorLabel clNew = GetColorLabel(iNewLabel); const ColorLabel &clOld = GetColorLabel(iCurrentLabel); // Copy the label properties clNew.SetValid(true); clNew.SetPropertiesFromColorLabel(clOld); // Set the description std::ostringstream oss; oss << clOld.GetLabel() << "(Copy)"; clNew.SetLabel(oss.str().c_str()); // Store the label SetColorLabel(iNewLabel, clNew); // Update the state of the widget OnLabelListUpdate(iNewLabel); // Update the list in the parent UI m_Parent->OnLabelListUpdate(); } size_t LabelEditorUILogic ::GetSelectedLabelId() { if(m_BrsLabelList->value() == 0) return 0; return (size_t) m_BrsLabelList->data(m_BrsLabelList->value()); } void LabelEditorUILogic ::OnDeleteAction() { // Must have a current label size_t iCurrentLabel = GetSelectedLabelId(); assert(iCurrentLabel > 0); // Find the next label for us to activate size_t iNextLabel = 0; for(size_t i = iCurrentLabel + 1; i < MAX_COLOR_LABELS; i++) if(GetColorLabel(i).IsValid()) { iNextLabel = i; break; } if(iNextLabel == 0) { // Turns out we are deleting the last label. Search back to the first label for(size_t j = iCurrentLabel - 1; j > 0; j--) if(GetColorLabel(j).IsValid()) { iNextLabel = j; break; } } if(iNextLabel == 0) { // We're removing the only label. We can't allow that! fl_alert( "SNAP can not operate without any labels. Please add \n" "some new labels before deleting this one."); return; } // Delete the label in the system, replacing it with zero size_t nUpdated = m_Driver->GetCurrentImageData()->GetSegmentation() ->ReplaceIntensity(iCurrentLabel, 0); // Set the label current label as invalid m_Driver->GetColorLabelTable()->SetColorLabelValid(iCurrentLabel, false); // If the label is a current paint-over label or a drawing label, // reposition the currently selected label if(iCurrentLabel == m_GlobalState->GetDrawingColorLabel()) m_GlobalState->SetDrawingColorLabel( m_Driver->GetColorLabelTable()->GetFirstValidLabel()); if(iCurrentLabel == m_GlobalState->GetOverWriteColorLabel()) m_GlobalState->SetOverWriteColorLabel(0); // Rebuild the list of labels OnLabelListUpdate(iNextLabel); // The segmentation image has been modified, let the parent know if(nUpdated > 0) { // This operation can not be undone! m_Parent->ClearUndoPoints(); m_Parent->OnSegmentationImageUpdate(false); } // Update the label list m_Parent->OnLabelListUpdate(); } void LabelEditorUILogic ::OnSetIdAction() { // Must have a current label size_t iCurrentLabel = GetSelectedLabelId(); assert(iCurrentLabel > 0); // Get the id and check that it's valid unsigned int iNewId = (unsigned int) m_InLabelId->value(); if(iNewId <= 0 || iNewId > MAX_COLOR_LABELS) { fl_alert("The label id must be a number between 1 and %d", MAX_COLOR_LABELS); return; } // Check if the id is already in use ColorLabel clTarget = GetColorLabel(iNewId); if(clTarget.IsValid()) { fl_alert("Label %d is already in use! Please select a different id", iNewId); return; } // Move the label into the new slot clTarget.SetPropertiesFromColorLabel(GetColorLabel(iCurrentLabel)); clTarget.SetValid(true); SetColorLabel(iNewId, clTarget); // Update the new labels m_Driver->GetColorLabelTable()->SetColorLabelValid(iCurrentLabel, false); // If the label was a current drawing label, we need to update that if(iCurrentLabel == m_GlobalState->GetDrawingColorLabel()) m_GlobalState->SetDrawingColorLabel(iNewId); if(iCurrentLabel == m_GlobalState->GetOverWriteColorLabel()) m_GlobalState->SetOverWriteColorLabel(iNewId); // Replace label iOld with iNew in the segmentation image size_t nUpdated = m_Driver->GetCurrentImageData()->GetSegmentation() ->ReplaceIntensity(iCurrentLabel, iNewId); // Rebuild the list of labels OnLabelListUpdate(iNewId); // The segmentation image has been modified, let the parent know if(nUpdated > 0) { // This operation can not be undone! m_Parent->ClearUndoPoints(); m_Parent->OnSegmentationImageUpdate(false); } } void LabelEditorUILogic ::OnLabelPropertyChange() { // Make sure there is a selected label size_t iCurrentLabel = GetSelectedLabelId(); assert(iCurrentLabel > 0); // Get the label ColorLabel cl = GetColorLabel(iCurrentLabel); // Get the color values unsigned char rgba[4]; rgba[0] = (unsigned char) (255 * m_GrpLabelColor->r()); rgba[1] = (unsigned char) (255 * m_GrpLabelColor->g()); rgba[2] = (unsigned char) (255 * m_GrpLabelColor->b()); rgba[3] = (unsigned char) (255 * m_InLabelOpacity->value()); // Set the values of the label cl.SetLabel(m_InLabelName->value()); cl.SetRGBAVector(rgba); // If visibility has changed, we need to enable mesh update bool old_visible = cl.IsVisible(), old_vis3d = cl.IsVisibleIn3D(); cl.SetVisible(m_ChkVisibility->value() == 0); cl.SetVisibleIn3D(m_ChkMeshVisibility->value() == 0); if((!old_vis3d && cl.IsVisibleIn3D()) || (!old_visible && cl.IsVisible())) { m_Parent->OnIRISMeshDisplaySettingsUpdate(); } // Store the new label SetColorLabel(iCurrentLabel, cl); // Update the label list // OnLabelListUpdate(iCurrentLabel); // Update the text in the browser std::ostringstream oss; oss << std::setw(3) << iCurrentLabel << ": " << cl.GetLabel(); m_BrsLabelList->text(m_BrsLabelList->value(), oss.str().c_str()); // Update the list in the parent UI m_Parent->OnLabelListUpdate(); } void LabelEditorUILogic ::OnMoveUpAction() { } void LabelEditorUILogic ::OnMoveDownAction() { } void LabelEditorUILogic ::OnLabelSelectAction() { SetEditorLabel(GetSelectedLabelId()); } const size_t LabelEditorUILogic ::TOOL_PAGE_LABEL_MENU_COUNT = 5; void LabelEditorUILogic ::OnToolsDialogAction() { // Flags decribing whether each dropdown includes the clear label int flagIncludeClear[TOOL_PAGE_LABEL_MENU_COUNT] = { 0, 1, 1, 0, 0 }; int flagInitToCurrent[TOOL_PAGE_LABEL_MENU_COUNT] = { 0, 1, 1, 0, 1 }; int flagInitToDrawOver[TOOL_PAGE_LABEL_MENU_COUNT] = { 1, 0, 0, 1, 0 }; // Initialize the menus m_ToolPageLabelMenu = new Fl_Menu_Item*[TOOL_PAGE_LABEL_MENU_COUNT]; // Populate the label dropdowns for(size_t i = 0; i < TOOL_PAGE_LABEL_MENU_COUNT; i++) { // Populate the dropdown m_ToolPageLabelMenu[i] = m_Parent->GenerateColorLabelMenu( false, false, flagIncludeClear[i]); m_InToolPageLabel[i]->menu(m_ToolPageLabelMenu[i]); // Find an item to select LabelType selected = this->GetSelectedLabelId(); LabelType drawover = m_Driver->GetGlobalState()->GetOverWriteColorLabel(); for(Fl_Menu_Item *p = m_ToolPageLabelMenu[i]; p->text; ++p) { LabelType id = (LabelType)(size_t)p->user_data(); if(flagInitToCurrent[i] && id == selected) m_InToolPageLabel[i]->value(p); else if(flagInitToDrawOver[i] && id == drawover) m_InToolPageLabel[i]->value(p); } } // Show the dialog UserInterfaceLogic::CenterChildWindowInParentWindow(m_WinTools, m_WinMain); m_WinTools->show(); } #include "itkTopologyPreservingDigitalSurfaceEvolutionImageFilter.h" #include "itkBinaryThresholdImageFilter.h" void LabelEditorUILogic ::OnToolsApplyAction() { // Relabeling operation if(m_InToolsOperation->value() == 0) { // Get the labels LabelType src = (LabelType) (size_t) m_InToolPageLabel[0]->mvalue()->user_data(); LabelType trg = (LabelType) (size_t) m_InToolPageLabel[1]->mvalue()->user_data(); // Do the simple merge size_t nChanged = m_Driver->ReplaceLabel(trg, src); if(nChanged == 0) fl_alert("No voxels were affected by this operation!"); else { m_Parent->StoreUndoPoint("Relabeling"); m_Parent->OnSegmentationImageUpdate(false); } } // Topological merge operation else if(m_InToolsOperation->value() == 1) { // Get the segmentation image typedef itk::OrientedImage LabelImageType; LabelImageType::Pointer iSeg = m_Driver->GetCurrentImageData()->GetSegmentation()->GetImage(); // Get the labels LabelType l_src = (LabelType) (size_t) m_InToolPageLabel[0]->mvalue()->user_data(); LabelType l_trg = (LabelType) (size_t) m_InToolPageLabel[1]->mvalue()->user_data(); // Extract binary images for each label typedef itk::BinaryThresholdImageFilter ThreshType; ThreshType::Pointer tsrc = ThreshType::New(); tsrc->SetInput(iSeg); tsrc->SetLowerThreshold(l_src); tsrc->SetUpperThreshold(l_src); tsrc->SetInsideValue(1); tsrc->SetOutsideValue(0); tsrc->Update(); ThreshType::Pointer ttrg = ThreshType::New(); ttrg->SetInput(iSeg); ttrg->SetLowerThreshold(l_trg); ttrg->SetUpperThreshold(l_trg); ttrg->SetInsideValue(1); ttrg->SetOutsideValue(0); ttrg->Update(); // Create a progress object typedef itk::MemberCommand CommandType; CommandType::Pointer pcom = CommandType::New(); pcom->SetCallbackFunction(m_Parent, &UserInterfaceBase::OnITKProgressEvent); // Create the evolution filter typedef itk::TopologyPreservingDigitalSurfaceEvolutionImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(tsrc->GetOutput()); filter->SetTargetImage(ttrg->GetOutput()); filter->SetNumberOfIterations(100); filter->SetForegroundValue(1); filter->SetUseInversionMode(false); filter->InPlaceOn(); filter->AddObserver(itk::AnyEvent(), pcom); filter->Update(); // Merge back into the segmentation typedef itk::ImageRegionIterator IteratorType; IteratorType itseg(iSeg, iSeg->GetBufferedRegion()); IteratorType itnew(filter->GetOutput(), iSeg->GetBufferedRegion()); size_t nChanged = 0; while(!itseg.IsAtEnd()) { if(itnew.Get()) { itseg.Set(l_src); ++nChanged; } ++itseg; ++itnew; } // Let parent know something happened if(nChanged) { iSeg->Modified(); m_Parent->StoreUndoPoint("Topological Merge"); m_Parent->OnSegmentationImageUpdate(false); } else { fl_alert("No voxel labels were changed"); } } // Topological merge operation else if(m_InToolsOperation->value() == 2) { fl_alert("This operation is not yet implemented"); } } void LabelEditorUILogic ::OnToolsOperationChange() { m_WizToolControls->value( m_WizToolControls->child(m_InToolsOperation->value())); } void LabelEditorUILogic ::OnToolsCloseAction() { // Empty all menus for(size_t i = 0; i < TOOL_PAGE_LABEL_MENU_COUNT; i++) { // Populate the dropdown m_InToolPageLabel[i]->clear(); m_Parent->DeleteColorLabelMenu(m_ToolPageLabelMenu[i]); m_ToolPageLabelMenu[i] = NULL; } delete m_ToolPageLabelMenu; m_WinTools->hide(); } itksnap/UserInterface/MainComponents/LabelEditorUILogic.h0000644000076500000240000000577011110344057023015 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LabelEditorUILogic.h,v $ Language: C++ Date: $Date: 2008/11/17 19:38:23 $ 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 __LabelEditorUILogic #define __LabelEditorUILogic #include "LabelEditorUI.h" #include "ColorLabel.h" #include #include // Forward references to some classes class IRISApplication; class GlobalState; class UserInterfaceBase; /** * \class LabelEditorUILogic * \brief Logic class for Label editor UI logic */ class LabelEditorUILogic : public LabelEditorUI { public: LabelEditorUILogic(); virtual ~LabelEditorUILogic(); /** Register with the parent user interface */ void Register(UserInterfaceBase *parent); /** Set the active label */ void SetEditorLabel(unsigned int iLabel); /** Respond to an update in the list of labels, setting the current * label to iLabel */ void OnLabelListUpdate(unsigned int iCurrentLabel); /** Display the editor window */ void DisplayWindow(); // Callbacks from the user interface void OnNewAction(); void OnDuplicateAction(); void OnDeleteAction(); void OnSetIdAction(); void OnMoveUpAction(); void OnMoveDownAction(); void OnCloseAction(); void OnLabelSelectAction(); void OnLabelPropertyChange(); void OnToolsDialogAction(); void OnToolsApplyAction(); void OnToolsCloseAction(); void OnToolsOperationChange(); private: UserInterfaceBase *m_Parent; GlobalState *m_GlobalState; IRISApplication *m_Driver; // Internal menus static const size_t TOOL_PAGE_LABEL_MENU_COUNT; Fl_Menu_Item **m_ToolPageLabelMenu; // Internal functions int FindSpaceForNewLabel(); size_t GetSelectedLabelId(); // Quick access to color labels const ColorLabel &GetColorLabel(size_t iLabel); void SetColorLabel(size_t iLabel, const ColorLabel &xLabel); }; #endif itksnap/UserInterface/MainComponents/LayerInspectorUI.fl0000644000076500000240000004134011405725000022755 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0109 use_FL_COMMAND header_name {.h} code_name {.cxx} class LayerInspectorUI {open : LayerInspectorUIBase } { Function {MakeWindow()} {open } { Fl_Window m_WinLayerUI { label {ITK-SNAP: Layer Inspector} open xywh {688 132 584 485} type Double box PLASTIC_DOWN_BOX resizable code0 {\#include "LayerInspectorUIBase.h"} visible } { Fl_Tabs m_LayerUITabs { callback {this->DisplayWindow();} open xywh {180 5 390 475} box PLASTIC_THIN_UP_BOX color 41 } { Fl_Group m_ImageContrastTab { label Contrast open xywh {180 25 390 455} labelsize 12 hide } { Fl_Group {} { label {Window and Level:} open xywh {190 55 370 45} box PLASTIC_DOWN_BOX color 55 labelfont 1 labelsize 12 align 5 } { Fl_Value_Input m_InLevel { label {Level:} callback {this->OnWindowLevelChange();} xywh {235 65 70 25} labelsize 12 when 4 step 0.01 textsize 12 } Fl_Value_Input m_InWindow { label {Window:} callback {this->OnWindowLevelChange();} xywh {370 65 70 25} labelsize 12 when 4 step 0.01 textsize 12 } Fl_Button {} { label {&Auto} callback {this->OnAutoFitWindow();} tooltip {Automatically fit window and level based on the image histogram} xywh {475 65 70 25} box PLASTIC_UP_BOX shortcut 0x80061 labelsize 12 } } Fl_Group {} { label {Curve-Based Adjustment:} open xywh {190 125 370 250} box PLASTIC_DOWN_BOX labelfont 1 labelsize 12 align 5 } { Fl_Box m_BoxCurve { label {Curve Display Area} xywh {210 135 340 180} box BORDER_BOX color 246 labelfont 1 labelsize 12 align 16 code0 {\#include "IntensityCurveBox.h"} class IntensityCurveBox } Fl_Button {} { label {&Reset} callback {this->OnCurveReset();} tooltip {Reset the curve, making it linear} xywh {500 340 50 25} box PLASTIC_UP_BOX shortcut 0x80072 labelsize 12 } Fl_Group {} { label {input image intensity} open xywh {210 315 340 10} labelsize 11 align 24 } {} Fl_Group {} {open image {Artwork/outputintensity.gif} xywh {198 135 15 125} align 24 } {} Fl_Button {} { label {+} callback {this->OnControlPointMoreAction();} tooltip {Insert a control point into the curve} xywh {440 340 25 25} box PLASTIC_UP_BOX shortcut 0x3d labelfont 4 labelsize 18 } Fl_Button m_BtnCurveLessControlPoint { label {-} callback {this->OnControlPointLessAction();} tooltip {Delete the current control point} xywh {470 340 25 25} box PLASTIC_UP_BOX shortcut 0x2d labelfont 4 labelsize 18 } Fl_Value_Input m_InControlX { label {Input Intensity:} callback {this->OnControlPointTextBoxUpdate();} tooltip {Image intensity (x coordinate) of the selected control point} xywh {210 340 90 25} labelsize 12 align 5 when 4 step 0.01 textsize 12 } Fl_Value_Input m_InControlY { label {Output Intensity:} callback {this->OnControlPointTextBoxUpdate();} tooltip {Y coordinate of the selected control point (between 0 and 1)} xywh {315 340 90 25} labelsize 12 align 5 when 4 step 0.01 textsize 12 } } Fl_Group {} { label {Histogram Options:} open xywh {190 400 370 45} box PLASTIC_DOWN_BOX color 55 labelfont 1 labelsize 12 align 5 } { Fl_Value_Input m_InHistogramMaxLevel { label {Cutoff (%):} callback {this->OnUpdateHistogram();} tooltip {Affects vertical scaling of the histogram. Use it when histogram is dominated by one or two very tall bars.} xywh {385 410 55 25} labelsize 12 when 4 maximum 100 step 0.5 value 100 textsize 12 } Fl_Value_Input m_InHistogramBinSize { label {Bin size:} callback {this->OnUpdateHistogram();} xywh {250 410 55 25} labelsize 12 when 4 minimum 1 maximum 65535 step 1 value 5 textsize 12 } Fl_Check_Button m_ChkHistogramLog { label {Log scale} callback {this->OnUpdateHistogram();} tooltip {Toggle between linear and logarithmic scale for the histogram y axis} xywh {470 410 80 25} down_box DOWN_BOX labelsize 12 } Fl_Button {} { label {Update Histogram} callback {this->OnUpdateHistogram();} xywh {445 440 115 5} box PLASTIC_UP_BOX shortcut 0xff1b selection_color 38 labelsize 12 hide deactivate } } } Fl_Group m_ColorMapTab { label {Color Map} open xywh {180 25 390 455} labelsize 12 hide } { Fl_Group {} { label {Color Map Editor:} open xywh {190 135 370 200} box PLASTIC_DOWN_BOX labelfont 1 labelsize 12 align 5 } { Fl_Box m_BoxColorMap { label {OpenGL Colormap Display} xywh {200 143 350 69} box BORDER_BOX color 246 labelfont 1 labelsize 12 code0 {\#include "ColorMapWidget.h"} class ColorMapWidget } Fl_Box m_InColorMapRGBA { label {color chooser} callback {this->OnColorMapRGBAUpdate()} xywh {330 219 220 107} box BORDER_BOX labelsize 11 code0 {\#include } class Fl_Color_Chooser } Fl_Value_Input m_InColorMapIndex { label {Color Index:} callback {this->OnColorMapIndexUpdate();} tooltip {The x-coordinate of the selected control point. This corresponds to the y-coordinate of the contrast adjustment curve.} xywh {200 235 65 25} labelsize 12 align 5 when 4 step 0.01 textsize 12 } Fl_Choice m_InColorMapSide { label {Side:} callback {this->OnColorMapSideUpdate();} open tooltip {Color map may be discontinuous at control points. Select 'Left' or 'Right' to introduce a discontinuity. Select 'Both' to make a control point continuous.} xywh {200 280 70 25} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } { MenuItem m_MenuColorMapBoth { label Both xywh {5 5 31 20} labelsize 12 } MenuItem m_MenuColorMapLeft { label Left xywh {15 15 31 20} labelsize 12 } MenuItem m_MenuColorMapRight { label Right xywh {25 25 31 20} labelsize 12 } } Fl_Button m_BtnColorMapDeletePoint { label delete callback {this->OnColorMapPointDelete();} tooltip {Delete the current control point} xywh {275 235 45 25} box PLASTIC_UP_BOX shortcut 0x2d labelsize 10 } } Fl_Group {} { label {Presets:} open xywh {190 55 370 55} box PLASTIC_DOWN_BOX labelfont 1 labelsize 12 align 5 } { Fl_Button {} { label {+} callback {this->OnColorMapAddPresetAction();} tooltip {Save current color map as a preset for future use} xywh {495 75 25 25} box PLASTIC_UP_BOX shortcut 0x3d labelfont 4 labelsize 18 } Fl_Button {} { label {-} callback {this->OnColorMapDeletePresetAction();} tooltip {Delete selected preset} xywh {525 75 25 25} box PLASTIC_UP_BOX shortcut 0x2d labelfont 4 labelsize 18 } Fl_Choice m_InColorMapPreset { label {Select a color map::} callback {this->OnColorMapPresetUpdate()} open xywh {200 75 280 25} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } {} } } Fl_Group m_ImageInfoTab { label Information open selected xywh {180 25 390 455} labelsize 12 } { Fl_Group {} { label Dimensions open xywh {190 59 370 1} box BORDER_FRAME color 8 labelfont 1 labelsize 12 align 5 } {} Fl_Value_Output {m_OutImageInfoDimensions[0]} { label {x:} xywh {325 65 65 25} labelsize 12 textsize 12 } Fl_Value_Output {m_OutImageInfoDimensions[1]} { label {y:} xywh {410 65 65 25} labelsize 12 textsize 12 } Fl_Value_Output {m_OutImageInfoDimensions[2]} { label {z:} xywh {495 65 65 25} labelsize 12 textsize 12 } Fl_Group {} { label {Voxel Spacing (mm)} open xywh {190 114 370 1} box BORDER_FRAME color 8 labelfont 1 labelsize 12 align 5 } {} Fl_Value_Output {m_OutImageInfoSpacing[0]} { label {x:} xywh {325 120 65 25} labelsize 12 step 4 textsize 12 code0 {\#include "SNAPValueOutput.h"} class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoSpacing[1]} { label {y:} xywh {410 120 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoSpacing[2]} { label {z:} xywh {495 120 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Group {} { label {Origin and Orientation} open xywh {190 169 370 1} box BORDER_FRAME color 8 labelfont 1 labelsize 12 align 5 } {} Fl_Value_Output {m_OutImageInfoOrigin[0]} { label {x:} xywh {325 175 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoOrigin[1]} { label {y:} xywh {410 175 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoOrigin[2]} { label {z:} xywh {495 175 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Output m_OutImageInfoOriginRAICode { label {RAI Code:} xywh {410 206 150 23} color 49 labelsize 12 textsize 12 } Fl_Group {} { label {Crosshair Position} open xywh {190 254 370 1} box BORDER_FRAME color 8 labelfont 1 labelsize 12 align 5 } {} Fl_Value_Output {m_OutImageInfoCursorPosition[0]} { label {x:} xywh {325 290 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoCursorPosition[1]} { label {y:} xywh {410 290 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoCursorPosition[2]} { label {z:} xywh {495 290 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoCursorNIFTIPosition[0]} { label {x:} xywh {325 320 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoCursorNIFTIPosition[1]} { label {y:} xywh {410 320 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoCursorNIFTIPosition[2]} { label {z:} xywh {495 320 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Input {m_InImageInfoCursorIndex[0]} { label {x:} callback {this->OnImageInformationVoxelCoordinatesUpdate();} xywh {325 260 65 25} labelsize 12 textsize 12 } Fl_Value_Input {m_InImageInfoCursorIndex[1]} { label {y:} callback {this->OnImageInformationVoxelCoordinatesUpdate();} xywh {410 260 65 25} labelsize 12 textsize 12 } Fl_Value_Input {m_InImageInfoCursorIndex[2]} { label {z:} callback {this->OnImageInformationVoxelCoordinatesUpdate();} xywh {495 260 65 25} labelsize 12 textsize 12 } Fl_Group {} {open xywh {300 260 15 24} } {} Fl_Group {} { label {Voxel coordinates} open xywh {210 260 90 25} labelsize 11 labelcolor 30 align 20 } {} Fl_Group {} { label {World (ITK) coordinates} open xywh {210 290 90 25} labelsize 11 labelcolor 30 align 20 } {} Fl_Group {} { label {World (NIFTI) coordinates} open xywh {210 320 90 25} labelsize 11 labelcolor 30 align 20 } {} Fl_Group {} { label {Intensity Range} open xywh {190 374 370 1} box BORDER_FRAME color 8 labelfont 1 labelsize 12 align 5 } {} Fl_Value_Output {m_OutImageInfoRange[0]} { label {min:} xywh {325 380 95 25} labelsize 12 step 6 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoRange[1]} { label {max:} xywh {465 380 95 25} labelsize 12 step 6 textsize 12 class SNAPValueOutput } Fl_Group {} { label {Voxel Under the Cursor} open xywh {190 429 370 1} box BORDER_FRAME color 8 labelfont 1 labelsize 12 align 5 } {} Fl_Wizard m_WizImageInfoVoxelValue {open xywh {200 435 360 25} box NO_BOX } { Fl_Group m_GrpWizImageInfoVoxelPageGray {open xywh {200 435 350 25} hide } { Fl_Value_Output m_OutImageInfoVoxelGray { label {Intensity:} xywh {455 435 95 25} labelsize 12 step 6 textsize 12 class SNAPValueOutput } } Fl_Group m_GrpWizImageInfoVoxelPageRGB {open xywh {200 435 360 25} } { Fl_Value_Output {m_OutImageInfoVoxelRGB[0]} { label {R:} xywh {325 435 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoVoxelRGB[1]} { label {G:} xywh {410 435 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } Fl_Value_Output {m_OutImageInfoVoxelRGB[2]} { label {B:} xywh {495 435 65 25} labelsize 12 step 4 textsize 12 class SNAPValueOutput } } } } Fl_Group m_ImageAdvancedInfoTab { label {Advanced Info} open xywh {180 25 390 455} labelsize 12 hide } { Fl_Box m_TableMetaData { label label xywh {190 58 370 200} selection_color 246 labeltype NO_LABEL code0 {\#include "MetaDataTable.h"} class MetaDataTable } Fl_Group {} { label {Image Header Contents} open xywh {190 54 370 1} box BORDER_FRAME color 8 labelfont 1 labelsize 12 align 5 } {} } } Fl_Group {} {open xywh {0 0 170 490} box FLAT_BOX labeltype ENGRAVED_LABEL labelsize 16 align 17 resizable } { Fl_Group {} { label {Layer Inspector} open xywh {10 25 150 1} box FLAT_BOX color 0 labeltype EMBOSSED_LABEL labelfont 1 labelcolor 136 align 5 } {} Fl_Value_Slider m_InOverallOpacity { label {Overall opacity:} callback {this->OnOverallOpacityUpdate()} xywh {10 185 150 20} type Horizontal labelsize 12 align 5 textsize 11 } Fl_Button {} { label Close callback {this->OnCloseAction()} xywh {45 455 80 25} box PLASTIC_UP_BOX color 139 labelfont 1 labelsize 12 } Fl_Browser m_BrsLayers { label {Image Layers:} callback {this->OnLayerSelectionUpdate()} xywh {10 60 150 100} type Hold labelsize 12 align 5 textsize 11 } Fl_Group {} { label {Use Q,W,E keys to adjust} open xywh {10 205 150 25} labelsize 10 labelcolor 96 align 17 } {} } Fl_Group {} {open xywh {170 0 1 490} box FLAT_BOX color 0 } {} } } } itksnap/UserInterface/MainComponents/LayerInspectorUIBase.h0000644000076500000240000000536011254242201023377 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LayerInspectorUIBase.h,v $ Language: C++ Date: $Date: 2009/09/16 20:03:13 $ Version: $Revision: 1.6 $ 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 __LayerInspectorUIBase_h_ #define __LayerInspectorUIBase_h_ /** * \class LayerInspectorUIBase * \brief Base class for Layer editor UI logic */ class LayerInspectorUIBase { public: virtual ~LayerInspectorUIBase() {} // Callbacks for the main pane virtual void DisplayWindow() = 0; virtual void OnLayerSelectionUpdate() = 0; virtual void OnOverallOpacityUpdate() = 0; virtual void OnCloseAction() = 0; // Callbacks for the contrast adjustment page virtual void DisplayImageContrastTab() = 0; virtual void OnCurveReset() = 0; virtual void OnAutoFitWindow() = 0; virtual void OnWindowLevelChange() = 0; virtual void OnUpdateHistogram() = 0; virtual void OnControlPointMoreAction() = 0; virtual void OnControlPointLessAction() = 0; virtual void OnControlPointTextBoxUpdate() = 0; virtual void OnControlPointUpdate() = 0; // Callbacks for the color map page virtual void DisplayColorMapTab() = 0; virtual void OnColorMapPresetUpdate() = 0; virtual void OnColorMapAddPresetAction() = 0; virtual void OnColorMapDeletePresetAction() = 0; virtual void OnColorMapIndexUpdate() = 0; virtual void OnColorMapSideUpdate() = 0; virtual void OnColorMapPointDelete() = 0; virtual void OnColorMapRGBAUpdate() = 0; // Callbacks for the image info page virtual void DisplayImageInfoTab() = 0; virtual void OnImageInformationVoxelCoordinatesUpdate() = 0; }; #endif itksnap/UserInterface/MainComponents/LayerInspectorUILogic.cxx0000644000076500000240000007271211553051537024156 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LayerInspectorUILogic.cxx,v $ Language: C++ Date: $Date: 2011/04/18 15:06:07 $ Version: $Revision: 1.27 $ 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 "LayerInspectorUILogic.h" #include "IntensityCurveVTK.h" #include "UserInterfaceLogic.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "GreyImageWrapper.h" LayerInspectorUILogic ::LayerInspectorUILogic(UserInterfaceLogic *parent) { m_Parent = parent; m_Driver = m_Parent->GetDriver(); m_MainWrapper = NULL; m_OverlayWrappers = NULL; m_SelectedWrapper = NULL; m_GreyWrapper = NULL; m_Curve = NULL; } void LayerInspectorUILogic ::Initialize() { // Color map m_BoxColorMap->SetParent(this); PopulateColorMapPresets(); // Intensity curve m_BoxCurve->SetParent(this); // Opacity m_InOverallOpacity->maximum(255); m_InOverallOpacity->minimum(0); m_InOverallOpacity->step(1); } void LayerInspectorUILogic ::SetImageWrappers() { // Connect the image wrappers m_MainWrapper = m_Driver->GetCurrentImageData()->GetMain(); m_OverlayWrappers = m_Driver->GetCurrentImageData()->GetOverlays(); // Build layer browser entries m_BrsLayers->clear(); GreyImageWrapper *wrapper = dynamic_cast(m_MainWrapper); if (wrapper) m_BrsLayers->add("Main Image (Greyscale)"); else m_BrsLayers->add("Main Image (RGB)"); WrapperIterator it = m_OverlayWrappers->begin(); while (it != m_OverlayWrappers->end()) { GreyImageWrapper *wrapper = dynamic_cast(*it); if (wrapper) m_BrsLayers->add("Overlay (Greyscale)"); else m_BrsLayers->add("Overlay (RGB)"); ++it; } // Select the last loaded image by default m_BrsLayers->select(1 + m_OverlayWrappers->size()); OnLayerSelectionUpdate(); } void LayerInspectorUILogic ::DisplayWindow() { if (m_LayerUITabs->value() == m_ImageContrastTab) { DisplayImageContrastTab(); } else if (m_LayerUITabs->value() == m_ColorMapTab) { DisplayColorMapTab(); } else if (m_LayerUITabs->value() == m_ImageInfoTab) { DisplayImageInfoTab(); } else if (m_LayerUITabs->value() == m_ImageAdvancedInfoTab) { DisplayImageAdvancedInfoTab(); } } void LayerInspectorUILogic ::DisplayImageContrastTab() { m_BoxColorMap->hide(); m_WinLayerUI->show(); m_LayerUITabs->value(m_ImageContrastTab); if (m_ImageContrastTab->active()) { UpdateWindowAndLevel(); m_BoxCurve->redraw(); m_BoxCurve->show(); } } void LayerInspectorUILogic ::DisplayColorMapTab() { m_BoxCurve->hide(); m_WinLayerUI->show(); m_LayerUITabs->value(m_ColorMapTab); if (m_ColorMapTab->active()) { // Determine the currently used color map ColorMap cm = m_GreyWrapper->GetColorMap(); // Update the GUI m_InColorMapPreset->value(cm.GetSystemPreset()); m_BoxColorMap->SetColorMap(cm); m_BoxColorMap->redraw(); m_BoxColorMap->show(); this->OnColorMapSelectedPointUpdate(); } } void LayerInspectorUILogic ::DisplayImageInfoTab() { m_BoxCurve->hide(); m_BoxColorMap->hide(); m_WinLayerUI->show(); m_LayerUITabs->value(m_ImageInfoTab); UpdateImageProbe(); } void LayerInspectorUILogic ::DisplayImageAdvancedInfoTab() { m_BoxCurve->hide(); m_BoxColorMap->hide(); m_WinLayerUI->show(); m_LayerUITabs->value(m_ImageAdvancedInfoTab); } void LayerInspectorUILogic ::RedrawWindow() { // Intensity curve if (m_LayerUITabs->value() == m_ImageContrastTab && m_ImageContrastTab->active()) { UpdateWindowAndLevel(); m_BoxCurve->redraw(); m_BoxCurve->show(); } // Color map else if (m_LayerUITabs->value() == m_ColorMapTab && m_ColorMapTab->active()) { // Determine the currently used color map ColorMap cm = m_GreyWrapper->GetColorMap(); // Update the GUI m_InColorMapPreset->value(cm.GetSystemPreset()); m_BoxColorMap->SetColorMap(cm); m_BoxColorMap->redraw(); m_BoxColorMap->show(); } // Image info else if (m_LayerUITabs->value() == m_ImageInfoTab) { UpdateImageProbe(); } else if (m_LayerUITabs->value() == m_ImageAdvancedInfoTab) { } } bool LayerInspectorUILogic ::Shown() { return m_WinLayerUI->shown(); } // Callbacks for the main pane void LayerInspectorUILogic ::OnLayerSelectionUpdate() { // If user click the empty space in the browser window if (m_BrsLayers->value() == 0) return; // Determine the corresponding image wrapper if (m_BrsLayers->value() == 1) { m_SelectedWrapper = m_MainWrapper; m_InOverallOpacity->deactivate(); } else { WrapperIterator it = m_OverlayWrappers->begin(); for (int i = 2; i < m_BrsLayers->value(); ++i) ++it; assert(it != m_OverlayWrappers->end()); m_SelectedWrapper = *it; m_InOverallOpacity->activate(); } // Overall Opacity m_InOverallOpacity->value(m_SelectedWrapper->GetAlpha()); // Hook it up with the GUI m_GreyWrapper = dynamic_cast(m_SelectedWrapper); if (m_GreyWrapper) { m_ImageContrastTab->activate(); m_ColorMapTab->activate(); // associate with intensity curve UI m_Curve = m_GreyWrapper->GetIntensityMapFunction(); if (m_Curve->GetControlPointCount() == 3) m_BtnCurveLessControlPoint->deactivate(); m_BoxCurve->SetCurve(m_Curve); m_BoxCurve->SetHistogramBinSize(1); m_BoxCurve->ComputeHistogram(m_GreyWrapper, 4); m_InHistogramMaxLevel->value(m_BoxCurve->GetHistogramMaxLevel() * 100.f); m_InHistogramBinSize->value(m_BoxCurve->GetHistogramBinSize()); m_ChkHistogramLog->value(m_BoxCurve->IsHistogramLog()); // associate with color map UI (if needed) // associate with image info: greyscale specific m_OutImageInfoRange[0]->value(m_GreyWrapper->GetImageMinNative()); m_OutImageInfoRange[1]->value(m_GreyWrapper->GetImageMaxNative()); } else { m_ImageContrastTab->deactivate(); m_BoxCurve->hide(); m_ColorMapTab->deactivate(); m_BoxColorMap->hide(); // associate with image info: RGB specific m_OutImageInfoRange[0]->value(0); m_OutImageInfoRange[1]->value(0); } // associate with image info: common for (unsigned int d = 0; d < 3; ++d) { m_OutImageInfoDimensions[d]->value(m_SelectedWrapper->GetSize()[d]); m_OutImageInfoOrigin[d]->value(m_SelectedWrapper->GetImageBase()->GetOrigin()[d]); m_OutImageInfoOrigin[d]->maximum(m_OutImageInfoOrigin[d]->value()); m_OutImageInfoOrigin[d]->minimum(m_OutImageInfoOrigin[d]->value()); m_OutImageInfoSpacing[d]->value(m_SelectedWrapper->GetImageBase()->GetSpacing()[d]); m_OutImageInfoSpacing[d]->maximum(m_OutImageInfoSpacing[d]->value()); m_OutImageInfoSpacing[d]->minimum(m_OutImageInfoSpacing[d]->value()); m_InImageInfoCursorIndex[d]->maximum(m_SelectedWrapper->GetSize()[d] - 1); m_InImageInfoCursorIndex[d]->minimum(0); m_InImageInfoCursorIndex[d]->step(1); } // get the RAI code ImageCoordinateGeometry::DirectionMatrix dmat = m_Driver->GetCurrentImageData()->GetImageGeometry().GetImageDirectionCosineMatrix(); string raicode = ImageCoordinateGeometry::ConvertDirectionMatrixToClosestRAICode(dmat); string raitext; if (ImageCoordinateGeometry::IsDirectionMatrixOblique(dmat)) raitext = string("Oblique (closest to ") + raicode + string(")"); else raitext = raicode; m_OutImageInfoOriginRAICode->value(raitext.c_str()); // Uodate the information table m_TableMetaData->SetInputImage(m_SelectedWrapper->GetImageBase()); m_TableMetaData->SetColumnWidth(370); // update GUI if (Shown()) RedrawWindow(); } void LayerInspectorUILogic ::OnOverallOpacityUpdate() { m_SelectedWrapper->SetAlpha((unsigned char) m_InOverallOpacity->value()); m_Parent->RedrawWindows(); } void LayerInspectorUILogic ::AdjustOverlayOpacity(double delta) { // If the selected wrapper is an overlay, adjust it's opacity if(m_SelectedWrapper != m_MainWrapper) { double alpha = (double) m_SelectedWrapper->GetAlpha() + delta; if(alpha < 0) alpha = 0; if(alpha > 255) alpha = 255; m_SelectedWrapper->SetAlpha((unsigned char) alpha); m_InOverallOpacity->value(alpha); } // Otherwise, adjust all the overlays else { for(WrapperList::iterator it = m_OverlayWrappers->begin(); it != m_OverlayWrappers->end(); it++) { double alpha = (double) (*it)->GetAlpha() + delta; if(alpha < 0) alpha = 0; if(alpha > 255) alpha = 255; (*it)->SetAlpha((unsigned char) alpha); } } m_Parent->RedrawWindows(); } void LayerInspectorUILogic ::ToggleOverlayVisibility() { // If the selected wrapper is an overlay, adjust it's opacity if(m_SelectedWrapper != m_MainWrapper) { // Get current alpha value m_SelectedWrapper->ToggleVisibility(); m_InOverallOpacity->value(m_SelectedWrapper->GetAlpha()); } // Otherwise, adjust all the overlays else { for(WrapperList::iterator it = m_OverlayWrappers->begin(); it != m_OverlayWrappers->end(); it++) { (*it)->ToggleVisibility(); } } m_Parent->RedrawWindows(); } void LayerInspectorUILogic ::OnCloseAction() { m_WinLayerUI->hide(); } // Callbacks for the contrast adjustment page void LayerInspectorUILogic ::OnCurveReset() { // Reinitialize the curve m_Curve->Initialize(); // Update the controlX/controlY OnControlPointUpdate(); // Fire the reset event OnCurveChange(); } void LayerInspectorUILogic ::UpdateWindowAndLevel() { // Need a curve and a wrapper assert(m_Curve && m_GreyWrapper); // This is the range of the curve in unit coordinates (0 to 1) float t0,x0,t1,x1; // Get the starting and ending control points m_Curve->GetControlPoint(0,t0,x0); m_Curve->GetControlPoint(m_Curve->GetControlPointCount()-1,t1,x1); // Get 'absolute' image intensity range, i.e., the largest and smallest // intensity in the whole image double iAbsMin = m_GreyWrapper->GetImageMinNative(); double iAbsMax = m_GreyWrapper->GetImageMaxNative(); double iAbsSpan = (iAbsMax - iAbsMin); // The the curve intensity range double iMin = iAbsMin + iAbsSpan * t0; double iMax = iAbsMin + iAbsSpan * t1; // Compute the level and window in intensity units double level = iMin; double window = iMax - iMin; // The step is set dynamically, so that there are on the order of // 1000 steps to cover the whole intensity range. That should give // a good tradeoff between precision and control. double step = pow(10, floor(0.5 + log10(iAbsSpan) - 3)); m_InWindow->step(step); m_InWindow->step(step); m_InControlX->step(step); // Compute and constrain the level m_InLevel->value(level); m_InLevel->minimum(iAbsMin); m_InLevel->maximum(iAbsMax - window); // Compute and constrain the window m_InWindow->value(window); m_InWindow->minimum(step); m_InWindow->maximum(iAbsMax - level); // In addition to window and level, we set up the valuators for // the current control point int cp = m_BoxCurve->GetInteractor()->GetMovingControlPoint(); if(cp >= 0) { if(cp == 0) { m_InControlX->minimum(iAbsMin); } else { m_Curve->GetControlPoint(cp - 1, t0, x0); m_InControlX->minimum(iAbsMin + iAbsSpan * t0 + step); } if(cp == (int)(m_Curve->GetControlPointCount() - 1)) { m_InControlX->maximum(iAbsMax); } else { m_Curve->GetControlPoint(cp + 1, t1, x1); m_InControlX->maximum(iAbsMin + iAbsSpan * t1 - step); } if(cp == 0 || cp == (int)(m_Curve->GetControlPointCount() - 1)) { m_InControlY->deactivate(); } else { m_InControlY->activate(); m_InControlY->minimum(x0 + 0.01); m_InControlY->maximum(x1 + 0.01); m_InControlY->step(0.01); } // Set the actual value float t, x; m_Curve->GetControlPoint(cp, t, x); m_InControlX->activate(); m_InControlX->value(iAbsMin + iAbsSpan * t); m_InControlY->value(x); } else { m_InControlX->deactivate(); m_InControlY->deactivate(); } } void LayerInspectorUILogic ::OnCurveChange() { m_BoxColorMap->hide(); // Update the values of the window and level UpdateWindowAndLevel(); // Redraw the window m_BoxCurve->redraw(); m_BoxCurve->show(); // Update the image wrapper m_GreyWrapper->UpdateIntensityMapFunction(); // Redraw parent m_Parent->RedrawWindows(); } void LayerInspectorUILogic ::OnAutoFitWindow() { // Get the histogram const std::vector &hist = m_BoxCurve->GetHistogram(); // Integrate the histogram until reaching 0.1% GreyType imin = m_GreyWrapper->GetImageMin(); GreyType ilow = imin; size_t accum = 0; size_t accum_goal = m_GreyWrapper->GetNumberOfVoxels() / 1000; for(size_t i = 0; i < hist.size(); i++) { if(accum + hist[i] < accum_goal) { accum += hist[i]; ilow += m_BoxCurve->GetHistogramBinSize(); } else break; } // Same, but from above GreyType imax = m_GreyWrapper->GetImageMax(); GreyType ihigh = imax; accum = 0; for(size_t i = hist.size() - 1; i >= 0; i--) { if(accum + hist[i] < accum_goal) { accum += hist[i]; ihigh -= m_BoxCurve->GetHistogramBinSize(); } else break; } // If for some reason the window is off, we set everything to max/min if(ilow >= ihigh) { ilow = imin; ihigh = imax; } // Compute and constrain the window GreyTypeToNativeFunctor native = m_GreyWrapper->GetNativeMapping(); double ilowNative = native(ilow); double ihighNative = native(ihigh); double imaxNative = native(imax); double iwin = ihighNative - ilowNative; m_InWindow->maximum(imaxNative - ilowNative); m_InWindow->value(iwin); m_InLevel->maximum(imaxNative - iwin); m_InLevel->value(ilowNative); OnWindowLevelChange(); } void LayerInspectorUILogic ::OnWindowLevelChange() { // Assure that input and output outside of the image range // is handled gracefully // m_InLevel->value(m_InLevel->clamp(m_InLevel->value())); // m_InWindow->value(m_InWindow->clamp(m_InWindow->value())); // Get 'absolute' image intensity range, i.e., the largest and smallest // intensity in the whole image double iAbsMin = m_GreyWrapper->GetImageMinNative(); double iAbsMax = m_GreyWrapper->GetImageMaxNative(); // Get the new values of min and max double iMin = m_InLevel->value(); double iMax = iMin + m_InWindow->value(); // Min better be less than max assert(iMin < iMax); // Compute the unit coordinate values that correspond to min and max float factor = 1.0f / (iAbsMax - iAbsMin); float t0 = factor * (iMin - iAbsMin); float t1 = factor * (iMax - iAbsMin); // Update the curve boundary m_Curve->ScaleControlPointsToWindow(t0,t1); // Update the controlX/controlY OnControlPointUpdate(); // Fire the reset event OnCurveChange(); } void LayerInspectorUILogic ::OnUpdateHistogram() { // Recompute the histogram and redisplay m_BoxCurve->SetHistogramBinSize((size_t) m_InHistogramBinSize->value()); m_BoxCurve->SetHistogramMaxLevel(m_InHistogramMaxLevel->value() / 100.0f); m_BoxCurve->SetHistogramLog(m_ChkHistogramLog->value() ? true : false); m_BoxCurve->ComputeHistogram(m_GreyWrapper, 1); m_BoxCurve->redraw(); // The histogram controls may have changed. Update them m_InHistogramMaxLevel->value(m_BoxCurve->GetHistogramMaxLevel() * 100.0); m_InHistogramBinSize->value(m_BoxCurve->GetHistogramBinSize()); } void LayerInspectorUILogic ::OnControlPointMoreAction() { m_Curve->Initialize(m_Curve->GetControlPointCount() + 1); if (m_Curve->GetControlPointCount() > 3) m_BtnCurveLessControlPoint->activate(); OnWindowLevelChange(); } void LayerInspectorUILogic ::OnControlPointLessAction() { if (m_Curve->GetControlPointCount() > 3) { m_Curve->Initialize(m_Curve->GetControlPointCount() - 1); m_BoxCurve->GetInteractor()->SetMovingControlPoint(0); OnWindowLevelChange(); } if (m_Curve->GetControlPointCount() == 3) m_BtnCurveLessControlPoint->deactivate(); } void LayerInspectorUILogic ::OnControlPointTextBoxUpdate() { // Get the values of the control points int cp = m_BoxCurve->GetInteractor()->GetMovingControlPoint(); if(cp >= 0) { // Get the range of the intensity double imin = m_GreyWrapper->GetImageMinNative(); double imax = m_GreyWrapper->GetImageMaxNative(); double delta = imax - imin; // Update the point, although the curve may reject these values float tnew = (m_InControlX->value() - imin) / delta; float xnew = m_InControlY->value(); bool accept = m_BoxCurve->UpdateControlPoint((size_t) cp, tnew, xnew); // Update the text boxes with actual values if(!accept) { float told, xold; m_Curve->GetControlPoint(cp, told, xold); m_InControlX->value(told * delta + imin); m_InControlY->value(xold); } else { this->OnCurveChange(); } } } void LayerInspectorUILogic ::OnControlPointUpdate() { int cp = m_BoxCurve->GetInteractor()->GetMovingControlPoint(); float fx, fy; m_Curve->GetControlPoint(cp, fx, fy); const double min = m_GreyWrapper->GetImageMinNative(); const double max = m_GreyWrapper->GetImageMaxNative(); const double delta = max - min; m_InControlX->value(fx*delta + min); m_InControlY->value(fy); } // Callbacks for the color map page LayerInspectorUILogic::PresetInfo LayerInspectorUILogic::m_PresetInfo[] = { {"Grayscale", 0x00ff0000}, {"Jet", 0x00000000}, {"Hot", 0x00000000}, {"Cool", 0x00000000}, {"Black to red", 0x00ff0000}, {"Black to green", 0xff000000}, {"Black to blue", 0xff000000}, {"Spring", 0x00000000}, {"Summer", 0x00000000}, {"Autumn", 0x00000000}, {"Winter", 0x00000000}, {"Copper", 0x00000000}, {"HSV", 0x00000000}, {"Blue, white and red", 0x00000000}, {"Red, white and blue", 0x00000000}}; void LayerInspectorUILogic ::OnColorMapChange() { m_GreyWrapper->SetColorMap(m_BoxColorMap->GetColorMap()); m_GreyWrapper->UpdateIntensityMapFunction(); this->OnColorMapSelectedPointUpdate(); // Redraw parent m_Parent->RedrawWindows(); } void LayerInspectorUILogic ::SelectNextColorMap() { if(m_InColorMapPreset->size() <= 1) return; // Cycle through the available colormaps int val = m_InColorMapPreset->value(); int newval = ( (val < 0 ? 0 : val) + 1) % (m_InColorMapPreset->size() - 1); if(newval != val) m_InColorMapPreset->value(newval); this->OnColorMapPresetUpdate(); } void LayerInspectorUILogic ::PopulateColorMapPresets() { // Set the system presets m_InColorMapPreset->clear(); for(int i = 0; i < ColorMap::COLORMAP_SIZE; i++) { if(i == ColorMap::COLORMAP_SIZE - 1) { string text = string("_") + m_PresetInfo[i].name; m_InColorMapPreset->add(text.c_str()); } else { m_InColorMapPreset->add(m_PresetInfo[i].name.c_str()); } } // Now add the custom presets std::vector saved = m_Driver->GetSystemInterface()->GetSavedObjectNames("ColorMaps"); for(int i = 0; i < (int) saved.size(); i++) m_InColorMapPreset->add(saved[i].c_str()); } void LayerInspectorUILogic ::OnColorMapPresetUpdate() { ColorMap cm; m_BoxCurve->hide(); // Apply the current preset int sel = m_InColorMapPreset->value(); if(sel < 0) return; if(sel < ColorMap::COLORMAP_SIZE) { ColorMap::SystemPreset preset = (ColorMap::SystemPreset) (ColorMap::COLORMAP_GREY + sel); // Update the color map cm.SetToSystemPreset(preset); } else { try { Registry reg = m_Driver->GetSystemInterface()->ReadSavedObject( "ColorMaps", m_InColorMapPreset->text(sel)); cm.LoadFromRegistry(reg); } catch(IRISException &exc) { fl_alert("Failed to read preset from file: \n%s", exc.what()); return; } } m_BoxColorMap->SetColorMap(cm); m_BoxColorMap->SetSelectedCMPoint(-1); m_BoxColorMap->redraw(); m_BoxColorMap->show(); // Update the image m_GreyWrapper->SetColorMap(cm); m_GreyWrapper->UpdateIntensityMapFunction(); // Redraw parent m_Parent->RedrawWindows(); this->OnColorMapSelectedPointUpdate(); } void LayerInspectorUILogic ::OnColorMapAddPresetAction() { // What default to recommend? int sel = m_InColorMapPreset->value(); string deflt = (sel < ColorMap::COLORMAP_SIZE) ? string("My ") + m_InColorMapPreset->text(sel) : m_InColorMapPreset->text(sel); // Prompt the user for the name const char *name = fl_input("How do you want to name your preset?", deflt.c_str()); if(!name || strlen(name) == 0) return; // Create a registry Registry reg; m_BoxColorMap->GetColorMap().SaveToRegistry(reg); // Write to file m_Driver->GetSystemInterface()->UpdateSavedObject("ColorMaps",name,reg); // Refresh the list of presents PopulateColorMapPresets(); // Set the current preset as the selection m_InColorMapPreset->value(-1); for(int i = 0; i < m_InColorMapPreset->size(); i++) if(!strcmp(m_InColorMapPreset->text(i), name)) { m_InColorMapPreset->value(i); break; } } void LayerInspectorUILogic ::OnColorMapDeletePresetAction() { int sel = m_InColorMapPreset->value(); if(sel >= ColorMap::COLORMAP_SIZE) { // Delete the bad one string name = m_InColorMapPreset->text(sel); m_Driver->GetSystemInterface()->DeleteSavedObject("ColorMaps",name.c_str()); // Refresh the list of presents PopulateColorMapPresets(); // Select the next color map if(sel < m_InColorMapPreset->size()-1) m_InColorMapPreset->value(sel); else m_InColorMapPreset->value(m_InColorMapPreset->size() - 2); this->OnColorMapPresetUpdate(); } } void LayerInspectorUILogic ::OnColorMapSelectedPointUpdate() { // Get the selected color map point int sel = m_BoxColorMap->GetSelectedCMPoint(); ColorMapWidget::Side side = m_BoxColorMap->GetSelectedSide(); if(sel >= 0) { // Activate relevant widgets m_InColorMapSide->activate(); m_InColorMapIndex->activate(); // m_InColorMapRGBA->activate(); // Get color ColorMap &cm = this->m_BoxColorMap->GetColorMap(); ColorMap::CMPoint p = cm.GetCMPoint(sel); // Handle continuous points if(p.m_Type == ColorMap::CONTINUOUS) { // Set the RGB value m_InColorMapRGBA->rgb(p.m_RGBA[0][0] / 255.0, p.m_RGBA[0][1] / 255.0, p.m_RGBA[0][2] / 255.0); // Set the location to 'both' m_InColorMapSide->value(0); } else if(side == ColorMapWidget::LEFT) { // Set the RGB value m_InColorMapRGBA->rgb(p.m_RGBA[0][0] / 255.0, p.m_RGBA[0][1] / 255.0, p.m_RGBA[0][2] / 255.0); // Set the location to 'both' m_InColorMapSide->value(1); } else if(side == ColorMapWidget::RIGHT) { // Set the RGB value m_InColorMapRGBA->rgb(p.m_RGBA[1][0] / 255.0, p.m_RGBA[1][1] / 255.0, p.m_RGBA[1][2] / 255.0); // Set the location to 'both' m_InColorMapSide->value(2); } // Set the index value m_InColorMapIndex->value(p.m_Index); // Set the max/min of the value if(sel == 0) { m_InColorMapIndex->minimum(0.0); m_InColorMapIndex->maximum(0.0); m_MenuColorMapBoth->deactivate(); m_BtnColorMapDeletePoint->deactivate(); } else if(sel == (int) cm.GetNumberOfCMPoints() - 1) { m_InColorMapIndex->minimum(1.0); m_InColorMapIndex->maximum(1.0); m_MenuColorMapBoth->deactivate(); m_BtnColorMapDeletePoint->deactivate(); } else { m_InColorMapIndex->minimum(cm.GetCMPoint(sel-1).m_Index); m_InColorMapIndex->maximum(cm.GetCMPoint(sel+1).m_Index); m_MenuColorMapBoth->activate(); m_BtnColorMapDeletePoint->activate(); } } else { m_BtnColorMapDeletePoint->deactivate(); m_InColorMapSide->deactivate(); m_InColorMapIndex->deactivate(); // m_InColorMapRGBA->deactivate(); } } void LayerInspectorUILogic ::OnColorMapIndexUpdate() { int sel = this->m_BoxColorMap->GetSelectedCMPoint(); if(sel >= 0) { m_InColorMapIndex->value(m_InColorMapIndex->clamp(m_InColorMapIndex->value())); ColorMap::CMPoint p = this->m_BoxColorMap->GetColorMap().GetCMPoint(sel); p.m_Index = m_InColorMapIndex->value(); this->m_BoxColorMap->GetColorMap().UpdateCMPoint(sel, p); m_BoxColorMap->redraw(); this->OnColorMapChange(); } } void LayerInspectorUILogic ::OnColorMapSideUpdate() { int sel = this->m_BoxColorMap->GetSelectedCMPoint(); if(sel >= 0) { ColorMap &cm = this->m_BoxColorMap->GetColorMap(); ColorMap::CMPoint p = cm.GetCMPoint(sel); size_t val = m_InColorMapSide->value(); if(val == 0) { if(p.m_Type != ColorMap::CONTINUOUS) { // Make everything like the left p.m_Type = ColorMap::CONTINUOUS; p.m_RGBA[1] = p.m_RGBA[0]; cm.UpdateCMPoint(sel, p); m_BoxColorMap->SetSelectedSide(ColorMapWidget::BOTH); m_BoxColorMap->redraw(); this->OnColorMapChange(); } } else { if(p.m_Type == ColorMap::CONTINUOUS) { // Split the point into two p.m_Type = ColorMap::DISCONTINUOUS; cm.UpdateCMPoint(sel, p); } m_BoxColorMap->SetSelectedSide( val == 1 ? ColorMapWidget::LEFT : ColorMapWidget::RIGHT); m_BoxColorMap->redraw(); this->OnColorMapChange(); } } } void LayerInspectorUILogic ::OnColorMapPointDelete() { int sel = m_BoxColorMap->GetSelectedCMPoint(); if(sel > 0 && sel < (int)(m_BoxColorMap->GetColorMap().GetNumberOfCMPoints() - 1)) { m_BoxColorMap->SetSelectedCMPoint(-1); m_BoxColorMap->GetColorMap().DeleteCMPoint(sel); m_BoxColorMap->redraw(); this->OnColorMapChange(); } } void LayerInspectorUILogic ::OnColorMapRGBAUpdate() { int sel = this->m_BoxColorMap->GetSelectedCMPoint(); if(sel >= 0) { ColorMap::CMPoint p = this->m_BoxColorMap->GetColorMap().GetCMPoint(sel); if(p.m_Type == ColorMap::CONTINUOUS || m_InColorMapSide->value() == 1) { p.m_RGBA[0][0] = (unsigned char) (255 * m_InColorMapRGBA->r()); p.m_RGBA[0][1] = (unsigned char) (255 * m_InColorMapRGBA->g()); p.m_RGBA[0][2] = (unsigned char) (255 * m_InColorMapRGBA->b()); } if(p.m_Type == ColorMap::CONTINUOUS || m_InColorMapSide->value() == 2) { p.m_RGBA[1][0] = (unsigned char) (255 * m_InColorMapRGBA->r()); p.m_RGBA[1][1] = (unsigned char) (255 * m_InColorMapRGBA->g()); p.m_RGBA[1][2] = (unsigned char) (255 * m_InColorMapRGBA->b()); } this->m_BoxColorMap->GetColorMap().UpdateCMPoint(sel, p); m_BoxColorMap->redraw(); this->OnColorMapChange(); } } // Callbacks for the image info page void LayerInspectorUILogic ::UpdateImageProbe() { // Code common to SNAP and IRIS Vector3ui crosshairs = m_Driver->GetCursorPosition(); Vector3d xPosition = m_SelectedWrapper->TransformVoxelIndexToPosition(crosshairs); Vector3d xNIFTI = m_SelectedWrapper->TransformVoxelIndexToNIFTICoordinates(to_double(crosshairs)); for (size_t d = 0; d < 3; ++d) { m_InImageInfoCursorIndex[d]->value(crosshairs[d]); m_OutImageInfoCursorPosition[d]->value(xPosition[d]); m_OutImageInfoCursorPosition[d]->maximum( m_OutImageInfoCursorPosition[d]->value()); m_OutImageInfoCursorPosition[d]->minimum( m_OutImageInfoCursorPosition[d]->value()); m_OutImageInfoCursorNIFTIPosition[d]->value(xNIFTI[d]); m_OutImageInfoCursorNIFTIPosition[d]->maximum( m_OutImageInfoCursorNIFTIPosition[d]->value()); m_OutImageInfoCursorNIFTIPosition[d]->minimum( m_OutImageInfoCursorNIFTIPosition[d]->value()); } if (m_GreyWrapper) { m_WizImageInfoVoxelValue->value(m_GrpWizImageInfoVoxelPageGray); m_OutImageInfoVoxelGray->value(m_GreyWrapper->GetVoxelMappedToNative(crosshairs)); } else { m_WizImageInfoVoxelValue->value(m_GrpWizImageInfoVoxelPageRGB); RGBImageWrapper *rgb = dynamic_cast(m_SelectedWrapper); m_OutImageInfoVoxelRGB[0]->value(rgb->GetVoxel(crosshairs)[0]); m_OutImageInfoVoxelRGB[1]->value(rgb->GetVoxel(crosshairs)[1]); m_OutImageInfoVoxelRGB[2]->value(rgb->GetVoxel(crosshairs)[2]); } } void LayerInspectorUILogic ::OnImageInformationVoxelCoordinatesUpdate() { // Read the cursor values Vector3ui cpos; for (size_t d = 0; d < 3; ++d) { cpos[d] = (unsigned int) m_InImageInfoCursorIndex[d]->clamp( m_InImageInfoCursorIndex[d]->round( m_InImageInfoCursorIndex[d]->value())); } m_Driver->SetCursorPosition(cpos); m_Parent->OnCrosshairPositionUpdate(); m_Parent->RedrawWindows(); } itksnap/UserInterface/MainComponents/LayerInspectorUILogic.h0000644000076500000240000001005711553051537023575 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: LayerInspectorUILogic.h,v $ Language: C++ Date: $Date: 2011/04/18 15:06:07 $ Version: $Revision: 1.14 $ 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 __LayerInspectorUILogic_h_ #define __LayerInspectorUILogic_h_ #include "LayerInspectorUI.h" #include "ImageWrapper.h" class UserInterfaceLogic; class IRISApplication; class GreyImageWrapper; /** * \class LayerInspectorUILogic * \brief Logic class for Layer editor UI logic */ class LayerInspectorUILogic : public LayerInspectorUI { typedef std::list WrapperList; typedef WrapperList::iterator WrapperIterator; typedef WrapperList::const_iterator WrapperConstIterator; public: LayerInspectorUILogic(UserInterfaceLogic *); virtual ~LayerInspectorUILogic() {}; // Initialization void Initialize(); // Hook to the image wrappers void SetImageWrappers(); // Callbacks for the main pane void OnLayerSelectionUpdate(); void OnOverallOpacityUpdate(); void OnCloseAction(); // Callbacks for the contrast adjustment page void UpdateWindowAndLevel(); void OnCurveChange(); void OnCurveReset(); void OnAutoFitWindow(); void OnWindowLevelChange(); void OnUpdateHistogram(); void OnControlPointMoreAction(); void OnControlPointLessAction(); void OnControlPointTextBoxUpdate(); void OnControlPointUpdate(); // Callbacks for the color map page void OnColorMapChange(); void OnColorMapPresetUpdate(); void OnColorMapAddPresetAction(); void OnColorMapDeletePresetAction(); void OnColorMapIndexUpdate(); void OnColorMapSideUpdate(); void OnColorMapPointDelete(); void OnColorMapRGBAUpdate(); void OnColorMapSelectedPointUpdate(); // Callbacks for the image info page void UpdateImageProbe(); void OnImageInformationVoxelCoordinatesUpdate(); // Display the dialog void DisplayWindow(); void RedrawWindow(); bool Shown(); void DisplayImageContrastTab(); void DisplayColorMapTab(); void DisplayImageInfoTab(); void DisplayImageAdvancedInfoTab(); // External update to visibility of overlays void AdjustOverlayOpacity(double delta); void ToggleOverlayVisibility(); // External update to colormap void SelectNextColorMap(); protected: void PopulateColorMapPresets(); // Info about the presets struct PresetInfo { std::string name; int cline, ccirc; }; static PresetInfo m_PresetInfo[]; // The intensity curve (same pointer stored in the m_BoxCurve) IntensityCurveInterface *m_Curve; // Pointer to the Parent GUI UserInterfaceLogic *m_Parent; // Pointer to the IRIS application object IRISApplication *m_Driver; // Main image wrapper and overlay wrapper lists ImageWrapperBase *m_MainWrapper; WrapperList *m_OverlayWrappers; // Currently selected wrapper ImageWrapperBase *m_SelectedWrapper; // Grey image wrapper for intensity curve GreyImageWrapper *m_GreyWrapper; }; #endif itksnap/UserInterface/MainComponents/PreprocessingUI.fl0000644000076500000240000002217411136370276022655 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0107 header_name {.h} code_name {.cxx} class PreprocessingUI {open : {private PreprocessingUIBase} } { Function {MakeWindow()} {open } { Fl_Window m_WinInOut { label {Intensity Region Filter} open xywh {988 69 399 350} type Double box PLASTIC_DOWN_BOX code0 {\#include "PreprocessingUIBase.h"} non_modal visible } { Fl_Value_Slider m_InThresholdSteepness { label Smoothness callback {o->take_focus(); this->OnThresholdSettingsChange();} tooltip {Set the smoothness of the speed function} xywh {25 265 200 20} type Horizontal color 51 selection_color 197 labelsize 12 align 5 step 0.01 value 0.25 } Fl_Group m_GrpThresholdDirection { label {Threshold direction:} open xywh {250 175 135 70} labelsize 12 align 5 } { Fl_Round_Button m_RadioThresholdAbove { callback {this->OnThresholdDirectionChange();} xywh {260 200 120 20} type Radio down_box ROUND_DOWN_BOX } Fl_Round_Button m_RadioThresholdBelow { callback {this->OnThresholdDirectionChange();} tooltip {Select this for a one-sided threshold, where values with low intensities are treated as 'background'} xywh {260 180 120 20} type Radio down_box ROUND_DOWN_BOX } Fl_Round_Button m_RadioThresholdBoth { callback {this->OnThresholdDirectionChange();} tooltip {Select this for a two-sided threshold, where values between the lower and upper thresholds are considered 'object' and values outside the thresholds are 'background'} xywh {260 220 120 20} type Radio down_box ROUND_DOWN_BOX value 1 } Fl_Group {} { label Below open xywh {275 180 80 20} labelsize 12 align 20 } {} Fl_Group {} { label Above open tooltip {Select this for a one-sided threshold, where values with high intensities are treated as 'background'} xywh {275 200 80 20} labelsize 12 align 20 } {} Fl_Group {} { label {Below and Above} open xywh {275 220 105 20} labelsize 12 align 20 } {} } Fl_Button m_BtnThresholdOk { label Okay callback {this->OnThresholdOk();} tooltip {Accept the speed image and close the window} xywh {170 300 65 25} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x1ff0d color 180 selection_color 134 labelfont 1 labelsize 12 } Fl_Button m_BtnThresholdApply { label {&Apply} callback {this->OnThresholdApply();} tooltip {Accept the speed image, but do not close the window} xywh {245 300 65 25} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x80061 color 180 selection_color 134 labelsize 12 } Fl_Button m_BtnThresholdClose { label Close callback {this->OnThresholdClose();} selected xywh {320 300 65 25} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0xff1b color 180 selection_color 134 labelsize 12 } Fl_Check_Button m_InThresholdPreview { label {Preview result} callback {this->OnThresholdPreviewChange();} tooltip {Select this to have the main program windows update automatically as you change threshold parameters} xywh {250 245 105 25} down_box DOWN_BOX value 1 labelsize 12 } Fl_Check_Button m_InThresholdOverlay { label {Combined display} callback {this->OnThresholdOverlayChange();} tooltip {When this option is selected, the voxels where the speed function is positive will be overlaid with the current segmentation label} xywh {250 265 125 25} down_box DOWN_BOX labelsize 12 align 148 } Fl_Group {} { label {The thresholding function} open xywh {25 15 350 120} box BORDER_FRAME color 0 labelsize 11 align 2 } { Fl_Box m_BoxThresholdFunctionPlot { tooltip {This window displays a mapping from the input image intensity to the 'speed' function. The mapping is controlled by setting an upper and lower threshold. Intensities falling between the thresholds are mapped to +1 and intensities outside the threshold are set to -1. The mapping is fuzzy and its softness is controlled by the smoothness parameter} xywh {25 15 350 120} class FunctionPlot2DBox } } Fl_Slider m_OutThresholdProgress { xywh {0 335 400 15} type {Horz Fill} selection_color 180 labelsize 12 align 0 value 1 deactivate } Fl_Value_Input m_InLowerThresholdText { label {Lower threshold} callback {this->OnThresholdLowerChange(o->value());} tooltip {Set the threshold below which the speed function will be negative} xywh {25 175 40 20} color 51 labelsize 12 align 5 maximum 100 step 0.01 textsize 10 } Fl_Slider m_InLowerThreshold { callback {o->take_focus(); this->OnThresholdLowerChange(o->value());} tooltip {Set the threshold below which the speed function will be negative} xywh {70 175 155 20} type Horizontal color 51 selection_color 197 value 0.01 } Fl_Value_Input m_InUpperThresholdText { label {Upper threshold} callback {this->OnThresholdUpperChange(o->value());} tooltip {Set the threshold above which the speed function will be negative} xywh {25 220 40 20} color 51 labelsize 12 align 5 maximum 100 step 0.01 textsize 10 } Fl_Slider m_InUpperThreshold { callback {o->take_focus(); this->OnThresholdUpperChange(o->value());} tooltip {Set the threshold above which the speed function will be negative} xywh {70 220 155 20} type Horizontal color 51 selection_color 197 value 0.01 } } Fl_Window m_WinEdge { label {Image Edge Filter} open xywh {732 549 400 351} type Double box PLASTIC_DOWN_BOX non_modal visible } { Fl_Value_Slider m_InEdgeScale { label {Scale of Gaussian blurring (sigma)} callback {o->take_focus(); this->OnEdgeSettingsChange();} tooltip {Set the scale of Gaussian blurring applied to the image before computing the gradient magnitude (and the speed function). Use smaller values to emphasize finer edges in the input image, and use larger values if you want the active contour evolution to be driven by the strongest edges.} xywh {25 175 225 20} type Horizontal color 51 selection_color 197 labelsize 12 align 5 minimum 0.1 maximum 3 step 0.1 value 1 } Fl_Value_Slider m_InEdgeKappa { label {Edge contrast (kappa)} callback {o->take_focus(); this->OnEdgeSettingsChange();} tooltip {This parameter affects the shape of the mapping between gradient magnitude and the speed function} xywh {25 220 225 20} type Horizontal color 51 selection_color 197 labelsize 12 align 5 minimum 0.002 maximum 0.2 step 0.002 value 0.1 } Fl_Value_Slider m_InEdgeExponent { label {Edge mapping exponent} callback {o->take_focus(); this->OnEdgeSettingsChange();} tooltip {This parameter affects the shape of the mapping between gradient magnitude and the speed function} xywh {25 265 225 20} type Horizontal color 51 selection_color 197 labelsize 12 align 5 minimum 1 maximum 3 step 0.05 value 2 } Fl_Button m_BtnEdgeOK { label Okay callback {this->OnEdgeOk();} tooltip {Accept the speed image and close the window} xywh {170 300 65 25} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x1ff0d color 180 selection_color 134 labelfont 1 labelsize 12 } Fl_Button m_BtnEdgeApply { label {&Apply} callback {this->OnEdgeApply();} tooltip {Accept the speed image, but do not close the window} xywh {245 300 65 25} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x80061 color 180 selection_color 134 labelsize 12 } Fl_Button m_BtnEdgeClose { label Close callback {this->OnEdgeClose();} xywh {320 300 65 25} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0xff1b color 180 selection_color 134 labelsize 12 } Fl_Check_Button m_InEdgePreview { label {Preview result} callback {this->OnEdgePreviewChange();} tooltip {Select this to have the main program windows update automatically as you change threshold parameters} xywh {270 170 105 25} down_box DOWN_BOX labelsize 12 } Fl_Group {} { label {The function g() applied to the gradient magnitude image} open xywh {25 15 350 120} box BORDER_FRAME color 0 labelsize 11 align 2 } { Fl_Box m_BoxEdgeFunctionPlot { tooltip {This window displays a mapping between gradient magnitude of the input image and the 'speed' function. The 'speed' function should be close to 0 near edges in the input image (where gradient magnitude is high), and it should be close to 1 at regions where image intensity is nearly constant (gradient magnitude close to 0). Gradient magnitude of the image is computed at a scale (sigma). At larger scales, small fine edges are smoothed out.} xywh {25 15 350 120} box BORDER_BOX color 7 align 25 class FunctionPlot2DBox } } Fl_Slider m_OutEdgeProgress { xywh {0 335 400 15} type {Horz Fill} selection_color 180 labelsize 12 align 0 value 1 deactivate } } } } itksnap/UserInterface/MainComponents/PreprocessingUIBase.h0000644000076500000240000000447010735614375023302 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PreprocessingUIBase.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 __PreprocessingUIBase_h_ #define __PreprocessingUIBase_h_ #include "FunctionPlot2DBox.h" /** * \class PreprocessingUIBase * \brief Base class for preprocessing UI. */ class PreprocessingUIBase { public: virtual ~PreprocessingUIBase() {} // Callbacks for the Edge snake preprocessing window virtual void OnEdgeSettingsChange() = 0; virtual void OnEdgePreviewChange() = 0; virtual void OnEdgeOk() = 0; virtual void OnEdgeClose() = 0; virtual void OnEdgeApply() = 0; // Callbacks for the InOut snake preprocessing window virtual void OnThresholdDirectionChange() = 0; virtual void OnThresholdLowerChange(double value) = 0; virtual void OnThresholdUpperChange(double value) = 0; virtual void OnThresholdSettingsChange() = 0; virtual void OnThresholdOk() = 0; virtual void OnThresholdClose() = 0; virtual void OnThresholdApply() = 0; virtual void OnThresholdPreviewChange() = 0; virtual void OnThresholdOverlayChange() = 0; }; #endif itksnap/UserInterface/MainComponents/PreprocessingUILogic.cxx0000644000076500000240000005223111457367216024037 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PreprocessingUILogic.cxx,v $ Language: C++ Date: $Date: 2010/10/19 19:16:30 $ Version: $Revision: 1.7 $ Copyright (c) 2007 Paul A. Yushkevich 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #include "PreprocessingUILogic.h" #include "GlobalState.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "SNAPImageData.h" #include "ThresholdSettings.h" #include "UserInterfaceBase.h" #include "itkOrientedImage.h" #include "itkEventObject.h" void PreprocessingUILogic ::Register(UserInterfaceBase *parent) { m_ParentUI = parent; m_Driver = parent->GetDriver(); m_GlobalState = parent->GetDriver()->GetGlobalState(); } void PreprocessingUILogic ::DisplayEdgeWindow() { // Get the threshold parameters EdgePreprocessingSettings settings = m_GlobalState->GetEdgePreprocessingSettings(); // Set the slider values m_InEdgeScale->value( m_InEdgeScale->clamp(settings.GetGaussianBlurScale())); m_InEdgeKappa->value( m_InEdgeKappa->clamp(settings.GetRemappingSteepness())); m_InEdgeExponent->value( m_InEdgeExponent->clamp(settings.GetRemappingExponent())); // Position the window and show it m_ParentUI->CenterChildWindowInMainWindow(m_WinEdge); // Get a handle to the snap image data SNAPImageData *snapData = m_Driver->GetSNAPImageData(); // Initialize the speed image if necessary, and assign it the correct // color map preset if(!snapData->IsSpeedLoaded()) { snapData->InitializeSpeed(); snapData->GetSpeed()->SetColorMap( SpeedColorMap::GetPresetColorMap(m_GlobalState->GetSpeedColorMap())); } // Set the intensity mapping mode for the speed image snapData->GetSpeed()->SetModeToEdgeSnake(); // Apply the automatic preview preference m_InEdgePreview->value( m_GlobalState->GetShowPreprocessedEdgePreview() ? 1 : 0); OnEdgePreviewChange(); // Set up the plot range, etc FunctionPlot2DSettings &plotSettings = m_BoxEdgeFunctionPlot->GetPlotter().GetSettings(); plotSettings.SetPlotRangeMin(Vector2f(0.0f)); // Compute the plot to be displayed UpdateEdgePlot(); // Show the window m_WinEdge->show(); // Explicitly show the plotting box m_BoxEdgeFunctionPlot->show(); } void PreprocessingUILogic ::DisplayInOutWindow(void) { // Get the threshold parameters ThresholdSettings settings = m_GlobalState->GetThresholdSettings(); // Shorthands GreyTypeToNativeFunctor g2n = m_Driver->GetCurrentImageData()->GetGrey()->GetNativeMapping(); float lower = g2n((GreyType)settings.GetLowerThreshold()); float upper = g2n((GreyType)settings.GetUpperThreshold()); // Set the ranges for the two thresholds. These ranges do not require the // lower slider to be less than the upper slider, that will be corrected // dynamically as the user moves the sliders double iMin = m_Driver->GetCurrentImageData()->GetGrey()->GetImageMinNative(); double iMax = m_Driver->GetCurrentImageData()->GetGrey()->GetImageMaxNative(); m_InLowerThreshold->minimum(iMin); m_InLowerThresholdText->minimum(iMin); m_InLowerThreshold->maximum(iMax); m_InLowerThresholdText->maximum(iMax); m_InUpperThreshold->minimum(iMin); m_InUpperThresholdText->minimum(iMin); m_InUpperThreshold->maximum(iMax); m_InUpperThresholdText->maximum(iMax); //m_InThresholdSteepness->minimum(1); //m_InThresholdSteepness->maximum(iMax-iMin); m_InThresholdSteepness->minimum(1); m_InThresholdSteepness->maximum(10); // Make sure that the specified range is valid if(lower > upper) { lower = 0.67 * iMin + 0.33 * iMax; upper = 0.33 * iMin + 0.67 * iMax; } // Make sure the current values of the upper and lower threshold are // within the bounds (Nathan Moon) SetLowerThresholdControlValue(m_InLowerThreshold->clamp(lower)); SetUpperThresholdControlValue(m_InUpperThreshold->clamp(upper)); m_InThresholdSteepness->value( m_InThresholdSteepness->clamp(settings.GetSmoothness())); // Set the radio buttons if(settings.IsLowerThresholdEnabled() && settings.IsUpperThresholdEnabled()) { m_RadioThresholdBoth->setonly(); } else if(settings.IsLowerThresholdEnabled()) { m_RadioThresholdBelow->setonly(); } else { m_RadioThresholdAbove->setonly(); } // Position the window and show it m_ParentUI->CenterChildWindowInMainWindow(m_WinInOut); // Get a handle to the snap image data SNAPImageData *snapData = m_Driver->GetSNAPImageData(); // Initialize the speed image if necessary and assign it a color map if(!snapData->IsSpeedLoaded()) { snapData->InitializeSpeed(); snapData->GetSpeed()->SetColorMap( SpeedColorMap::GetPresetColorMap(m_GlobalState->GetSpeedColorMap())); } // Set the speed image to In/Out mode snapData->GetSpeed()->SetModeToInsideOutsideSnake(); // Apply the automatic preview preference m_InThresholdPreview->value( m_GlobalState->GetShowPreprocessedInOutPreview() ? 1 : 0); OnThresholdPreviewChange(); // Use a callback method to enable / disable sliders OnThresholdDirectionChange(); // Apply the overlay settings (this is a personal preference, but I like // to disable the color overlay when the window comes up initially) m_InThresholdOverlay->value(0); OnThresholdOverlayChange(); // Set up the plot range, etc FunctionPlot2DSettings &plotSettings = m_BoxThresholdFunctionPlot->GetPlotter().GetSettings(); NativeToGreyTypeFunctor n2g(g2n); plotSettings.SetPlotRangeMin(Vector2f(n2g(iMin),-1.0f)); plotSettings.SetPlotRangeMax(Vector2f(n2g(iMax),1.0f)); // Compute the plot to be displayed UpdateThresholdPlot(); // Show the window m_WinInOut->show(); // Explicitly show the plotting box m_BoxThresholdFunctionPlot->show(); } void PreprocessingUILogic ::OnThresholdDirectionChange() { // Enable and disable the state of the sliders based on the // current button settings if(m_RadioThresholdBoth->value()) { m_InLowerThreshold->activate(); m_InLowerThresholdText->activate(); m_InUpperThreshold->activate(); m_InUpperThresholdText->activate(); } else if(m_RadioThresholdAbove->value()) { m_InLowerThreshold->deactivate(); m_InLowerThresholdText->deactivate(); SetLowerThresholdControlValue( m_Driver->GetCurrentImageData()->GetGrey()->GetImageMinNative()); m_InUpperThreshold->activate(); m_InUpperThresholdText->activate(); } else { m_InLowerThreshold->activate(); m_InLowerThresholdText->activate(); SetUpperThresholdControlValue( m_Driver->GetCurrentImageData()->GetGrey()->GetImageMaxNative()); m_InUpperThreshold->deactivate(); m_InUpperThresholdText->deactivate(); } // The settings have changed, so call that method OnThresholdSettingsChange(); } void PreprocessingUILogic ::SetUpperThresholdControlValue(double val) { m_InUpperThreshold->value(val); m_InUpperThresholdText->value(val); } void PreprocessingUILogic ::SetLowerThresholdControlValue(double val) { m_InLowerThreshold->value(val); m_InLowerThresholdText->value(val); } void PreprocessingUILogic ::OnThresholdLowerChange(double value) { // Propagate the value to all controls SetLowerThresholdControlValue( value ); // There may be a need to shift the upper bound if(m_InUpperThreshold->value() < m_InLowerThreshold->value()) { SetUpperThresholdControlValue( m_InLowerThreshold->value() ); } // Call the generic callback OnThresholdSettingsChange(); } void PreprocessingUILogic ::OnThresholdUpperChange(double value) { // Propagate the value to all controls SetUpperThresholdControlValue( value ); // There may be a need to shift the lower bound if( m_InUpperThreshold->value() < m_InLowerThreshold->value()) { SetLowerThresholdControlValue( m_InUpperThreshold->value() ); } // Call the generic callback OnThresholdSettingsChange(); } void PreprocessingUILogic ::OnEdgeSettingsChange() { // Pass the current GUI settings to the filter EdgePreprocessingSettings settings; settings.SetGaussianBlurScale(m_InEdgeScale->value()); settings.SetRemappingSteepness(m_InEdgeKappa->value()); settings.SetRemappingExponent(m_InEdgeExponent->value()); // Store the settings globally m_GlobalState->SetEdgePreprocessingSettings(settings); // Update the plotter UpdateEdgePlot(); // Update display if in preview mode if(m_GlobalState->GetShowPreprocessedEdgePreview()) { // Apply the settings to the preview filters m_EdgePreviewFilter[0]->SetEdgePreprocessingSettings(settings); m_EdgePreviewFilter[1]->SetEdgePreprocessingSettings(settings); m_EdgePreviewFilter[2]->SetEdgePreprocessingSettings(settings); // Repaint the slice windows m_ParentUI->RedrawWindows(); } } void PreprocessingUILogic ::OnThresholdSettingsChange() { // Pass the current GUI settings to the filter ThresholdSettings settings; GreyTypeToNativeFunctor g2n = m_Driver->GetCurrentImageData()->GetGrey()->GetNativeMapping(); NativeToGreyTypeFunctor n2g(g2n); settings.SetLowerThreshold(n2g(m_InLowerThreshold->value())); settings.SetUpperThreshold(n2g(m_InUpperThreshold->value())); settings.SetSmoothness(m_InThresholdSteepness->value()); settings.SetLowerThresholdEnabled(m_InLowerThreshold->active()); settings.SetUpperThresholdEnabled(m_InUpperThreshold->active()); // Store the settings globally m_GlobalState->SetThresholdSettings(settings); // Compute the plot to be displayed UpdateThresholdPlot(); // Apply the settings to the filter but only if we are in preview mode if(m_GlobalState->GetShowPreprocessedInOutPreview()) { m_InOutPreviewFilter[0]->SetThresholdSettings(settings); m_InOutPreviewFilter[1]->SetThresholdSettings(settings); m_InOutPreviewFilter[2]->SetThresholdSettings(settings); // Repaint the slice windows m_ParentUI->RedrawWindows(); } } void PreprocessingUILogic ::OnEdgePreviewChange(void) { bool preview = (m_InEdgePreview->value() == 1); // Store the value of the flag globally m_GlobalState->SetShowPreprocessedEdgePreview(preview); // Entering preview mode means that we can draw the speed image m_GlobalState->SetShowSpeed(preview); if(preview) { // Initialize each preview filter for(unsigned int i=0;i<3;i++) { // Make sure the preview filter is deallocated assert(!m_EdgePreviewFilter[i]); // Create the filter m_EdgePreviewFilter[i] = EdgeFilterType::New(); // Give it an input m_EdgePreviewFilter[i]->SetInput( m_Driver->GetSNAPImageData()->GetGrey()->GetImage()); // Pass the current settings to the filter m_EdgePreviewFilter[i]->SetEdgePreprocessingSettings( m_GlobalState->GetEdgePreprocessingSettings()); } // Attach the preview filters to the corresponding slicers for(unsigned int j=0;j<3;j++) { // What is the image axis corresponding to the j-th slicer? unsigned int iSliceAxis = m_Driver->GetSNAPImageData()->GetSpeed()->GetDisplaySliceImageAxis(j); // Connect the previewer to that slicer m_Driver->GetSNAPImageData()->GetSpeed()->SetSliceSourceForPreview( j,m_EdgePreviewFilter[iSliceAxis]->GetOutput()); } // The speed preview is now valid m_GlobalState->SetSpeedPreviewValid(true); } else { // Clear the preview filters m_EdgePreviewFilter[0] = NULL; m_EdgePreviewFilter[1] = NULL; m_EdgePreviewFilter[2] = NULL; // Revert to the old speed image // TODO: We're reverting to the last APPLY. The user may get confused. m_Driver->GetSNAPImageData()->GetSpeed()->RemoveSliceSourcesForPreview(); // The speed preview is now invalid m_GlobalState->SetSpeedPreviewValid(false); } // Notify parent of the update m_ParentUI->OnPreprocessingPreviewStatusUpdate(preview); } void PreprocessingUILogic ::OnThresholdPreviewChange(void) { bool preview = (m_InThresholdPreview->value() == 1); // Store the value of the flag globally m_GlobalState->SetShowPreprocessedInOutPreview(preview); // Entering preview mode means that we can draw the speed image m_GlobalState->SetShowSpeed(preview); if(preview) { // Initialize each preview filter for(unsigned int i=0;i<3;i++) { // Make sure the preview filter is deallocated assert(!m_InOutPreviewFilter[i]); // Create the filter m_InOutPreviewFilter[i] = InOutFilterType::New(); // Give it an input m_InOutPreviewFilter[i]->SetInput( m_Driver->GetSNAPImageData()->GetGrey()->GetImage()); // Pass the current settings to the filter m_InOutPreviewFilter[i]->SetThresholdSettings( m_GlobalState->GetThresholdSettings()); } // Attach the preview filters to the corresponding slicers for(unsigned int j=0;j<3;j++) { // What is the image axis corresponding to the j-th slicer? unsigned int iSliceAxis = m_Driver->GetSNAPImageData()->GetSpeed()->GetDisplaySliceImageAxis(j); // Connect the previewer to that slicer m_Driver->GetSNAPImageData()->GetSpeed()->SetSliceSourceForPreview( j,m_InOutPreviewFilter[iSliceAxis]->GetOutput()); } // The speed preview is now valid m_GlobalState->SetSpeedPreviewValid(true); } else { // Clear the preview filters m_InOutPreviewFilter[0] = NULL; m_InOutPreviewFilter[1] = NULL; m_InOutPreviewFilter[2] = NULL; // Revert to the old speed image // TODO: We're reverting to the last APPLY. The user may get confused. m_Driver->GetSNAPImageData()->GetSpeed()->RemoveSliceSourcesForPreview(); // The speed preview is now invalid m_GlobalState->SetSpeedPreviewValid(false); } // Notify parent of the update m_ParentUI->OnPreprocessingPreviewStatusUpdate(preview); } void PreprocessingUILogic ::OnEdgeOk() { // Apply the preprocessing on the whole image OnEdgeApply(); // Run the same code as when the window is closed OnEdgeClose(); } void PreprocessingUILogic ::OnThresholdOk(void) { // Apply the preprocessing on the whole image OnThresholdApply(); // Run the same code as when the window is closed OnThresholdClose(); } void PreprocessingUILogic ::OnEdgeApply() { // Create a callback object CommandPointer callback = CommandType::New(); callback->SetCallbackFunction(this,&PreprocessingUILogic::OnEdgeProgress); // Use the SNAPImageData to perform preprocessing m_OutEdgeProgress->value(0); m_Driver->GetSNAPImageData()->DoEdgePreprocessing( m_GlobalState->GetEdgePreprocessingSettings(),callback); m_OutEdgeProgress->value(1); // The preprocessing image is valid m_GlobalState->SetSpeedValid(true); // Update the parent UI m_ParentUI->OnSpeedImageUpdate(); } void PreprocessingUILogic ::OnThresholdApply() { // Create a callback object CommandPointer callback = CommandType::New(); callback->SetCallbackFunction(this,&PreprocessingUILogic::OnThresholdProgress); // Use the SNAPImageData to perform preprocessing m_Driver->GetSNAPImageData()->DoInOutPreprocessing( m_GlobalState->GetThresholdSettings(),callback); // The preprocessing image is valid m_GlobalState->SetSpeedValid(true); // Update the parent UI m_ParentUI->OnSpeedImageUpdate(); } void PreprocessingUILogic ::OnThresholdOverlayChange(void) { if(m_InThresholdOverlay->value()) { // These five lines of code pass the current drawing color to the overlay // system in the SpeedImageWrapper unsigned char rgbaOverlay[4]; unsigned int label = m_GlobalState->GetDrawingColorLabel(); m_Driver->GetColorLabelTable()->GetColorLabel(label).GetRGBAVector(rgbaOverlay); SpeedImageWrapper::OverlayPixelType colorOverlay(rgbaOverlay); m_Driver->GetSNAPImageData()->GetSpeed()->SetOverlayColor(colorOverlay); // Set the cutoff threshold to zero, i.e., positive speed values will be // painted m_Driver->GetSNAPImageData()->GetSpeed()->SetOverlayCutoff(0); // Set the appropriate flag m_GlobalState->SetSpeedViewZero(true); // Repaint m_ParentUI->RedrawWindows(); } else { // Clear the appropriate flag m_GlobalState->SetSpeedViewZero(false); } // Repaint the slice windows m_ParentUI->RedrawWindows(); } void PreprocessingUILogic ::OnCloseCommon() { // Revert to the old speed image // TODO: We're reverting to the last APPLY. The user may get confused. m_Driver->GetSNAPImageData()->GetSpeed()->RemoveSliceSourcesForPreview(); // The speed preview is now invalid m_GlobalState->SetSpeedPreviewValid(false); // Make sure that if the speed is not valid, then it is not visible if(!m_GlobalState->GetSpeedValid()) { m_GlobalState->SetShowSpeed(false); m_ParentUI->RedrawWindows(); } // Notify that we have been closed m_ParentUI->OnPreprocessClose(); } void PreprocessingUILogic ::OnEdgeClose() { // If in preview mode, disconnect and destroy the preview filters m_EdgePreviewFilter[0] = NULL; m_EdgePreviewFilter[1] = NULL; m_EdgePreviewFilter[2] = NULL; // Close the window m_WinEdge->hide(); // Common closing tasks OnCloseCommon(); } void PreprocessingUILogic ::OnThresholdClose(void) { // Make sure we are no longer looking in grey/speed overlay mode m_GlobalState->SetSpeedViewZero(false); // If in preview mode, disconnect and destroy the preview filters m_InOutPreviewFilter[0] = NULL; m_InOutPreviewFilter[1] = NULL; m_InOutPreviewFilter[2] = NULL; // Close the window m_WinInOut->hide(); // Common closing tasks OnCloseCommon(); } void PreprocessingUILogic ::HidePreprocessingWindows() { m_WinInOut->hide(); m_WinEdge->hide(); } void PreprocessingUILogic ::UpdateEdgePlot() { // Create a functor object used in the filter EdgeFilterType::FunctorType functor; // Get the global settings EdgePreprocessingSettings settings = m_GlobalState->GetEdgePreprocessingSettings(); // Pass the settings to the functor functor.SetParameters(0.0f,1.0f, settings.GetRemappingExponent(), settings.GetRemappingSteepness()); // Compute the function for a range of values const unsigned int nSamples = 200; float x[nSamples]; float y[nSamples]; for(unsigned int i=0;iGetPlotter().SetDataPoints(x,y,nSamples); // Redraw the box m_BoxEdgeFunctionPlot->redraw(); } void PreprocessingUILogic ::UpdateThresholdPlot() { // Create a functor object used in the filter SmoothBinaryThresholdFunctor functor; // Get the global settings ThresholdSettings settings = m_GlobalState->GetThresholdSettings(); // We need to know the min/max of the image GreyTypeToNativeFunctor g2n = m_Driver->GetCurrentImageData()->GetGrey()->GetNativeMapping(); NativeToGreyTypeFunctor n2g(g2n); float iMin = n2g(m_InLowerThreshold->minimum()); float iMax = n2g(m_InUpperThreshold->maximum()); // Pass the settings to the functor functor.SetParameters(iMin,iMax,settings); // Compute the function for a range of values const unsigned int nSamples = 200; float x[nSamples]; float y[nSamples]; for(unsigned int i=0;iGetPlotter().SetDataPoints(x,y,nSamples); // Redraw the box m_BoxThresholdFunctionPlot->redraw(); } #include void PreprocessingUILogic ::OnEdgeProgress(itk::Object *object, const itk::EventObject &irisNotUsed(event)) { // Last progress value static double last_refresh = clock(); static const double delta = CLOCKS_PER_SEC * 0.25; // Ignore event if nothing has happened if(last_refresh + delta < clock()) { // Get the value of the progress float progress = dynamic_cast(object)->GetProgress(); // Display the filter's progress m_OutEdgeProgress->value(progress); // Let the UI refresh Fl::check(); // Reset the refresh counter last_refresh = clock(); } } void PreprocessingUILogic ::OnThresholdProgress(itk::Object *object, const itk::EventObject &irisNotUsed(event)) { // Last progress value static double last_refresh = clock(); static const double delta = CLOCKS_PER_SEC * 0.25; // Ignore event if nothing has happened if(last_refresh + delta < clock()) { // Get the value of the progress float progress = dynamic_cast(object)->GetProgress(); // Display the filter's progress m_OutThresholdProgress->value(progress); // Let the UI refresh Fl::check(); // Reset the refresh counter last_refresh = clock(); } } itksnap/UserInterface/MainComponents/PreprocessingUILogic.h0000644000076500000240000001146411136422002023442 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PreprocessingUILogic.h,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. =========================================================================*/ #ifndef __PreprocessingUILogic_h_ #define __PreprocessingUILogic_h_ #include "PreprocessingUI.h" #include "SNAPCommonUI.h" #include "EdgePreprocessingImageFilter.h" #include "SmoothBinaryThresholdImageFilter.h" // Forward references to application classes class GlobalState; class IRISApplication; class UserInterfaceBase; // ITK forward references namespace itk { template class Image; template class MemberCommand; class EventObject; } /** * \class PreprocessingUILogic * \brief Logic for the preprocessing UI. */ class PreprocessingUILogic : public PreprocessingUI { public: // A virtual destructor to make gcc happy virtual ~PreprocessingUILogic() {} // Callbacks for the Edge snake preprocessing window void OnEdgeSettingsChange(); void OnEdgePreviewChange(); void OnEdgeOk(); void OnEdgeClose(); void OnEdgeApply(); // Callbacks for the InOut snake preprocessing window void OnThresholdDirectionChange(); void OnThresholdLowerChange(double value); void OnThresholdUpperChange(double value); void OnThresholdSettingsChange(); void OnThresholdOk(); void OnThresholdClose(); void OnThresholdApply(); void OnThresholdPreviewChange(); void OnThresholdOverlayChange(); // Register this class with the parent user interface void Register(UserInterfaceBase *parent); // Display the Edge preprocessing window void DisplayEdgeWindow(); // Display the In/Out preprocessing window void DisplayInOutWindow(); // Programmatically hide the preprocessing windows void HidePreprocessingWindows(); private: GlobalState *m_GlobalState; IRISApplication *m_Driver; UserInterfaceBase *m_ParentUI; /** The image types used for preprocessing */ typedef itk::OrientedImage GreyImageType; typedef itk::OrientedImage SpeedImageType; /** The filter type for in/out processing */ typedef SmoothBinaryThresholdImageFilter< GreyImageType,SpeedImageType> InOutFilterType; typedef itk::SmartPointer InOutFilterPointer; /** The filter type for edge preprocessing */ typedef EdgePreprocessingImageFilter< GreyImageType,SpeedImageType> EdgeFilterType; typedef itk::SmartPointer EdgeFilterPointer; /** A command type for progress reporting */ typedef itk::MemberCommand CommandType; typedef itk::SmartPointer CommandPointer; /** The filter used for in-out thresholding */ InOutFilterPointer m_InOutPreviewFilter[3]; InOutFilterPointer m_InOutFilterWhole; /** The filters used for edge preprocessing */ EdgeFilterPointer m_EdgePreviewFilter[3]; EdgeFilterPointer m_EdgeFilterWhole; /** Update the plot shown in the edge plot box */ void UpdateEdgePlot(); /** Update the plot shown in the in-out plot box */ void UpdateThresholdPlot(); /** Progress callback for edge preprocessing */ void OnEdgeProgress(itk::Object *object, const itk::EventObject &event); /** Progress callback for thresholding preprocessing */ void OnThresholdProgress(itk::Object *object, const itk::EventObject &event); /** Common closing code for both preprocessors */ void OnCloseCommon(); /** Code to set values in both controls used to show the upper threshold */ void SetUpperThresholdControlValue(double val); /** Code to set values in both controls used to show the upper threshold */ void SetLowerThresholdControlValue(double val); }; #endif // __PreprocessingUILogic_h_ itksnap/UserInterface/MainComponents/ReorientImageUI.fl0000644000076500000240000002030011107537226022547 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0108 header_name {.h} code_name {.cxx} class ReorientImageUI {open : ReorientImageUIBase } { Function {MakeWindow()} {open } { Fl_Window m_WinReorient { label {Change Image Orientation} open selected xywh {712 314 580 429} type Double box PLASTIC_DOWN_BOX resizable code0 {\#include "ReorientImageUIBase.h"} visible } { Fl_Group {} { label Desired open xywh {345 30 225 390} box PLASTIC_DOWN_BOX labeltype EMBOSSED_LABEL align 5 } { Fl_Input m_InDesiredRAI { callback {this->OnDesiredRAIUpdate();} xywh {355 40 70 25} labelsize 12 when 1 textsize 12 } Fl_Output m_OutDesiredRAIError { xywh {355 65 95 15} box FLAT_BOX color 52 labeltype NO_LABEL labelsize 10 align 0 textfont 1 textsize 10 textcolor 72 } Fl_Output {m_OutDesiredAxisDirection[0]} { xywh {355 85 205 25} color 49 labelsize 12 textsize 12 } Fl_Output {m_OutDesiredAxisDirection[1]} { xywh {355 115 205 25} color 49 labelsize 12 textsize 12 } Fl_Output {m_OutDesiredAxisDirection[2]} { xywh {355 145 205 25} color 49 labelsize 12 textsize 12 } Fl_Output m_OutDesiredSForm { label {output:} xywh {355 185 205 55} type Multiline color 49 labeltype NO_LABEL align 20 textfont 4 textsize 9 } Fl_Group {} {open xywh {355 255 205 155} box ROUNDED_BOX color 7 align 16 } { Fl_Wizard m_GrpDesiredDoll {open xywh {395 270 130 120} box NO_BOX } { Fl_Group {} {open image {../ImageIOWizard/Artwork/dollRAI.gif} xywh {395 270 130 120} align 16 } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollRIA.gif} xywh {395 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollARI.gif} xywh {395 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollAIR.gif} xywh {395 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollIRA.gif} xywh {395 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollIAR.gif} xywh {395 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollInvalid.gif} xywh {395 270 130 120} align 16 hide } {} } Fl_Wizard {m_GrpDesiredDollAxis[0]} {open xywh {435 380 75 30} box NO_BOX } { Fl_Group {} { image {../ImageIOWizard/Artwork/x02.png} xywh {435 380 75 30} align 0 hide } {} Fl_Group {} { image {../ImageIOWizard/Artwork/x01.png} xywh {435 380 75 30} align 0 } {} } Fl_Wizard {m_GrpDesiredDollAxis[1]} {open xywh {520 285 30 95} box NO_BOX } { Fl_Group {} { image {../ImageIOWizard/Artwork/y02.png} xywh {520 285 20 85} align 0 hide } {} Fl_Group {} { image {../ImageIOWizard/Artwork/y01.png} xywh {520 285 20 85} align 0 } {} } Fl_Wizard {m_GrpDesiredDollAxis[2]} {open xywh {380 345 50 65} box NO_BOX } { Fl_Group {} {open image {../ImageIOWizard/Artwork/z01.png} xywh {380 345 40 55} align 0 } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/z02.png} xywh {380 345 40 55} align 0 hide } {} } } } Fl_Group {} { label Current open xywh {105 30 225 390} box PLASTIC_DOWN_BOX labeltype EMBOSSED_LABEL align 5 } { Fl_Output m_OutCurrentRAI { xywh {115 40 70 25} color 49 labelsize 12 textsize 12 } Fl_Output {m_OutCurrentAxisDirection[0]} { xywh {115 85 205 25} color 49 labelsize 12 textsize 12 } Fl_Output {m_OutCurrentAxisDirection[1]} { xywh {115 115 205 25} color 49 labelsize 12 textsize 12 } Fl_Output {m_OutCurrentAxisDirection[2]} { xywh {115 145 205 25} color 49 labelsize 12 textsize 12 } Fl_Group {} {open xywh {115 255 205 155} box ROUNDED_BOX color 7 align 16 } { Fl_Wizard m_GrpCurrentDoll {open xywh {155 270 130 120} box NO_BOX } { Fl_Group {} {open image {../ImageIOWizard/Artwork/dollRAI.gif} xywh {155 270 130 120} align 16 } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollRIA.gif} xywh {155 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollARI.gif} xywh {155 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollAIR.gif} xywh {155 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollIRA.gif} xywh {155 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollIAR.gif} xywh {155 270 130 120} align 16 hide } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/dollInvalid.gif} xywh {155 270 130 120} align 16 hide } {} } Fl_Wizard {m_GrpCurrentDollAxis[0]} {open xywh {195 380 75 30} box NO_BOX } { Fl_Group {} { image {../ImageIOWizard/Artwork/x02.png} xywh {195 380 75 30} align 0 hide } {} Fl_Group {} { image {../ImageIOWizard/Artwork/x01.png} xywh {195 380 75 30} align 0 } {} } Fl_Wizard {m_GrpCurrentDollAxis[1]} {open xywh {280 285 30 95} box NO_BOX } { Fl_Group {} { image {../ImageIOWizard/Artwork/y02.png} xywh {280 285 20 85} align 0 hide } {} Fl_Group {} { image {../ImageIOWizard/Artwork/y01.png} xywh {280 285 20 85} align 0 } {} } Fl_Wizard {m_GrpCurrentDollAxis[2]} {open xywh {140 345 50 65} box NO_BOX } { Fl_Group {} {open image {../ImageIOWizard/Artwork/z01.png} xywh {140 345 40 55} align 0 } {} Fl_Group {} {open image {../ImageIOWizard/Artwork/z02.png} xywh {140 345 40 55} align 0 hide } {} } } Fl_Output m_OutCurrentSForm { label {output:} xywh {115 185 205 55} type Multiline color 49 labeltype NO_LABEL align 20 textfont 4 textsize 9 } Fl_Output m_OutCurrentRAIClosest { xywh {115 65 130 15} box FLAT_BOX color 52 labeltype NO_LABEL labelsize 10 align 0 textfont 1 textsize 10 textcolor 60 } } Fl_Group {} { label {RAI Code:} open xywh {10 40 85 25} labelsize 12 align 24 } {} Fl_Group {} { label {Voxel X Axis:} open xywh {10 85 85 25} labelsize 12 align 24 } {} Fl_Group {} { label {Voxel Y Axis:} open xywh {10 115 85 25} labelsize 12 align 24 } {} Fl_Group {} { label {Voxel Z Axis:} open xywh {10 145 85 25} labelsize 12 align 24 } {} Fl_Group {} { label {Voxel to World Matrix (NIFTI):} open xywh {10 185 85 25} labelsize 12 align 24 } {} Fl_Button {} { label {&Apply} callback {this->OnApplyAction();} xywh {10 360 80 25} box PLASTIC_UP_BOX shortcut 0x80061 color 180 labelfont 1 labelsize 12 } Fl_Button {} { label Close callback {this->OnCloseAction();} xywh {10 390 80 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } Fl_Button {} { label {&Ok} callback {this->OnOkAction();} xywh {10 330 80 25} box PLASTIC_UP_BOX shortcut 0x8006f color 180 labelfont 1 labelsize 12 } } } } itksnap/UserInterface/MainComponents/ReorientImageUIBase.h0000644000076500000240000000322311110345135023163 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ReorientImageUIBase.h,v $ Language: C++ Date: $Date: 2008/11/17 19:47:41 $ 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 __ReorientImageUIBase_h_ #define __ReorientImageUIBase_h_ class ReorientImageUIBase { public: virtual ~ReorientImageUIBase() {} virtual void OnDesiredRAIUpdate() = 0; virtual void OnOkAction() = 0; virtual void OnApplyAction() = 0; virtual void OnCloseAction() = 0; }; #endif itksnap/UserInterface/MainComponents/ReorientImageUILogic.cxx0000644000076500000240000002006111272614326023732 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ReorientImageUILogic.cxx,v $ Language: C++ Date: $Date: 2009/10/30 16:48:22 $ 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 "SNAPCommonUI.h" #include "ReorientImageUILogic.h" #include "UserInterfaceLogic.h" #include "IRISApplication.h" #include "GenericImageData.h" #include "itkOrientedImage.h" #include #include #include #include std::string PrintMatrixFormatted( vnl_matrix mat, size_t width, size_t precision, const char *prefix = "") { // Create output stream std::ostringstream sout; // Print each row and column of the matrix for(size_t i = 0; i < mat.rows(); i++) { sout << prefix; for(size_t j = 0; j < mat.columns(); j++) { sout << std::setw(width) << std::setprecision(precision) << mat(i,j); if(j == mat.columns() - 1) sout << std::endl; else sout << " "; } } // Return result return sout.str(); } const char ReorientImageUILogic::m_RAICodes[3][2] = { {'R', 'L'}, {'A', 'P'}, {'I', 'S'}}; const char *ReorientImageUILogic::m_AxisLabels[3][2] = { {"Right to Left", "Left to Right"}, {"Anterior to Posterior", "Posterior to Anterior"}, {"Inferior to Superior", "Superior to Inferior"}}; ReorientImageUILogic ::ReorientImageUILogic() { } void ReorientImageUILogic ::ShowDialog() { assert(m_ParentUI); // Get the current image data m_ImageData = m_ParentUI->GetDriver()->GetCurrentImageData(); // Get the direction matrix vnl_matrix_fixed dir = m_ImageData->GetMain()->GetImageBase()->GetDirection().GetVnlMatrix(); // Compute the RAI code from the direction matrix char rai[4]; rai[3] = 0; bool oblique = false; for(size_t i = 0; i < 3; i++) { // Get the direction cosine for voxel direction i Vector3d dcos = dir.get_column(i); double dabsmax = dcos.inf_norm(); for(size_t j = 0; j < 3; j++) { double dabs = fabs(dcos[j]); size_t dsgn = dcos[j] > 0 ? 0 : 1; if(dabs == 1.0) { rai[i] = m_RAICodes[j][dsgn]; m_OutCurrentAxisDirection[i]->value(m_AxisLabels[j][dsgn]); } else if(dabs == dabsmax) { oblique = true; rai[i] = m_RAICodes[j][dsgn]; m_OutCurrentAxisDirection[i]->value("Oblique"); } } } // If RAI code is valid, set all the other components if(oblique) { m_OutCurrentRAI->value("OBLIQUE"); std::ostringstream sout; sout << "Closest to " << rai; m_OutCurrentRAIClosest->value(sout.str().c_str()); m_InDesiredRAI->value(rai); } else { m_OutCurrentRAI->value(rai); m_OutCurrentRAIClosest->value(""); m_InDesiredRAI->value(rai); } // Print the matrix string smat_current = PrintMatrixFormatted(m_ImageData->GetMain()->GetNiftiSform(), 9, 3); m_OutCurrentSForm->value(smat_current.c_str()); // Update the 'current' graphic this->UpdateOrientationGraphic(rai, oblique, m_GrpCurrentDoll, m_GrpCurrentDollAxis); // Update the other fields in the desired column this->UpdateDesiredDerivedFields(); // Show the window m_WinReorient->show(); m_ParentUI->CenterChildWindowInMainWindow(m_WinReorient); } void ReorientImageUILogic ::UpdateDesiredDerivedFields() { // An identity matrix, for pulling out rows vnl_matrix_fixed eye; eye.set_identity(); // Apply the RAI code const char *rai = m_InDesiredRAI->value(); for(size_t i = 0; i < 3; i++) { for(size_t j = 0; j < 3; j++) { for(size_t k = 0; k < 2; k++) { if(toupper(rai[i]) == m_RAICodes[j][k]) { m_OutDesiredAxisDirection[i]->value(m_AxisLabels[j][k]); m_DesiredDirection.set_column(i, (k==0 ? 1.0 : -1.0) * eye.get_row(j)); } } } } // Compute the new NIFTI matrix given these directions vnl_matrix_fixed m_sform = GreyImageWrapper::ConstructNiftiSform( m_DesiredDirection, m_ImageData->GetMain()->GetImageBase()->GetOrigin().GetVnlVector(), m_ImageData->GetMain()->GetImageBase()->GetSpacing().GetVnlVector()); // Print the matrix string smat_desired = PrintMatrixFormatted(m_sform, 9, 3); m_OutDesiredSForm->value(smat_desired.c_str()); // Update the graphic this->UpdateOrientationGraphic(rai, false, m_GrpDesiredDoll, m_GrpDesiredDollAxis); } void ReorientImageUILogic ::UpdateOrientationGraphic( const char *rai, bool oblique, Fl_Wizard *grpDoll, Fl_Wizard *grpDollAxis[]) { // Handle oblique/invalid cases (show question mark) if(oblique || !ImageCoordinateGeometry::IsRAICodeValid(rai)) { grpDoll->value(grpDoll->child(6)); return; } // Initialize the mappings for the doll display size_t xCodeToGraphicMap[3][3][3]; xCodeToGraphicMap[0][1][2] = 0; // RAI xCodeToGraphicMap[0][2][1] = 1; // RIA xCodeToGraphicMap[1][0][2] = 2; // ARI xCodeToGraphicMap[1][2][0] = 3; // AIR xCodeToGraphicMap[2][0][1] = 4; // IRA xCodeToGraphicMap[2][1][0] = 5; // IAR // Convert RAI to a numeric mapping Vector3i rn = ImageCoordinateGeometry::ConvertRAIToCoordinateMapping(rai); Vector3i ra = rn.apply(abs); // Get the appropriate doll size_t idoll = xCodeToGraphicMap[ra[0]-1][ra[1]-1][ra[2]-1]; // Select the doll grpDoll->value(grpDoll->child(idoll)); // Geez, this is getting complicated! int xDollAxisFlips[6][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 1 }, { 0, 0, 1 }}; // Flip the arrows too for(size_t i = 0; i < 3; i++) { int axdir = (rn[i] > 0) ? xDollAxisFlips[idoll][i] : 1 - xDollAxisFlips[idoll][i]; grpDollAxis[i]->value(grpDollAxis[i]->child(axdir)); } } void ReorientImageUILogic ::Register(UserInterfaceLogic *parent_ui) { m_ParentUI = parent_ui; } void ReorientImageUILogic ::OnDesiredRAIUpdate() { // If the code is valid, update the UI if(ImageCoordinateGeometry::IsRAICodeValid(m_InDesiredRAI->value())) { m_OutDesiredRAIError->value(""); this->UpdateDesiredDerivedFields(); } else { m_OutDesiredRAIError->value("Invalid RAI Code"); m_OutDesiredRAIError->show(); m_OutDesiredAxisDirection[0]->value(""); m_OutDesiredAxisDirection[1]->value(""); m_OutDesiredAxisDirection[2]->value(""); m_OutDesiredSForm->value(""); this->UpdateOrientationGraphic("", false, m_GrpDesiredDoll, m_GrpDesiredDollAxis); } } void ReorientImageUILogic ::OnOkAction() { this->OnApplyAction(); this->OnCloseAction(); } void ReorientImageUILogic ::OnApplyAction() { // Reorient the image m_ParentUI->GetDriver()->ReorientImage(m_DesiredDirection); // GUI has to refresh m_ParentUI->OnImageGeometryUpdate(); } void ReorientImageUILogic ::OnCloseAction() { m_WinReorient->hide(); } bool ReorientImageUILogic ::Shown() { return m_WinReorient->shown(); } itksnap/UserInterface/MainComponents/ReorientImageUILogic.h0000644000076500000240000000453311272614330023360 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ReorientImageUILogic.h,v $ Language: C++ Date: $Date: 2009/10/30 16:48:24 $ 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 __ReorientImageUILogic_h_ #define __ReorientImageUILogic_h_ #include "ReorientImageUI.h" #include class UserInterfaceLogic; class GenericImageData; class ReorientImageUILogic : public ReorientImageUI { public: // Constructor ReorientImageUILogic(); virtual ~ReorientImageUILogic() {} // Callbacks void OnDesiredRAIUpdate(); void OnOkAction(); void OnApplyAction(); void OnCloseAction(); void ShowDialog(); void Register(UserInterfaceLogic *parent_ui); bool Shown(); private: void UpdateDesiredDerivedFields(); // This code matches the orientation graphic to a RAI void UpdateOrientationGraphic( const char *rai, bool oblique, Fl_Wizard *grpDoll, Fl_Wizard *grpDollAxis[]); UserInterfaceLogic *m_ParentUI; GenericImageData *m_ImageData; // Direction matrix for the desired RAI code vnl_matrix_fixed m_DesiredDirection; static const char m_RAICodes[3][2]; static const char *m_AxisLabels[3][2]; }; #endif itksnap/UserInterface/MainComponents/ResizeRegionDialog.fl0000644000076500000240000001653510534177576023336 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0102 header_name {.h} code_name {.cxx} class ResizeRegionDialog {open : { private ResizeRegionDialogBase } } { Function {MakeWindow()} {open return_type {virtual void} } { Fl_Window m_WinResample { label {Region Resampling} open xywh {766 513 440 349} type Double box PLASTIC_DOWN_BOX code0 {\#include "ResizeRegionDialogBase.h"} modal visible } { Fl_Group {} { label {Segmentation Region Resampling} open xywh {5 5 435 35} labeltype EMBOSSED_LABEL align 16 } {} Fl_Group {} { label {Input image voxel size:} open xywh {10 60 420 55} box PLASTIC_UP_BOX labelsize 12 align 5 } { Fl_Value_Output {m_OutSize[0]} { label {X:} xywh {45 75 55 25} box DOWN_BOX color 51 labelsize 12 textsize 12 } Fl_Value_Output {m_OutSize[1]} { label {Y:} xywh {180 75 55 25} box DOWN_BOX color 51 labelsize 12 textsize 12 } Fl_Value_Output {m_OutSize[2]} { label {Z:} xywh {315 75 55 25} box DOWN_BOX color 51 labelsize 12 textsize 12 } Fl_Group {} { label {mm.} open xywh {100 75 35 25} labelsize 12 align 16 } {} Fl_Group {} { label {mm.} open xywh {235 75 35 25} labelsize 12 align 16 } {} Fl_Group {} { label {mm.} open xywh {370 75 35 25} labelsize 12 align 16 } {} } Fl_Group {} { label {Region of interest voxel size:} open xywh {10 140 420 75} box PLASTIC_UP_BOX labelsize 12 align 5 } { Fl_Value_Input {m_InSize[0]} { label {X:} callback {this->OnVoxelSizeChange();} xywh {45 155 55 25} labelsize 12 textsize 12 } Fl_Value_Input {m_InSize[1]} { label {Y:} callback {this->OnVoxelSizeChange();} xywh {180 155 55 25} labelsize 12 textsize 12 } Fl_Value_Input {m_InSize[2]} { label {Z:} callback {this->OnVoxelSizeChange();} xywh {315 155 55 25} labelsize 12 textsize 12 } Fl_Choice {m_InScale[0]} { callback {this->OnVoxelScaleChange();} open xywh {45 185 75 20} down_box BORDER_BOX labelsize 11 textsize 11 } { menuitem {} { label custom xywh {90 90 100 20} labelsize 11 } menuitem {} { label {5 : 1} xywh {0 0 100 20} labelsize 11 } menuitem {} { label {4 : 1} xywh {10 10 100 20} labelsize 11 } menuitem {} { label {3 : 1} xywh {20 20 100 20} labelsize 11 } menuitem {} { label {2 : 1} xywh {30 30 100 20} labelsize 11 } menuitem {} { label {1 : 1} xywh {40 40 100 20} value 1 labelsize 11 } menuitem {} { label {1 : 2} xywh {50 50 100 20} labelsize 11 } menuitem {} { label {1 : 3} xywh {60 60 100 20} labelsize 11 } menuitem {} { label {1 : 4} xywh {70 70 100 20} labelsize 11 } menuitem {} { label {1 : 5} xywh {80 80 100 20} labelsize 11 } } Fl_Choice {m_InScale[1]} { callback {this->OnVoxelScaleChange();} open xywh {180 185 75 20} down_box BORDER_BOX labelsize 11 textsize 11 } { menuitem {} { label custom xywh {100 100 100 20} labelsize 11 } menuitem {} { label {5 : 1} xywh {10 10 100 20} labelsize 11 } menuitem {} { label {4 : 1} xywh {20 20 100 20} labelsize 11 } menuitem {} { label {3 : 1} xywh {30 30 100 20} labelsize 11 } menuitem {} { label {2 : 1} xywh {40 40 100 20} labelsize 11 } menuitem {} { label {1 : 1} xywh {50 50 100 20} value 1 labelsize 11 } menuitem {} { label {1 : 2} xywh {60 60 100 20} labelsize 11 } menuitem {} { label {1 : 3} xywh {70 70 100 20} labelsize 11 } menuitem {} { label {1 : 4} xywh {80 80 100 20} labelsize 11 } menuitem {} { label {1 : 5} xywh {90 90 100 20} labelsize 11 } } Fl_Choice {m_InScale[2]} { callback {this->OnVoxelScaleChange();} open xywh {315 185 75 20} down_box BORDER_BOX labelsize 11 textsize 11 } { menuitem {} { label custom xywh {110 110 100 20} labelsize 11 } menuitem {} { label {5 : 1} xywh {20 20 100 20} labelsize 11 } menuitem {} { label {4 : 1} xywh {30 30 100 20} labelsize 11 } menuitem {} { label {3 : 1} xywh {40 40 100 20} labelsize 11 } menuitem {} { label {2 : 1} xywh {50 50 100 20} labelsize 11 } menuitem {} { label {1 : 1} xywh {60 60 100 20} value 1 labelsize 11 } menuitem {} { label {1 : 2} xywh {70 70 100 20} labelsize 11 } menuitem {} { label {1 : 3} xywh {80 80 100 20} labelsize 11 } menuitem {} { label {1 : 4} xywh {90 90 100 20} labelsize 11 } menuitem {} { label {1 : 5} xywh {100 100 100 20} labelsize 11 } } Fl_Group {} { label {mm.} xywh {235 155 35 25} labelsize 12 align 16 } {} Fl_Group {} { label {mm.} open xywh {100 155 35 25} labelsize 12 align 16 } {} Fl_Group {} { label {mm.} open xywh {370 155 35 25} labelsize 12 align 16 } {} } Fl_Group {} { label {Resampling method:} open xywh {10 240 420 55} box PLASTIC_UP_BOX labelsize 12 align 5 } { Fl_Choice m_InInterpolation {open xywh {85 255 265 25} down_box BORDER_BOX labelsize 12 textsize 12 } { menuitem {} { label {Nearest Neighbor (fastest) } selected xywh {0 0 100 20} labelsize 12 } menuitem {} { label {Linear Interpolation (fast)} xywh {10 10 100 20} labelsize 12 } menuitem {} { label {Cubic Interpolation (high quality)} xywh {20 20 100 20} value 1 labelsize 12 } menuitem {} { label {Windowed Sinc Interpolation (best quality)} xywh {30 30 100 20} labelsize 12 } } } Fl_Button {} { label Ok callback {OnOkAction();} xywh {125 310 90 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 } Fl_Button {} { label Cancel callback {OnCancelAction();} xywh {225 310 90 25} box PLASTIC_UP_BOX color 180 labelsize 12 } } } } itksnap/UserInterface/MainComponents/ResizeRegionDialogBase.h0000644000076500000240000000330310735614375023740 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ResizeRegionDialogBase.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 __ResizeRegionDialogBase_h_ #define __ResizeRegionDialogBase_h_ class ResizeRegionDialogBase { public: virtual ~ResizeRegionDialogBase() {} virtual void OnVoxelSizeChange() = 0; virtual void OnVoxelScaleChange() = 0; virtual void OnOkAction() = 0; virtual void OnCancelAction() = 0; }; #endif // __ResizeRegionDialogBase_h_ itksnap/UserInterface/MainComponents/ResizeRegionDialogLogic.cxx0000644000076500000240000001113110735614375024474 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ResizeRegionDialogLogic.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 "ResizeRegionDialogLogic.h" const int ResizeRegionDialogLogic::NumberOfScaleChoices = 10; const int ResizeRegionDialogLogic::ScaleChoices[10][2] = {{0,0},{5,1},{4,1},{3,1},{2,1},{1,1},{1,2},{1,3},{1,4},{1,5}}; void ResizeRegionDialogLogic ::MakeWindow() { // Call parent method ResizeRegionDialog::MakeWindow(); // Update the UI choices to defaults m_InScale[0]->value(5); m_InScale[1]->value(5); m_InScale[2]->value(5); m_InInterpolation->value(2); } bool ResizeRegionDialogLogic ::DisplayDialog(const double *voxelSpacing, SNAPSegmentationROISettings &targetROI) { // Set the input spacing values for(unsigned int i=0;i<3;i++) m_OutSize[i]->value(voxelSpacing[i]); // Compute the output voxel sizes based on the current scaling settings OnVoxelScaleChange(); // Show the dialog m_WinResample->show(); // Run until closed m_Accept = false; while(m_WinResample->shown()) Fl::wait(); // Compute the voxel scaling Vector3d voxelScaling(1.0); if(m_Accept) { voxelScaling[0] = GetSpacing(0) / voxelSpacing[0]; voxelScaling[1] = GetSpacing(1) / voxelSpacing[1]; voxelScaling[2] = GetSpacing(2) / voxelSpacing[2]; } // Update the settings object targetROI.SetVoxelScale(voxelScaling); targetROI.SetResampleFlag(m_Accept); // Update the interpolation mode switch(m_InInterpolation->value()) { case 0 : targetROI.SetInterpolationMethod( SNAPSegmentationROISettings::NEAREST_NEIGHBOR); break; case 1 : targetROI.SetInterpolationMethod( SNAPSegmentationROISettings::TRILINEAR); break; case 2 : targetROI.SetInterpolationMethod( SNAPSegmentationROISettings::TRICUBIC); break; case 3 : targetROI.SetInterpolationMethod( SNAPSegmentationROISettings::SINC_WINDOW_05); break; }; // Return the accept flag return m_Accept; } void ResizeRegionDialogLogic ::OnVoxelScaleChange() { // Set the output spacing values for(unsigned int i=0;i<3;i++) { // Get the numerator and denominator int choice = m_InScale[i]->value(); int scaleFrom = ScaleChoices[choice][0]; int scaleTo = ScaleChoices[choice][1]; // If both scale choices are 0 and 0, i.e, custom mode, leave the values // unchanged if(scaleFrom == 0 && scaleTo == 0) continue; if(scaleFrom == 1) m_InSize[i]->value(scaleTo * m_OutSize[i]->value()); else m_InSize[i]->value(m_OutSize[i]->value() / scaleFrom); } } void ResizeRegionDialogLogic ::OnVoxelSizeChange() { // Set the output spacing values for(unsigned int i=0;i<3;i++) { int choice = 0; // Custom choice for(int j = 1;j < NumberOfScaleChoices;j++) { // Get the numerator and denominator int scaleFrom = ScaleChoices[j][0]; int scaleTo = ScaleChoices[j][1]; // See if the choice fits if(m_InSize[i]->value() * scaleFrom == m_OutSize[i]->value() * scaleTo) { choice = j; break; } } m_InScale[i]->value(choice); } } void ResizeRegionDialogLogic ::OnOkAction() { m_WinResample->hide(); m_Accept = true; } void ResizeRegionDialogLogic ::OnCancelAction() { m_WinResample->hide(); m_Accept = false; } itksnap/UserInterface/MainComponents/ResizeRegionDialogLogic.h0000644000076500000240000000461210735614375024127 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ResizeRegionDialogLogic.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 __ResizeRegionDialogLogic_h_ #define __ResizeRegionDialogLogic_h_ #include "ResizeRegionDialog.h" #include "SNAPSegmentationROISettings.h" class ResizeRegionDialogLogic : public ResizeRegionDialog { public: virtual ~ResizeRegionDialogLogic() {} // A list of scaling choices static const int NumberOfScaleChoices; static const int ScaleChoices[10][2]; // Resampling method enum ResamplingMethod { NearestNeighbor, Linear }; // Get the new spacing double GetSpacing(unsigned int dim) { return m_InSize[dim]->value(); } // Get the selected resampling method ResamplingMethod GetResamplingMethod() { return (ResamplingMethod)(NearestNeighbor + m_InInterpolation->value()); } // Callback functions void MakeWindow(); bool DisplayDialog(const double *voxelSpacing, SNAPSegmentationROISettings &targetROI); void OnVoxelSizeChange(); void OnVoxelScaleChange(); void OnOkAction(); void OnCancelAction(); private: bool m_Accept; }; #endif // __ResizeRegionDialogLogic_h_ itksnap/UserInterface/MainComponents/RestoreSettingsDialog.fl0000644000076500000240000000454411553107611024053 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0300 header_name {.h} code_name {.cxx} class RestoreSettingsDialog {open : {private RestoreSettingsDialogBase} } { Function {MakeWindow()} {open } { Fl_Window m_WinMain { label {Restore Settings?} callback {OnCancelAction();} open xywh {123 300 400 255} type Double box PLASTIC_DOWN_BOX code0 {\#include "RestoreSettingsDialogBase.h"} visible } { Fl_Group {} { label {Would you like ITK-SNAP to restore the settings that were used the last time you worked with this image? The following settings can be restored:} open selected xywh {10 10 385 65} labelsize 12 align 149 } {} Fl_Group {} {open xywh {20 70 360 115} } { Fl_Check_Button m_ChkLabels { label {Labels for segmentation} xywh {55 80 320 20} down_box DOWN_BOX value 1 labelsize 12 } Fl_Check_Button m_ChkPreprocessing { label {Image preprocessing parameters} xywh {55 100 320 20} down_box DOWN_BOX value 1 labelsize 12 } Fl_Check_Button m_ChkParameters { label {Automatic segmentation parameters} xywh {55 120 320 20} down_box DOWN_BOX value 1 labelsize 12 } Fl_Check_Button m_ChkDisplayOptions { label {Display options} xywh {55 140 320 20} down_box DOWN_BOX value 1 labelsize 12 } } Fl_Button {} { label {&Restore Settings} callback {OnRestoreSettingsAction();} xywh {85 220 120 25} box PLASTIC_UP_BOX shortcut 0x80072 color 180 labelfont 1 labelsize 12 } Fl_Button {} { label {Don't Restore} callback {OnDoNotRestoreSettingsAction();} xywh {220 220 105 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } Fl_Choice m_InFutureApproach { label {In the future, } open xywh {100 185 230 20} down_box BORDER_BOX labelsize 12 textsize 12 } { MenuItem {} { label {continue asking this question} xywh {0 0 100 20} labelsize 12 } MenuItem {} { label {don't ask again about this image} xywh {20 20 100 20} labelsize 12 } MenuItem {} { label {never ask me this question} xywh {30 30 100 20} labelsize 12 } } } } } itksnap/UserInterface/MainComponents/RestoreSettingsDialogBase.h0000644000076500000240000000330410735614375024500 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RestoreSettingsDialogBase.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 __RestoreSettingsDialogBase_h_ #define __RestoreSettingsDialogBase_h_ class RestoreSettingsDialogBase { public: virtual ~RestoreSettingsDialogBase() {} virtual void OnRestoreSettingsAction() = 0; virtual void OnDoNotRestoreSettingsAction() = 0; virtual void OnCancelAction() = 0; }; #endif // __RestoreSettingsDialogBase_h_ itksnap/UserInterface/MainComponents/RestoreSettingsDialogLogic.cxx0000644000076500000240000001163010735614375025237 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RestoreSettingsDialogLogic.cxx,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 "RestoreSettingsDialogLogic.h" #include "UserInterfaceBase.h" #include "SystemInterface.h" #include // Disable some windows debug length messages #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #pragma warning ( disable : 4503 ) #endif using namespace std; void RestoreSettingsDialogLogic ::DisplayDialog(UserInterfaceBase *parent, Registry *associatedSettings) { // Remember the code and the system interface m_SystemInterface = parent->GetSystemInterface(); m_AssociatedSettings = associatedSettings; // Clear this flag m_AssociatedSettingsHaveChanged = false; // Determine whether the options should be displayed at all Registry *folder = &m_SystemInterface->Folder("ImageAssociation.RestoreOptions.Generic"); bool noprompt = folder->Entry("DoNotPrompt")[false]; // If the generic setting is to prompt, check for a specific setting if(!noprompt) { folder = &m_AssociatedSettings->Folder("RestoreOptions"); noprompt = folder->Entry("DoNotPrompt")[false]; } // Now the noprompt and key combination should reflect either the generic or // the specific state if(noprompt) { // The user doesn't want to be prompted. Load the settings m_RestoreSettings = folder->Entry("RestoreAny")[true]; m_RestoreLabels = folder->Entry("RestoreLabels")[true]; m_RestoreParameters = folder->Entry("RestoreParameters")[true]; m_RestorePreprocessing = folder->Entry("RestorePreprocessing")[true]; m_RestoreDisplayOptions = folder->Entry("RestoreDisplayOptions")[true]; } else { // Show the dialog and let the user specify what features to restore parent->CenterChildWindowInMainWindow(m_WinMain); m_WinMain->show(); while(m_WinMain->shown()) Fl::wait(); } } void RestoreSettingsDialogLogic ::OnRestoreSettingsAction() { // Set the current state m_RestoreSettings = true; m_RestoreLabels = m_ChkLabels->value() != 0; m_RestorePreprocessing = m_ChkPreprocessing->value() != 0; m_RestoreParameters = m_ChkParameters->value() != 0; m_RestoreDisplayOptions = m_ChkDisplayOptions->value() != 0; // Save for the future SaveDefaultSettingsForFutureIfRequested(); // Hide the window m_WinMain->hide(); } void RestoreSettingsDialogLogic ::OnDoNotRestoreSettingsAction() { // Set the current state m_RestoreSettings = false; // Save for the future SaveDefaultSettingsForFutureIfRequested(); // Hide the window m_WinMain->hide(); } void RestoreSettingsDialogLogic ::OnCancelAction() { // Hide the window m_WinMain->hide(); } void RestoreSettingsDialogLogic ::SaveDefaultSettingsForFutureIfRequested() { // If the user specified a future action, record that he/she does not // want to be bothered with this question again unsigned int future = m_InFutureApproach->value(); if(!future) return; // Get a folder into which the settings should be dumped Registry *folder = (future == 2) ? &m_SystemInterface->Folder("ImageAssociation.RestoreOptions.Generic") : &m_AssociatedSettings->Folder("RestoreOptions"); // Store the current settings as associated with the key folder->Entry("DoNotPrompt") << true; folder->Entry("RestoreAny") << m_RestoreSettings; // Store the individual settings folder->Entry("RestoreLabels") << m_RestoreLabels; folder->Entry("RestoreParameters") << m_RestoreParameters; folder->Entry("RestorePreprocessing") << m_RestorePreprocessing; folder->Entry("RestoreDisplayOptions") << m_RestoreDisplayOptions; // Set this flag if(future == 1) m_AssociatedSettingsHaveChanged = true; } itksnap/UserInterface/MainComponents/RestoreSettingsDialogLogic.h0000644000076500000240000000731710735614375024673 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RestoreSettingsDialogLogic.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 __RestoreSettingsDialogLogic_h_ #define __RestoreSettingsDialogLogic_h_ #include "Registry.h" #include "SNAPCommonUI.h" #include "RestoreSettingsDialog.h" class SystemInterface; class UserInterfaceBase; /** * \class RestoreSettingsDialogLogic * \brief A dialog used to prompt the user whether to restore settings associated * with an image or not. */ class RestoreSettingsDialogLogic : public RestoreSettingsDialog { public: RestoreSettingsDialogLogic() {}; virtual ~RestoreSettingsDialogLogic() {}; /** Optionally display this dialog based on the options specified in * the system settings. The caller can prompt whether the user wanted * to restore settings, and which settings the user wanted to restore * using the GetXXX methods in this class. * * The first parameter is the pointer to the \see SystemInterface object. * The second parameter is the 16 digit code used to refer to the image * in the system interface registry */ void DisplayDialog(UserInterfaceBase *parent, Registry *associatedSettings); /** Did the user want to restore settings */ irisGetMacro(RestoreSettings,bool); /** Did the user want to restore segmentation labels */ irisGetMacro(RestoreLabels,bool); /** Did the user want to restore preprocessing settings */ irisGetMacro(RestorePreprocessing,bool); /** Did the user want to restore segmentation parameters */ irisGetMacro(RestoreParameters,bool); /** Did the user want to restore display options */ irisGetMacro(RestoreDisplayOptions,bool); /** Has the dialog changed the AssociatedSettings ? */ irisGetMacro(AssociatedSettingsHaveChanged,bool); // User interface callbacks void OnRestoreSettingsAction(); void OnDoNotRestoreSettingsAction(); void OnCancelAction(); private: // The system interface pointer SystemInterface *m_SystemInterface; // Settings associated with the current image Registry *m_AssociatedSettings; // Settings from the restore state bool m_RestoreSettings; bool m_RestoreLabels; bool m_RestorePreprocessing; bool m_RestoreParameters; bool m_RestoreDisplayOptions; // Whether or not we updated the AssociatedSettings bool m_AssociatedSettingsHaveChanged; // Method that saves the settings for the future on user request void SaveDefaultSettingsForFutureIfRequested(); }; #endif // __RestoreSettingsDialogLogic_h_ itksnap/UserInterface/MainComponents/SimpleFileDialog.fl0000644000076500000240000000255110534177576022753 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0104 header_name {.h} code_name {.cxx} class SimpleFileDialog {open : { private SimpleFileDialogBase } } { Function {MakeWindow()} {open } { Fl_Window m_Window { label {Load File} open xywh {551 370 373 152} type Double box PLASTIC_DOWN_BOX code0 {\#include "SimpleFileDialogBase.h"} modal visible } { Fl_Input m_InFile { label {File:} callback {OnFileChange();} xywh {15 35 340 25} labelsize 12 align 5 textsize 12 } Fl_Button {} { label {&Browse...} callback {this->OnBrowseAction();} xywh {185 65 80 25} box PLASTIC_UP_BOX down_box UP_BOX shortcut 0x80062 labelsize 12 } Fl_Menu_Button m_InHistory { label History callback {OnHistoryChange();} open xywh {275 65 80 25} box PLASTIC_UP_BOX selection_color 181 labelsize 12 align 20 textsize 12 } {} Fl_Button m_BtnOk { label Ok callback {this->OnOkAction();} selected xywh {100 120 80 25} box PLASTIC_UP_BOX down_box DOWN_BOX shortcut 0x1ff0d color 180 labelfont 1 labelsize 12 } Fl_Button {} { label Cancel callback {this->OnCancelAction();} xywh {190 120 80 25} box PLASTIC_UP_BOX down_box DOWN_BOX shortcut 0xff1b color 180 labelsize 12 } } } } itksnap/UserInterface/MainComponents/SimpleFileDialogBase.h0000644000076500000240000000332410735614375023367 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SimpleFileDialogBase.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:17 $ 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 __SimpleFileDialogBase_h_ #define __SimpleFileDialogBase_h_ class SimpleFileDialogBase { public: virtual ~SimpleFileDialogBase() {} virtual void OnFileChange() = 0; virtual void OnHistoryChange() = 0; virtual void OnBrowseAction() = 0; virtual void OnOkAction() = 0; virtual void OnCancelAction() = 0; }; #endif // __SimpleFileDialogBase_h_ itksnap/UserInterface/MainComponents/SimpleFileDialogLogic.cxx0000644000076500000240000001126111401011605024076 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SimpleFileDialogLogic.cxx,v $ Language: C++ Date: $Date: 2010/05/31 19:52:37 $ 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 "SimpleFileDialogLogic.h" #include "itkCommand.h" #include "FL/Fl_Native_File_Chooser.H" #include using namespace std; SimpleFileDialogLogic ::SimpleFileDialogLogic() { m_FileChooser = NULL; } SimpleFileDialogLogic ::~SimpleFileDialogLogic() { if(m_FileChooser) delete m_FileChooser; } void SimpleFileDialogLogic ::MakeWindow() { SimpleFileDialog::MakeWindow(); m_FileChooser = new Fl_Native_File_Chooser; } void SimpleFileDialogLogic ::DisplayLoadDialog(const HistoryListType &history,const char *file) { m_SaveMode = false; this->DisplayDialog(history,file); } void SimpleFileDialogLogic ::DisplaySaveDialog(const HistoryListType &history,const char *file) { m_SaveMode = true; this->DisplayDialog(history,file); } void SimpleFileDialogLogic ::DisplayDialog(const HistoryListType &history, const char *file) { // If the filename was supplied, update it in the UI if(file) { m_InFile->value(file); } // Clear the history drop box m_InHistory->clear(); // Add elements in the history list if(history.size() > 0) { // Add each item to the history menu (history is traversed backwards) HistoryListType::const_reverse_iterator it; for(it=history.rbegin();it!=history.rend();it++) { // FLTK's add() treats slashes as submenu separators, hence this code m_InHistory->replace(m_InHistory->add("dummy"),it->c_str()); } // Activate the history menu m_InHistory->activate(); } else { // Deactivate history m_InHistory->deactivate(); } // Deactivate / activate the OK button this->OnFileChange(); // Show the dialog and wait until it closes m_Window->show(); while(m_Window->shown()) Fl::wait(); } void SimpleFileDialogLogic ::OnFileChange() { // Disable the OK button if the file box is empty if(!m_InFile->value() || strlen(m_InFile->value()) == 0) m_BtnOk->deactivate(); else m_BtnOk->activate(); } void SimpleFileDialogLogic ::OnHistoryChange() { // Put the seleted history into the file box m_InFile->value(m_InHistory->mvalue()->text); // Act as if the user changed the file OnFileChange(); } void SimpleFileDialogLogic ::OnBrowseAction() { if (m_SaveMode) { m_FileChooser->type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); m_FileChooser->title("Save a file"); } else { m_FileChooser->type(Fl_Native_File_Chooser::BROWSE_FILE); m_FileChooser->title("Load a file"); } // If there is something in the file box, pass it to the chooser if(m_InFile->value() && strlen(m_InFile->value())) m_FileChooser->preset_file(m_InFile->value()); // Set the pattern m_FileChooser->filter(m_Pattern.c_str()); // Show the dialog if (m_FileChooser->show() == 0) { const char *fName = NULL; fName = m_FileChooser->filename(); if (fName && strlen(fName)) { m_InFile->value(fName); m_BtnOk->activate(); } } } void SimpleFileDialogLogic ::OnOkAction() { try { // Fire the appropriate event if(m_SaveMode) m_SaveCallback->Execute((itk::Object *) 0,itk::NoEvent()); else m_LoadCallback->Execute((itk::Object *) 0,itk::NoEvent()); // Hide the window m_Window->hide(); } catch(...) { } } void SimpleFileDialogLogic ::OnCancelAction() { // Hide the window m_Window->hide(); } itksnap/UserInterface/MainComponents/SimpleFileDialogLogic.h0000644000076500000240000001153211206550330023532 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SimpleFileDialogLogic.h,v $ Language: C++ Date: $Date: 2009/05/25 17:09:44 $ 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 __SimpleFileDialogLogic_h_ #define __SimpleFileDialogLogic_h_ #include "itkSmartPointer.h" #include "SNAPCommonUI.h" #include "SimpleFileDialog.h" #include "itkCommand.h" class Fl_Native_File_Chooser; namespace itk { class Command; } /** * \class SimpleFileDialogLogic * \brief A very basic file dialog with a history list * A simple file dialog used to load and save text files, such as * projects, voxel counts, etc. */ class SimpleFileDialogLogic : public SimpleFileDialog { public: SimpleFileDialogLogic(); virtual ~SimpleFileDialogLogic(); // Vector type for passing in history typedef std::string StringType; typedef std::vector HistoryListType; /** Called on creation, this method configures some controls */ void MakeWindow(); /** Display a load dialog. The first parameter to this dialog * is a registry representing the history of recently loaded * files. The second parameter is the optional text to display * in the filename box. If NULL is passed in (default), the previously * used file name will be retained. * * The history will not be updated until a * * The return from this method is analogous * to fl_file_chooser, ie, a filename or NULL if user cancelled.*/ void DisplayLoadDialog( const HistoryListType &history, const char *file = NULL); /** Display a save dialog. \see DisplayLoadDialog */ void DisplaySaveDialog( const HistoryListType &history, const char *file = NULL); /** Set the title of the dialog */ void SetTitle(const char *title) { m_Title = title; m_Window->label(m_Title.c_str()); } /** Set the text displayed above the filename box */ void SetFileBoxTitle(const char *title) { m_FileBoxTitle = title; m_InFile->label(m_FileBoxTitle.c_str()); } /** Set the filename pattern (FLTK format) */ irisSetMacro(Pattern,const char *); /** Get the file name currently in the box */ const char *GetFileName() { return m_InFile->value(); } /** Set the command to call when the user clicks OK in the load dialog * This command should fire an exception (any exception) if the load * fails, and then the dialog will remain open and the history list will * not be updated */ template void SetLoadCallback(T *object, void (T::*member)()) { typedef itk::SimpleMemberCommand CommandType; typename CommandType::Pointer cmd = CommandType::New(); cmd->SetCallbackFunction(object,member); m_LoadCallback = cmd; } /** Set save callback. \see SetLoadCallback */ template void SetSaveCallback(T *object, void (T::*member)()) { typedef itk::SimpleMemberCommand CommandType; typename CommandType::Pointer cmd = CommandType::New(); cmd->SetCallbackFunction(object,member); m_SaveCallback = cmd; } // User interface callbacks void OnFileChange(); void OnHistoryChange(); void OnBrowseAction(); void OnOkAction(); void OnCancelAction(); private: /** Common dialog config, regardless of save/load */ void DisplayDialog(const HistoryListType &history, const char *file); // Whether we are in save or load mode bool m_SaveMode; // Labels StringType m_FileBoxTitle; StringType m_HistoryBoxTitle; StringType m_Pattern; StringType m_Title; // Callback commands typedef itk::SmartPointer CommandPointer; CommandPointer m_SaveCallback; CommandPointer m_LoadCallback; // File chooser Fl_Native_File_Chooser *m_FileChooser; }; #endif // __SimpleFileDialogLogic_h_ itksnap/UserInterface/MainComponents/SnakeParametersUI.fl0000644000076500000240000004321711553107611023112 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0300 header_name {.h} code_name {.cxx} class SnakeParametersUI {open : {private SnakeParametersUIBase} } { Function {MakeWindow()} {open } { Fl_Window m_Window { label {Snake Parameters} open selected xywh {157 237 715 480} type Double box PLASTIC_DOWN_BOX code0 {\#include "SnakeParametersUIBase.h"} non_modal visible } { Fl_Tabs m_TabsMode {open xywh {5 5 350 360} box PLASTIC_UP_BOX color 60 labelfont 2 labelsize 12 align 0 } { Fl_Group m_GrpEasy { label {Intuitive Mode} open xywh {5 25 350 340} color 22 selection_color 94 labelfont 2 labelsize 11 } { Fl_Group m_GrpPropagationEasy { label {Balloon force:} open xywh {15 55 330 70} box PLASTIC_DOWN_BOX color 22 labelsize 12 align 5 } { Fl_Group {} {open xywh {55 90 245 35} } { Fl_Value_Slider m_InPropagationWeightEasy { callback {o->take_focus(); OnPropagationWeightChange(o);} xywh {55 90 225 15} type Horizontal color 52 labelsize 12 align 4 minimum -3 maximum 3 step 0.1 value 1 } Fl_Group {} { label expanding open xywh {250 110 50 15} labelsize 10 align 144 } {} Fl_Group {} { label contracting open xywh {70 110 50 15} labelsize 10 align 16 } {} Fl_Group {} { label static open xywh {160 110 50 15} labelsize 10 align 16 } {} Fl_Group {} { label {^} open xywh {70 105 50 10} labelfont 1 labelsize 10 align 16 } {} Fl_Group {} { label {^} open xywh {250 105 50 10} labelfont 1 labelsize 10 align 16 } {} Fl_Group {} { label {^} open xywh {160 105 50 10} labelfont 1 labelsize 10 align 16 } {} } Fl_Button m_BtnEasyHelpPropagation { label {More info...} callback {this->ShowHelp("TutorialSectionIntroductionToAutomatic.html\#Propagation");} xywh {274 61 65 21} box PLASTIC_UP_BOX color 180 labelsize 11 } } Fl_Group m_GrpCurvatureEasy { label {Curvature force:} open xywh {15 155 330 70} box PLASTIC_DOWN_BOX color 22 labelsize 12 align 5 } { Fl_Group {} { xywh {55 190 245 35} } { Fl_Value_Slider m_InCurvatureWeightEasy { callback {o->take_focus(); OnCurvatureWeightChange(o);} xywh {55 190 225 15} type Horizontal color 52 labelsize 12 align 4 value 0.2 } Fl_Group {} { label spherical open xywh {250 210 50 15} labelsize 10 align 144 } {} Fl_Group {} { label detailed open xywh {70 210 50 15} labelsize 10 align 16 } {} Fl_Group {} { label smooth open xywh {155 210 50 15} labelsize 10 align 16 } {} Fl_Group {} { label {^} open xywh {70 205 50 10} labelfont 1 labelsize 10 align 16 } {} Fl_Group {} { label {^} open xywh {250 205 50 10} labelfont 1 labelsize 10 align 16 } {} Fl_Group {} { label {^} open xywh {155 205 50 10} labelfont 1 labelsize 10 align 16 } {} } Fl_Group {} { label {Makes the boundary smoother and may help prevent leaking at corners and narrow places.} xywh {20 160 245 25} labelsize 10 align 148 } {} Fl_Button m_BtnEasyHelpCurvature { label {More info...} callback {this->ShowHelp("TutorialSectionIntroductionToAutomatic.html\#Curvature");} xywh {274 161 65 20} box PLASTIC_UP_BOX color 180 labelsize 11 } } Fl_Group m_GrpAdvectionEasy { label {Advection force:} open xywh {15 255 330 70} box PLASTIC_DOWN_BOX color 22 labelsize 12 align 5 } { Fl_Group {} { xywh {55 290 250 35} } { Fl_Value_Slider m_InAdvectionWeightEasy { callback {o->take_focus(); OnAdvectionWeightChange(o);} xywh {55 290 225 15} type Horizontal color 52 labelsize 12 align 4 maximum 5 value 0.7 } Fl_Group {} { label {large effect} open xywh {245 310 60 15} labelsize 10 align 144 } {} Fl_Group {} { label {no effect} open xywh {70 310 50 15} labelsize 10 align 16 } {} Fl_Group {} { label smooth open xywh {155 310 50 15} labelsize 10 align 16 hide } {} Fl_Group {} { label {^} open xywh {70 305 50 10} labelfont 1 labelsize 10 align 16 } {} Fl_Group {} { label {^} open xywh {250 305 50 10} labelfont 1 labelsize 10 align 16 } {} Fl_Group {} { label {^} open xywh {155 305 50 10} labelfont 1 labelsize 10 align 16 hide } {} } Fl_Group {} { label {Pushes boundary back as it tries to cross edges, causing the segmentation to converge.} open xywh {20 260 260 25} labelsize 10 align 148 } {} Fl_Button m_BtnEasyHelpAdvection { label {More info...} callback {this->ShowHelp("TutorialSectionIntroductionToAutomatic.html\#Advection");} xywh {274 261 65 20} box PLASTIC_UP_BOX color 180 labelsize 11 } } Fl_Group {} { label {Pushes the boundary inwards or outwards, propor- tionally to the preprocessed image intensity.} open xywh {20 60 260 25} labelsize 10 align 148 } {} } Fl_Group m_GrpMath { label {Mathematical Mode} open xywh {5 25 350 320} color 22 selection_color 102 labelfont 2 labelsize 11 align 2 hide } { Fl_Group {} { label {Front propagation equation:} open xywh {20 55 330 60} labelsize 12 align 5 } { Fl_Check_Button m_BtnAdvancedEquation { label {Use experimental equation (advanced)} callback {OnAdvancedEquationAction();} xywh {110 90 235 15} down_box DOWN_BOX color 109 selection_color 32 labelsize 11 } Fl_Wizard m_WizEquation {open xywh {20 55 325 30} } { Fl_Group m_GrpEquationEdge {open image {Artwork/formula01.gif} xywh {20 55 325 30} box BORDER_BOX color 7 align 16 } {} Fl_Group m_GrpEquationRegion {open image {Artwork/formula02.gif} xywh {20 55 325 30} box BORDER_BOX color 7 align 16 hide } {} Fl_Group m_GrpEquationExperimental {open image {Artwork/formula03.gif} xywh {20 55 325 30} box BORDER_BOX color 7 align 16 hide } {} } } Fl_Group m_GrpCurvatureMath { label {Curvature term:} open xywh {20 210 325 40} box PLASTIC_DOWN_BOX color 22 labelsize 12 align 5 } { Fl_Value_Input m_InCurvatureExponentMathText { callback {OnCurvatureExponentChange(o);} image {Artwork/rb.gif} xywh {235 220 30 20} labelsize 11 maximum 2 step 1 textsize 12 hide } Fl_Slider m_InCurvatureWeightMathSlider { callback {o->take_focus(); OnCurvatureWeightChange(o);} xywh {145 220 110 20} type Horizontal step 0.01 } Fl_Slider m_InCurvatureExponentMathSlider { callback {OnCurvatureExponentChange(o);} xywh {270 220 65 20} type Horizontal color 51 selection_color 60 maximum 3 step 1 hide } Fl_Value_Input m_InCurvatureWeightMathText { label {beta:} callback {OnCurvatureWeightChange(o);} xywh {95 220 45 20} labelsize 12 maximum 5 step 0.01 textsize 12 } } Fl_Group m_GrpAdvectionMath { label {Advection Term:} open xywh {20 275 325 40} box PLASTIC_DOWN_BOX color 22 labelsize 12 align 5 } { Fl_Value_Input m_InAdvectionWeightMathText { label {gamma:} callback {OnAdvectionWeightChange(o);} xywh {95 285 45 20} labelsize 12 maximum 5 step 0.01 textsize 12 } Fl_Value_Input m_InAdvectionExponentMathText { callback {OnAdvectionExponentChange(o);} image {Artwork/rc.gif} xywh {235 285 30 20} labelsize 11 maximum 2 step 1 textsize 12 hide } Fl_Slider m_InAdvectionWeightMathSlider { callback {o->take_focus(); OnAdvectionWeightChange(o);} xywh {145 285 110 20} type Horizontal maximum 5 step 0.01 } Fl_Slider m_InAdvectionExponentMathSlider { callback {OnAdvectionExponentChange(o);} xywh {270 285 65 20} type Horizontal color 51 selection_color 60 maximum 2 step 1 hide } } Fl_Group m_GrpPropagationMath { label {Propagation (baloon force) term:} open xywh {20 145 325 40} box PLASTIC_DOWN_BOX color 22 selection_color 59 labelsize 12 align 5 } { Fl_Value_Input m_InPropagationWeightMathText { label {alpha:} callback {OnPropagationWeightChange(o);} xywh {95 155 45 20} labelsize 12 minimum -5 maximum 5 step 0.01 value 1 textsize 12 } Fl_Slider m_InPropagationWeightMathSlider { callback {o->take_focus(); OnPropagationWeightChange(o);} xywh {145 155 110 20} type Horizontal minimum -1 step 0.1 value 1 } Fl_Value_Input m_InPropagationExponentMathText { callback {OnPropagationExponentChange(o);} image {Artwork/ra.gif} xywh {235 155 30 20} labelfont 2 labelsize 12 maximum 2 step 1 value 1 textsize 12 hide } Fl_Slider m_InPropagationExponentMathSlider { callback {OnPropagationExponentChange(o);} xywh {270 155 65 20} type Horizontal color 51 selection_color 60 maximum 2 step 1 value 1 hide } } } Fl_Group {} { label {Advanced Settings} open xywh {5 25 350 320} color 22 selection_color 210 labelfont 2 labelsize 11 hide } { Fl_Group m_GrpTimeStep { label {Time step computation:} open xywh {20 185 320 100} box PLASTIC_DOWN_BOX color 22 selection_color 245 labelsize 12 align 5 } { Fl_Value_Slider m_InTimeStep { label {Speed-up factor:} callback {o->take_focus(); OnTimeStepChange(o);} tooltip {Specify how much faster the snake evolution should be performed} xywh {185 230 145 20} type Horizontal labelsize 11 align 4 minimum 1 maximum 10 step 0.25 value 2 textsize 11 deactivate } Fl_Round_Button {m_BtnTimeStepAuto[0]} { callback {this->OnTimeStepAutoAction();} xywh {30 190 260 20} type Radio down_box ROUND_DOWN_BOX value 1 } Fl_Round_Button {m_BtnTimeStepAuto[1]} { callback {this->OnTimeStepAutoAction();} xywh {30 210 150 20} type Radio down_box ROUND_DOWN_BOX } Fl_Group {} { label {Automatically compute optimal time step} open xywh {45 190 200 20} labelsize 11 align 20 } {} Fl_Group {} { label {Override optimal time step (faster but less robust):} callback {this->OnTimeStepAutoAction();} open xywh {45 210 285 20} labelsize 11 align 20 } {} Fl_Value_Slider m_InSmoothingWeight { label {Noise reduction:} callback {o->take_focus(); OnSmoothingWeightChange(o);} tooltip {Experimental feature: additional smoothing of the snake} xywh {185 255 145 20} type Horizontal labelsize 11 align 4 value 0.1 textsize 11 deactivate } } Fl_Group {} { label {Algorithm configuration:} open xywh {20 55 320 110} box PLASTIC_DOWN_BOX color 22 labelsize 12 align 5 } { Fl_Choice m_InSolver { label {Use algorithm:} callback {OnSolverChange();} open xywh {105 65 225 20} down_box BORDER_BOX labelsize 11 textsize 11 } { MenuItem {} { label {ITK Sparse Field Level Set Algorithm} xywh {0 0 100 20} labelsize 11 } MenuItem {} { label {ITK Narrow Band Level Set Algorithm} xywh {10 10 100 20} labelsize 11 } MenuItem {} { label {ITK Dense Level Set Algorithm} xywh {20 20 100 20} labelsize 11 } MenuItem {} { label {SNAP Original Algorithm} xywh {30 30 100 20} labelsize 11 hide } } Fl_Wizard m_WizSolverOptions { label {Options:} open xywh {35 105 295 50} box THIN_UP_FRAME color 245 labelsize 11 align 5 } { Fl_Group m_GrpSparseSolverOptions {open xywh {35 105 290 50} } {} Fl_Group m_GrpNarrowSolverOptions {open xywh {35 105 290 50} hide } {} Fl_Group m_GrpLegacySolverOptions {open xywh {35 105 295 50} hide } { Fl_Check_Button m_ChkLegacyClamp { label {Clamp to ground (faster, less accurate)} callback {OnLegacyClampChange(o);} xywh {45 110 270 15} down_box DOWN_BOX value 1 labelsize 11 } Fl_Value_Slider m_InLegacyGround { label {'Ground' value:} callback {OnLegacyGroundChange(o);} xywh {190 130 130 15} type Horizontal labelsize 11 align 4 maximum 5 step 0.1 value 2 } } } } } } Fl_Button m_BtnClose { label Cancel callback {this->OnCloseAction();} xywh {405 445 64 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } Fl_Button m_BtnHelp { label {&Help!} callback {this->OnHelpAction();} xywh {330 445 64 25} box PLASTIC_UP_BOX shortcut 0x80068 color 180 labelsize 12 } Fl_Button m_BtnAccept { label {&Accept} callback {this->OnOkAction();} xywh {255 445 64 25} box PLASTIC_UP_BOX shortcut 0x80061 color 180 labelfont 1 labelsize 12 } Fl_Group {} { label {Conceptual illustration of the forces in 2D:} open xywh {365 25 345 405} box PLASTIC_UP_BOX color 22 labelfont 2 labelsize 12 } { Fl_Group {} { label {Curvature force} open xywh {540 46 164 164} box FLAT_BOX color 0 labelsize 12 } { Fl_Box {m_BoxPreview[1]} { xywh {542 48 160 160} box FLAT_BOX color 27 labelsize 10 code0 {\#include "SnakeParametersPreviewBox.h"} class SnakeParametersPreviewBox } } Fl_Group {} { label {Advection force} open xywh {370 231 164 164} box FLAT_BOX color 0 labelsize 12 } { Fl_Box {m_BoxPreview[2]} { xywh {372 233 160 160} box FLAT_BOX color 27 labelsize 10 code0 {\#include "SnakeParametersPreviewBox.h"} class SnakeParametersPreviewBox } } Fl_Group {} { label {Combined force} open xywh {540 231 164 164} box FLAT_BOX color 0 labelsize 12 } { Fl_Box {m_BoxPreview[3]} { xywh {542 233 160 160} box FLAT_BOX color 27 labelsize 10 code0 {\#include "SnakeParametersPreviewBox.h"} class SnakeParametersPreviewBox } } Fl_Group {} { label {Balloon force} open xywh {370 46 164 164} box FLAT_BOX color 0 labelsize 12 } { Fl_Box {m_BoxPreview[0]} { xywh {372 48 160 160} box FLAT_BOX color 27 labelsize 10 code0 {\#include "SnakeParametersPreviewBox.h"} class SnakeParametersPreviewBox } } Fl_Check_Button m_BtnAnimate { label {Show animated 2D segmentation preview} callback {this->OnAnimateAction();} xywh {485 405 220 20} down_box DOWN_BOX labelsize 11 } } Fl_Group {} {open xywh {5 375 350 55} box PLASTIC_UP_BOX color 22 } { Fl_Button m_BtnLoadParameters { label {&Load Parameters...} callback {this->OnLoadParametersAction();} xywh {65 390 105 25} box PLASTIC_UP_BOX shortcut 0x8006c color 180 labelsize 11 } Fl_Button m_BtnSaveParameters { label {&Save Parameters...} callback {this->OnSaveParametersAction();} xywh {185 390 105 25} box PLASTIC_UP_BOX shortcut 0x80073 color 180 labelsize 11 } } } } } itksnap/UserInterface/MainComponents/SnakeParametersUIBase.h0000644000076500000240000000541210735614376023542 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParametersUIBase.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:18 $ 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 __SnakeParametersUIBase_h_ #define __SnakeParametersUIBase_h_ class Fl_Valuator; class Fl_Check_Button; /** * \class SnakeParametersUIBase * \brief Base class for the parameter setting user interface. */ class SnakeParametersUIBase { public: virtual ~SnakeParametersUIBase() {} virtual void OnAdvectionExponentChange(Fl_Valuator *input) = 0; virtual void OnAdvectionWeightChange(Fl_Valuator *input) = 0; virtual void OnCurvatureExponentChange(Fl_Valuator *input) = 0; virtual void OnCurvatureWeightChange(Fl_Valuator *input) = 0; virtual void OnPropagationExponentChange(Fl_Valuator *input) = 0; virtual void OnPropagationWeightChange(Fl_Valuator *input) = 0; virtual void OnHelpAction() = 0 ; virtual void OnOkAction() = 0; virtual void OnCloseAction() = 0; virtual void OnSaveParametersAction() = 0; virtual void OnLoadParametersAction() = 0; virtual void OnAdvancedEquationAction() = 0; // Advanced page virtual void OnTimeStepAutoAction() = 0; virtual void OnTimeStepChange(Fl_Valuator *input) = 0; virtual void OnSmoothingWeightChange(Fl_Valuator *input) = 0; virtual void OnLegacyClampChange(Fl_Check_Button *input) = 0; virtual void OnLegacyGroundChange(Fl_Valuator *input) = 0; virtual void OnSolverChange() = 0; // Force example virtual void OnAnimateAction() = 0; // Help System virtual void ShowHelp(const char *link) = 0; }; #endif // __SnakeParametersUIBase_h_ itksnap/UserInterface/MainComponents/SnakeParametersUILogic.cxx0000644000076500000240000004710711457367102024301 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParametersUILogic.cxx,v $ Language: C++ Date: $Date: 2010/10/19 19:15:14 $ Version: $Revision: 1.10 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #include "SnakeParametersUILogic.h" #include "FL/fl_ask.H" #include "itkEventObject.h" #include "itkOrientedImage.h" #include "itkImageFileReader.h" #include "itkImageRegionIterator.h" #include "itkShiftScaleImageFilter.h" #include "itkPNGImageIO.h" #include "itkRGBPixel.h" #include "GlobalState.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "SNAPImageData.h" #include "SNAPRegistryIO.h" #include "SimpleFileDialogLogic.h" #include "SnakeParametersPreviewPipeline.h" #include "SystemInterface.h" #include "ThresholdSettings.h" #include "UserInterfaceBase.h" void SnakeParametersUILogic ::OnAdvectionExponentChange(Fl_Valuator *input) { int clamped = (int) input->clamp(input->value()); m_Parameters.SetAdvectionSpeedExponent(clamped); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnAdvectionWeightChange(Fl_Valuator *input) { m_Parameters.SetAdvectionWeight(input->value()); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnCurvatureExponentChange(Fl_Valuator *input) { int clamped = (int) input->clamp(input->value()); m_Parameters.SetCurvatureSpeedExponent(clamped - 1); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnCurvatureWeightChange(Fl_Valuator *input) { m_Parameters.SetCurvatureWeight(input->value()); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnPropagationExponentChange(Fl_Valuator *input) { int clamped = (int) input->clamp(input->value()); m_Parameters.SetPropagationSpeedExponent(clamped); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnPropagationWeightChange(Fl_Valuator *input) { m_Parameters.SetPropagationWeight(input->value()); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnTimeStepChange(Fl_Valuator *input) { m_Parameters.SetTimeStepFactor(input->value()); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnSmoothingWeightChange(Fl_Valuator *input) { m_Parameters.SetLaplacianWeight(input->value()); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnLegacyClampChange(Fl_Check_Button *input) { m_Parameters.SetClamp(input->value() > 0); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnLegacyGroundChange(Fl_Valuator *input) { m_Parameters.SetGround(input->value()); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnSolverChange() { if(m_WarnOnSolverUpdate) { int rc = fl_choice("Changing the algorithm while level set evolution is running\n" "will cause the results to be lost! Are you sure?", "Yes, change it","No",NULL); if(rc == 1) { this->OnParameterUpdate(); return; } } SnakeParameters::SolverType solver; switch(m_InSolver->value()) { case 0: solver = SnakeParameters::PARALLEL_SPARSE_FIELD_SOLVER; break; case 1: solver = SnakeParameters::NARROW_BAND_SOLVER; break; case 2: solver = SnakeParameters::DENSE_SOLVER; break; case 3: solver = SnakeParameters::LEGACY_SOLVER; break; default: solver = SnakeParameters::PARALLEL_SPARSE_FIELD_SOLVER; } if(solver != SnakeParameters::LEGACY_SOLVER) m_Parameters.SetAutomaticTimeStep(true); m_Parameters.SetSolver(solver); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnTimeStepAutoAction() { m_Parameters.SetAutomaticTimeStep(m_BtnTimeStepAuto[0]->value() == 1); this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnAdvancedEquationAction() { // Just call the parameters update method this->OnParameterUpdate(); } void SnakeParametersUILogic ::OnParameterUpdate() { // Propagate the values of the parameters to all the controls in // this user interface // Activate or disactivate widgets based on the snake type if(m_Parameters.GetSnakeType() == SnakeParameters::EDGE_SNAKE) { m_GrpAdvectionEasy->show(); m_GrpAdvectionMath->show(); m_InAdvectionWeightMathText->show(); m_InAdvectionExponentMathText->show(); m_InCurvatureExponentMathText->show(); m_InPropagationExponentMathText->show(); m_InAdvectionWeightMathSlider->show(); m_InAdvectionExponentMathSlider->show(); m_InCurvatureExponentMathSlider->show(); m_InPropagationExponentMathSlider->show(); } else { m_GrpAdvectionEasy->hide(); m_GrpAdvectionMath->hide(); m_InAdvectionWeightMathText->hide(); m_InAdvectionExponentMathText->hide(); m_InCurvatureExponentMathText->hide(); m_InPropagationExponentMathText->hide(); m_InAdvectionWeightMathSlider->hide(); m_InAdvectionExponentMathSlider->hide(); m_InCurvatureExponentMathSlider->hide(); m_InPropagationExponentMathSlider->hide(); } // Curvature weight float value = m_Parameters.GetCurvatureWeight(); m_InCurvatureWeightMathText->value(value); m_InCurvatureWeightMathSlider->value(value); m_InCurvatureWeightEasy->value(value); // Curvature exponent value = m_Parameters.GetCurvatureSpeedExponent() + 1; m_InCurvatureExponentMathText->value(value); m_InCurvatureExponentMathSlider->value(value); //m_InCurvatureExponentEasy->value(value); // Propagation weight value = m_Parameters.GetPropagationWeight(); m_InPropagationWeightMathText->value(value); m_InPropagationWeightMathSlider->value(value); //m_InPropagationWeightEasy->value(value); // Propagation exponent value = m_Parameters.GetPropagationSpeedExponent(); m_InPropagationExponentMathText->value(value); m_InPropagationExponentMathSlider->value(value); //m_InPropagationExponentEasy->value(value); // Advection weight value = m_Parameters.GetAdvectionWeight(); m_InAdvectionWeightMathText->value(value); m_InAdvectionWeightMathSlider->value(value); m_InAdvectionWeightEasy->value(value); // Advection exponent value = m_Parameters.GetAdvectionSpeedExponent(); m_InAdvectionExponentMathText->value(value); m_InAdvectionExponentMathSlider->value(value); //m_InAdvectionExponentEasy->value(value); // Experimental / normal equation display if(m_BtnAdvancedEquation->value() > 0) { // Show the right equation m_WizEquation->value(m_GrpEquationExperimental); // Enable all the controls m_InAdvectionExponentMathText->show(); m_InAdvectionExponentMathSlider->show(); m_InCurvatureExponentMathText->show(); m_InCurvatureExponentMathSlider->show(); m_InPropagationExponentMathText->show(); m_InPropagationExponentMathSlider->show(); m_GrpAdvectionEasy->show(); } else { m_InAdvectionExponentMathText->hide(); m_InAdvectionExponentMathSlider->hide(); m_InCurvatureExponentMathText->hide(); m_InCurvatureExponentMathSlider->hide(); m_InPropagationExponentMathText->hide(); m_InPropagationExponentMathSlider->hide(); if(m_Parameters.GetSnakeType() == SnakeParameters::EDGE_SNAKE) { // Show the right equation m_WizEquation->value(m_GrpEquationEdge); m_GrpAdvectionEasy->show(); m_GrpAdvectionMath->show(); } else { // Show the right equation m_WizEquation->value(m_GrpEquationRegion); m_GrpAdvectionEasy->hide(); m_GrpAdvectionMath->hide(); } } // Update the display in the 2D preview boxes if(m_Parameters.GetSnakeType() == SnakeParameters::EDGE_SNAKE) m_PreviewPipeline->SetSpeedImage(m_ExampleImage[0]); else m_PreviewPipeline->SetSpeedImage(m_ExampleImage[1]); // Pass the parameters to the pipeline m_PreviewPipeline->SetSnakeParameters(m_Parameters); // Advanced page : solver if(m_Parameters.GetSolver() == SnakeParameters::LEGACY_SOLVER) { // Show the right solver m_InSolver->value(2); // Show the options page m_WizSolverOptions->value(m_GrpLegacySolverOptions); // Fill the option page controls m_ChkLegacyClamp->value(m_Parameters.GetClamp() ? 1 : 0); m_InLegacyGround->value(m_Parameters.GetGround()); // Disable the automatic timestep selection m_Parameters.SetAutomaticTimeStep(false); m_BtnTimeStepAuto[0]->deactivate(); } else { if(m_Parameters.GetSolver() == SnakeParameters::NARROW_BAND_SOLVER) { // Show the right solver m_InSolver->value(1); // Show the options page m_WizSolverOptions->value(m_GrpNarrowSolverOptions); } else if(m_Parameters.GetSolver() == SnakeParameters::DENSE_SOLVER) { // Show the right solver m_InSolver->value(2); // Show the options page m_WizSolverOptions->value(m_GrpNarrowSolverOptions); } else { // Show the right solver m_InSolver->value(0); // Show the options page m_WizSolverOptions->value(m_GrpSparseSolverOptions); } // Enable the automatic timestep selection m_BtnTimeStepAuto[0]->activate(); } // Advanced page : time step if(m_Parameters.GetAutomaticTimeStep()) { m_BtnTimeStepAuto[0]->setonly(); m_InTimeStep->deactivate(); m_Parameters.SetLaplacianWeight(0); m_InSmoothingWeight->deactivate(); } else { m_BtnTimeStepAuto[1]->setonly(); m_InTimeStep->activate(); m_InSmoothingWeight->activate(); } m_InTimeStep->value(m_Parameters.GetTimeStepFactor()); m_InSmoothingWeight->value(m_Parameters.GetLaplacianWeight()); // Update the parameter display windows RedrawAllBoxes(); } void SnakeParametersUILogic ::RedrawAllBoxes() { m_BoxPreview[0]->redraw(); m_BoxPreview[1]->redraw(); m_BoxPreview[2]->redraw(); m_BoxPreview[3]->redraw(); } void SnakeParametersUILogic ::OnHelpAction() { } void SnakeParametersUILogic ::CloseWindow() { // Close the window m_Window->hide(); } void SnakeParametersUILogic ::OnOkAction() { m_UserAccepted = true; CloseWindow(); } void SnakeParametersUILogic ::OnCloseAction() { m_UserAccepted = false; CloseWindow(); } void SnakeParametersUILogic ::OnSaveParametersAction() { // Show the save dialog using the correct history m_IODialog->SetTitle("Save Snake Parameters"); m_IODialog->DisplaySaveDialog( m_SystemInterface->GetHistory("SnakeParameters"),NULL); } void SnakeParametersUILogic ::SaveParametersCallback() { // Get the selected file name const char *file = m_IODialog->GetFileName(); try { // Create a registry for the file Registry regParameters; // Use a registry IO object to put the parameters into a registry SNAPRegistryIO().WriteSnakeParameters(m_Parameters,regParameters); // Write the registry to file regParameters.WriteToFile(file,"# ITK-SNAP Snake Parameters File"); // Add the filename to the history m_SystemInterface->UpdateHistory("SnakeParameters",file); } catch(...) { fl_alert("Unable to write to file %s!",file); throw; } } void SnakeParametersUILogic ::OnLoadParametersAction() { // Show the save dialog using the correct history m_IODialog->SetTitle("Load Snake Parameters"); m_IODialog->DisplayLoadDialog( m_SystemInterface->GetHistory("SnakeParameters"),NULL); } void SnakeParametersUILogic ::LoadParametersCallback() { // Get the selected file name const char *file = m_IODialog->GetFileName(); SnakeParameters parameters; try { // Read a registry from the indicated file Registry regParameters(file); // Read the parameters from the registry parameters = SNAPRegistryIO().ReadSnakeParameters(regParameters,m_Parameters); } catch(...) { fl_alert("Unable to load parameters from file %s!",file); throw; } // Make sure the parameters are of valid type if(parameters.GetSnakeType() != m_Parameters.GetSnakeType()) { int rc = 0; if(m_Parameters.GetSnakeType() == SnakeParameters::EDGE_SNAKE) { rc = fl_choice( "Warning! The snake evolution parameters in the file are for the\n" "REGION COMPETITION mode. ITK-SNAP is currently in EDGE STOPPING mode.\n" "Do you wish to load the parameters anyway?", "Yes", "No", NULL); } else { rc = fl_choice( "Warning! The snake evolution parameters in the file are for the\n" "EDGE STOPPING mode. ITK-SNAP is currently in REGION COMPETITION mode.\n" "Do you wish to load the parameters anyway?", "Yes", "No", NULL); } // Show the message if(rc == 1) throw rc; // If region competition, drop the advection stuff if(m_Parameters.GetSnakeType() == SnakeParameters::REGION_SNAKE) { parameters.SetAdvectionSpeedExponent(0); parameters.SetAdvectionWeight(0); parameters.SetCurvatureSpeedExponent(-1); } // Keep the mode parameters.SetSnakeType(m_Parameters.GetSnakeType()); } // Set the parameters SetParameters(parameters); // Add the filename to the history m_SystemInterface->UpdateHistory("SnakeParameters",file); } void SnakeParametersUILogic ::SetParameters(const SnakeParameters &parms) { // Set the parameters m_Parameters = parms; // Update the user interface controls OnParameterUpdate(); } void SnakeParametersUILogic ::Register(UserInterfaceBase *parent) { // Get the parent's system object m_ParentUI = parent; m_SystemInterface = parent->GetSystemInterface(); // Get the edge and region example image file names string fnImage[2]; fnImage[0] = m_SystemInterface->GetFileInRootDirectory("Images2D/EdgeForcesExample.png"); fnImage[1] = m_SystemInterface->GetFileInRootDirectory("Images2D/RegionForcesExample.png"); // Typedefs typedef itk::ImageFileReader ReaderType; typedef itk::ImageRegionIterator IteratorType; typedef itk::ShiftScaleImageFilter ScaleShiftType; // Initialize the pipeline m_PreviewPipeline = new SnakeParametersPreviewPipeline( m_ParentUI->GetDriver()->GetGlobalState()); // 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 { // Read the image in ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName(fnImage[i].c_str()); ScaleShiftType::Pointer scaler = ScaleShiftType::New(); scaler->SetScale(scale_factor[i]); scaler->SetShift(shift_factor[i]); scaler->SetInput(reader->GetOutput()); scaler->Update(); // Allocate the example image m_ExampleImage[i] = scaler->GetOutput(); } catch(itk::ExceptionObject &exc) { // An exception occurred. fl_alert("Unable to load image %s\n" "Exception %s\n" "Force illustration example will not be available.", exc.GetDescription(), fnImage[i].c_str()); // Initialize an image to zeros m_ExampleImage[i] = NULL; } } // Load the points from the registry std::vector points; string fnPreset = m_SystemInterface->GetFileInRootDirectory( "Presets/SnakeParameterPreviewCurve.txt"); try { Registry regPoints(fnPreset.c_str()); points = regPoints.Folder("Points").GetArray(Vector2d(0.0)); } catch(...) { // An exception occurred. fl_alert("Unable to load file %s\n" "Force illustration example will not be available.", fnPreset.c_str()); } // Assign our previewer to the preview windows for(unsigned int i=0;i<4;i++) { m_BoxPreview[i]->SetParentUI(this); m_BoxPreview[i]->SetPipeline(m_PreviewPipeline); } // If there are some points in there, draw them if(points.size() >= 4) { // Set spline points, etc m_PreviewPipeline->SetControlPoints(points); // Set which forces to display m_BoxPreview[0]->SetForceToDisplay(SnakeParametersPreviewBox::PROPAGATION_FORCE); m_BoxPreview[1]->SetForceToDisplay(SnakeParametersPreviewBox::CURVATURE_FORCE); m_BoxPreview[2]->SetForceToDisplay(SnakeParametersPreviewBox::ADVECTION_FORCE); m_BoxPreview[3]->SetForceToDisplay(SnakeParametersPreviewBox::TOTAL_FORCE); } // Don't warn by default m_WarnOnSolverUpdate = false; } void SnakeParametersUILogic ::DisplayWindow() { // Show everything m_Window->show(); for(unsigned int j=0;j<4;j++) { m_BoxPreview[j]->show(); } // Update parameters OnParameterUpdate(); } SnakeParametersUILogic ::SnakeParametersUILogic() : SnakeParametersUI() { // Clear the pipeline m_PreviewPipeline = NULL; // Create the parameter IO dialog window m_IODialog = new SimpleFileDialogLogic; m_IODialog->MakeWindow(); m_IODialog->SetFileBoxTitle("Snake Parameters File:"); m_IODialog->SetPattern("Text files\t*.txt"); m_IODialog->SetLoadCallback( this,&SnakeParametersUILogic::LoadParametersCallback); m_IODialog->SetSaveCallback( this,&SnakeParametersUILogic::SaveParametersCallback); } SnakeParametersUILogic ::~SnakeParametersUILogic() { if(m_PreviewPipeline) delete m_PreviewPipeline; delete m_IODialog; } void SnakeParametersUILogic ::ShowHelp(const char *link) { m_ParentUI->ShowHTMLPage(link); } void SnakeParametersUILogic ::OnAnimateAction() { // Remove a timeout callback if it exists Fl::remove_timeout(SnakeParametersUILogic::OnTimerCallback, this); m_PreviewPipeline->SetDemoLoopRunning(false); // If the button value is one, add the timeout if(m_BtnAnimate->value()) { Fl::add_timeout(0.2, SnakeParametersUILogic::OnTimerCallback, this); m_PreviewPipeline->SetDemoLoopRunning(true); } else { RedrawAllBoxes(); } } void SnakeParametersUILogic ::OnTimerCallback(void *cbdata) { // Get the object that this refers to SnakeParametersUILogic *self = reinterpret_cast(cbdata); // If the window is not visible, there is nothing to do if(!self->m_Window->visible()) { self->m_PreviewPipeline->SetDemoLoopRunning(false); self->m_BtnAnimate->value(0); } else { // Call the pipeline's animation method self->m_PreviewPipeline->AnimationCallback(); // Redraw all the windows self->RedrawAllBoxes(); // Schedule another run Fl::repeat_timeout(0.05, SnakeParametersUILogic::OnTimerCallback, self); } } void SnakeParametersUILogic ::OnSpeedColorMapUpdate() { if(m_Window->visible()) RedrawAllBoxes(); } itksnap/UserInterface/MainComponents/SnakeParametersUILogic.h0000644000076500000240000001206211136422002023677 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SnakeParametersUILogic.h,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. =========================================================================*/ #ifndef __SnakeParametersUILogic_h_ #define __SnakeParametersUILogic_h_ #include "SnakeParametersUI.h" #include "SNAPCommonUI.h" #include "SnakeParameters.h" #include "itkSmartPointer.h" // Forward references to application classes class GlobalState; class IRISApplication; class UserInterfaceBase; class SnakeParametersPreviewPipeline; class SimpleFileDialogLogic; class SystemInterface; // ITK forward references namespace itk { template class OrientedImage; template class SimpleMemberCommand; } /** * \class SnakeParametersUILogic * \brief Logic for the preprocessing UI. */ class SnakeParametersUILogic : public SnakeParametersUI { public: SnakeParametersUILogic(); virtual ~SnakeParametersUILogic(); /** Register with the parent object (required for examples to work) */ void Register(UserInterfaceBase *parent); /** Initialize the internal snake parameters with external value */ void SetParameters(const SnakeParameters &parms); /** Show the window */ void DisplayWindow(); /** Get the internal snake parameters */ irisGetMacro(Parameters,SnakeParameters); /** Find out whether the user accepted the parameters or not */ irisGetMacro(UserAccepted,bool); /** Provide access to the window */ irisGetMacro(Window,Fl_Window *); /** This dialog can warn the user about changing solvers */ irisSetMacro(WarnOnSolverUpdate,bool); irisGetMacro(WarnOnSolverUpdate,bool); void OnAdvectionExponentChange(Fl_Valuator *input); void OnAdvectionWeightChange(Fl_Valuator *input); void OnCurvatureExponentChange(Fl_Valuator *input); void OnCurvatureWeightChange(Fl_Valuator *input); void OnPropagationExponentChange(Fl_Valuator *input); void OnPropagationWeightChange(Fl_Valuator *input); void OnHelpAction(); void OnOkAction(); void OnCloseAction(); void OnSaveParametersAction(); void OnLoadParametersAction(); void OnAdvancedEquationAction(); // Advanced page void OnTimeStepAutoAction(); void OnTimeStepChange(Fl_Valuator *input); void OnSmoothingWeightChange(Fl_Valuator *input); void OnLegacyClampChange(Fl_Check_Button * input); void OnLegacyGroundChange(Fl_Valuator *input); void OnSolverChange(); // Force example void OnAnimateAction(); // This method should be called when the speed color map is updated void OnSpeedColorMapUpdate(); // Help System void ShowHelp(const char *link); // Redraw the boxes void RedrawAllBoxes(); private: /** Called internally when the parameters change */ void OnParameterUpdate(); /** Internal parameter values */ SnakeParameters m_Parameters; /** Whether or not the user accepted the parameter values */ bool m_UserAccepted; // Image used to demonstrate examples typedef itk::OrientedImage ExampleImageType; typedef itk::SmartPointer ExampleImagePointer; /** Internally used example images */ ExampleImagePointer m_ExampleImage[2]; /** The preview pipeline used by all the preview windows */ SnakeParametersPreviewPipeline *m_PreviewPipeline; /** Whether or not to warn if the user tries to change the solver */ bool m_WarnOnSolverUpdate; /** Parent user interface, needed for invoking the help system */ UserInterfaceBase *m_ParentUI; /** A pointer to the system interface object */ SystemInterface *m_SystemInterface; /** An interface used to save and load parameters */ SimpleFileDialogLogic *m_IODialog; // IO Dialog callback functions void LoadParametersCallback(); void SaveParametersCallback(); // FLTK timer callback for animation static void OnTimerCallback(void *); // Common code for closing the window void CloseWindow(); }; #endif // __SnakeParametersUILogic_h_ itksnap/UserInterface/MainComponents/UserInterface.fl0000644000076500000240000030617411553107611022332 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0300 use_FL_COMMAND header_name {.h} code_name {.cxx} decl {\#include } {public local } decl {\#include } {public local } decl {\#include } {public local } class UserInterface {open : {public UserInterfaceBase} } { declblock {\#if 1 // Local variable declarations} {after {\#endif} } { decl {Vector3i fileRAI;} {public local } decl {Vector3i intRAI;} {public local } decl {Vector3i labelRAI;} {public local } decl {Vector3i preprocRAI;} {public local } } Function {UserInterface()} {open } { Fl_Window m_WinMain { label {ITK-SNAP Main Window} callback {OnMainWindowCloseAction();} open xywh {530 159 875 735} type Double align 0 resizable code0 {\#include "UserInterfaceBase.h"} class SNAPMainWindow visible } { Fl_Wizard m_WizMainLayout {open xywh {0 0 875 735} box NO_BOX resizable } { Fl_Group m_GrpMainLayoutNormal {open xywh {0 0 875 735} color 86 resizable } { Fl_Menu_Bar m_MenubarMain { xywh {0 0 875 25} labelsize 12 textsize 12 } { Submenu {} { label {&File} xywh {15 15 100 20} labelsize 12 } { MenuItem m_MenuLoadGrey { label {Open &Greyscale Image...} callback {this->OnMenuLoadGrey();} tooltip {Load an image that you want to segment} xywh {10 10 100 20} shortcut 0x40067 labelsize 12 } MenuItem m_MenuLoadRGB { label {Open &RGB Image...} callback {this->OnMenuLoadRGB();} tooltip {Load an RGB (color) image, e.g., from a diffusion tensor imaging study} xywh {20 20 100 20} shortcut 0x40072 labelsize 12 } MenuItem m_MenuLoadAdvection { label {Advection Field (Advanced)} callback {this->OnMenuLoadAdvection();} tooltip {experimental feature} xywh {40 40 100 20} labelsize 12 hide deactivate divider } Submenu m_MenuLoadPrevious { label {&Open Recent} xywh {10 10 100 20} labelsize 12 divider } { MenuItem m_MenuLoadPreviousFirst { label {Not Available} callback {OnLoadRecentAction(0);} xywh {5 5 100 20} shortcut 0x40031 labelsize 12 deactivate } MenuItem {} { label {Not Available} callback {OnLoadRecentAction(1);} xywh {15 15 100 20} shortcut 0x40032 labelsize 12 deactivate } MenuItem {} { label {Not Available} callback {OnLoadRecentAction(2);} xywh {25 25 100 20} shortcut 0x40033 labelsize 12 deactivate } MenuItem {} { label {Not Available} callback {OnLoadRecentAction(3);} xywh {35 35 100 20} shortcut 0x40034 labelsize 12 deactivate } MenuItem m_MenuLoadPreviousLast { label {Not Available} callback {OnLoadRecentAction(4);} xywh {45 45 100 20} shortcut 0x40035 labelsize 12 deactivate } } Submenu m_MenuSave { label {&Save} open xywh {25 25 100 20} labelsize 12 } { MenuItem m_MenuSaveGrey { label {&Greyscale Image...} callback {this->OnMenuSaveGrey();} tooltip {Save the segmentation results as an image} xywh {45 45 100 20} labelsize 12 deactivate divider } MenuItem m_MenuSavePreprocessed { label {&Preprocessed Image...} callback {this->OnMenuSavePreprocessed();} xywh {45 45 100 20} shortcut 0x50070 labelsize 12 deactivate } MenuItem m_MenuSaveGreyROI { label {Greyscale Image &Region...} callback {this->OnMenuSaveGreyROI();} xywh {45 45 100 20} shortcut 0x50072 labelsize 12 deactivate } MenuItem m_MenuSaveLevelSet { label {&Level Set Function...} callback {this->OnMenuSaveLevelSet();} tooltip {Save the floating-point level set function during segmentation} xywh {55 55 100 20} shortcut 0x5006c labelsize 12 deactivate } MenuItem m_MenuSaveVoxelCounts { label {&Volumes && Statistics...} callback {this->OnMenuWriteVoxelCounts();} xywh {15 15 100 20} shortcut 0x50076 labelsize 12 hide deactivate } } Submenu m_MenuExport { label {&Export} open xywh {25 25 100 20} labelsize 12 divider } { Submenu m_MenuExportSlice { label {&Image Slice} open xywh {5 5 100 20} labelsize 12 deactivate } { MenuItem m_MenuExportSliceAxial { label {&Axial...} callback {OnMenuExportSlice(0);} xywh {55 55 100 20} labelsize 12 } MenuItem m_MenuExportSliceSagittal { label {&Sagittal...} callback {OnMenuExportSlice(1);} xywh {65 65 100 20} labelsize 12 } MenuItem m_MenuExportSliceCoronal { label {&Coronal...} callback {OnMenuExportSlice(2);} xywh {75 75 100 20} labelsize 12 } } Submenu m_MenuSaveScreenshot { label {&Screenshot} open xywh {15 15 100 20} labelsize 12 deactivate } { MenuItem m_MenuSaveScreenshotAxial { label {&Axial...} callback {OnMenuSaveScreenshot(0);} xywh {55 55 100 20} labelsize 12 } MenuItem m_MenuSaveScreenshotSaggital { label {&Sagittal...} callback {OnMenuSaveScreenshot(1);} xywh {55 55 100 20} labelsize 12 } MenuItem m_MenuSaveScreenshotCoronal { label {&Coronal...} callback {OnMenuSaveScreenshot(2);} xywh {55 55 100 20} labelsize 12 } } Submenu m_MenuSaveScreenshotSeries { label {Screenshot S&eries} open xywh {15 15 100 20} labelsize 12 deactivate } { MenuItem m_MenuSaveScreenshotSeriesAxial { label {&Axial...} callback {OnMenuSaveScreenshotSeries(0);} xywh {55 55 100 20} labelsize 12 } MenuItem m_MenuSaveScreenshotSeriesSaggital { label {&Sagittal...} callback {OnMenuSaveScreenshotSeries(1);} xywh {55 55 100 20} labelsize 12 } MenuItem m_MenuSaveScreenshotSeriesCoronal { label {&Coronal...} callback {OnMenuSaveScreenshotSeries(2);} xywh {55 55 100 20} labelsize 12 } } } MenuItem m_MenuResetAll { label {&Unload All Images} callback {this->OnMenuResetAll()} tooltip Reset xywh {5 5 30 20} shortcut 0x40075 labelsize 12 } MenuItem m_MenuLaunchNewInstance { label {&New ITK-SNAP Window} callback {this->OnMenuLaunchNewInstance()} tooltip Reset xywh {15 15 30 20} shortcut 0x5006e labelsize 12 divider } MenuItem {} { label {&Quit} callback {this->OnMenuQuit();} xywh {50 50 100 20} shortcut 0x40071 labelsize 12 } } Submenu {} { label {&Segmentation} xywh {10 10 100 20} labelsize 12 } { MenuItem m_MenuLoadSegmentation { label {&Load From Image...} callback {this->OnMenuLoadSegmentation();} tooltip {Load a previously created segmentation} xywh {20 20 100 20} shortcut 0x40064 labelsize 12 deactivate } MenuItem m_MenuNewSegmentation { label {&Clear} callback {this->OnMenuNewSegmentation()} tooltip {Creates a new empty segmentation} xywh {5 5 30 20} shortcut 0x4006e labelsize 12 divider } MenuItem m_MenuSaveSegmentation { label {&Save As Image...} callback {this->OnMenuSaveSegmentation();} tooltip {Save the segmentation results as an image} xywh {35 35 100 20} shortcut 0x40073 labelsize 12 deactivate } MenuItem m_MenuSaveSegmentationMesh { label {Export As Surface &Mesh...} callback {this->OnMenuSaveSegmentationMesh();} tooltip {Save the segmentation results as an image} xywh {45 45 100 20} shortcut 0x5006d labelsize 12 deactivate divider } MenuItem m_MenuLoadLabels { label {Load Label &Descriptions...} callback {this->OnMenuLoadLabels();} xywh {30 30 100 20} shortcut 0x4006c labelsize 12 } MenuItem m_MenuSaveLabels { label {Save Label D&escriptions...} callback {this->OnMenuSaveLabels();} xywh {45 45 100 20} shortcut 0x5006c labelsize 12 divider } MenuItem m_MenuShowVolumes { label {&Volumes and Statistics...} callback {this->OnMenuShowVolumes();} tooltip {Change the mapping between voxel coordinates and world coordinates} xywh {25 25 100 20} shortcut 0x50076 labelsize 12 } } Submenu {} { label {&Overlay} xywh {10 10 100 20} labelsize 12 } { MenuItem m_MenuLoadGreyOverlay { label {Add &Greyscale Overlay...} callback {this->OnMenuLoadGreyOverlay();} tooltip {Load a Grey image on top of an already loaded grayscale image} xywh {20 20 100 20} shortcut 0x50067 labelsize 12 deactivate } MenuItem m_MenuLoadRGBOverlay { label {Add &RGB Overlay...} callback {this->OnMenuLoadRGBOverlay();} tooltip {Load an RGB image on top of an already loaded grayscale image} xywh {20 20 100 20} shortcut 0x50072 labelsize 12 deactivate divider } MenuItem m_MenuUnloadOverlayLast { label {&Unload Last Overlay} callback {this->OnMenuUnloadOverlayLast()} tooltip {Unload Last Greyscale Overlay} xywh {5 5 30 20} labelsize 12 } MenuItem m_MenuUnloadOverlays { label {Unload &All Overlays} callback {this->OnMenuUnloadOverlays()} tooltip {Unload All Greyscale Overlays} xywh {5 5 30 20} labelsize 12 divider } MenuItem m_MenuLayerInspector { label {&Layer Inspector...} callback {OnMenuShowLayerInspector()} xywh {10 10 30 20} labelsize 12 } } Submenu {} { label {&Tools} open xywh {10 10 100 20} labelsize 12 } { Submenu {} { label {Active Main &Tool} open xywh {0 0 62 20} labelsize 12 } { MenuItem m_MenuCrosshairsMode { label {&Crosshair} callback {SetToolbarMode(CROSSHAIRS_MODE);} xywh {0 0 31 20} shortcut 0x31 value 1 labelsize 12 } MenuItem m_MenuZoomPanMode { label {&Zoom / Pan} callback {SetToolbarMode(NAVIGATION_MODE);} xywh {10 10 31 20} shortcut 0x32 labelsize 12 } MenuItem m_MenuPolygonMode { label {&Polygon} callback {SetToolbarMode(POLYGON_DRAWING_MODE);} xywh {20 20 31 20} shortcut 0x33 labelsize 12 } MenuItem m_MenuSNAPMode { label {&Snake ROI} callback {SetToolbarMode(ROI_MODE);} xywh {30 30 31 20} shortcut 0x34 labelsize 12 } MenuItem m_MenuPaintbrushMode { label {Paint&brush} callback {SetToolbarMode(PAINTBRUSH_MODE);} xywh {40 40 31 20} shortcut 0x35 labelsize 12 } MenuItem m_MenuAnnotationMode { label {&Annotation} callback {SetToolbarMode(ANNOTATION_MODE);} xywh {50 50 31 20} shortcut 0x36 labelsize 12 hide } } Submenu {} { label {Active &3D Tool} open xywh {15 15 62 20} labelsize 12 divider } { MenuItem m_MenuTrackballMode { label {&Trackball} callback {SetToolbarMode3D(TRACKBALL_MODE);} xywh {10 10 31 20} value 1 labelsize 12 } MenuItem m_MenuCrosshair3DMode { label {&Crosshair} callback {SetToolbarMode3D(CROSSHAIRS_3D_MODE);} xywh {20 20 31 20} labelsize 12 } MenuItem m_MenuScalpelMode { label {&Scalpel} callback {SetToolbarMode3D(SCALPEL_MODE);} xywh {30 30 31 20} labelsize 12 } MenuItem m_MenuSpraypaintMode { label {Spray&paint} callback {SetToolbarMode3D(SPRAYPAINT_MODE);} xywh {40 40 31 20} labelsize 12 } } MenuItem m_MenuIntensityCurve { label {Image &Contrast...} callback {this->OnMenuIntensityCurve();} xywh {20 20 100 20} shortcut 0x40069 labelsize 12 deactivate } MenuItem m_MenuColorMap { label {Image Color &Map...} callback {this->OnMenuColorMap();} xywh {20 20 100 20} shortcut 0x4006b labelsize 12 deactivate } MenuItem m_MenuImageInfo { label {&Image Info...} callback {this->OnMenuImageInfo();} xywh {10 10 100 20} labelsize 12 divider } MenuItem m_MenuReorientImage { label {&Reorient Image...} callback {this->OnMenuReorientImage();} tooltip {Change the mapping between voxel coordinates and world coordinates} xywh {30 30 100 20} labelsize 12 divider } MenuItem {} { label {&Display Options...} callback {OnMenuShowDisplayOptions();} xywh {10 10 100 20} labelsize 12 } } Submenu {} { label {&Layout} xywh {0 0 62 20} labelsize 12 } { MenuItem {} { label {Toggle User Interface Elements} callback {this->OnMenuViewToggleUI();} xywh {15 15 30 20} shortcut 0xffc0 labelsize 12 } MenuItem {} { label {Toggle Full-Screen Mode} callback {this->OnMenuViewToggleFullscreen();} xywh {0 0 30 20} shortcut 0xffc1 labelsize 12 } MenuItem {} { label {Restore Default Layout} callback {this->OnMenuViewRestoreDefault();} xywh {10 10 30 20} shortcut 0x40ffc0 labelsize 12 } } Submenu {} { label {&Help} xywh {5 5 100 20} labelsize 12 } { MenuItem {} { label {&Help Index...} callback {fl_open_uri("http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.HomePage");} xywh {35 35 100 20} shortcut 0xffbe labelsize 12 } MenuItem {} { label {&Keyboard shortcuts...} callback {fl_open_uri("http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.KeyboardShortcuts");} xywh {45 45 100 20} labelsize 12 } MenuItem {} { label {&Credits...} callback {fl_open_uri("http://www.itksnap.org/pmwiki/pmwiki.php?n=Credits");} xywh {25 25 100 20} shortcut 0x1ffbe labelsize 12 } MenuItem {} { label {&About...} callback {m_WinAbout->show();} xywh {35 35 100 20} labelsize 12 } MenuItem {} { label {Check for &Updates...} callback {this->OnMenuCheckForUpdate();} tooltip {Connect to the ITK-SNAP server to check if your version is up to date.} xywh {55 55 100 20} labelsize 12 } } } Fl_Group m_GrpControls {open xywh {0 24 145 711} box BORDER_FRAME color 0 } { Fl_Wizard m_WizControlPane { label {Control Pane} open xywh {0 25 145 700} box NO_BOX color 22 align 16 } { Fl_Group m_GrpToolbarPage {open xywh {0 25 145 700} color 53 labelsize 12 } { Fl_Group m_GrpInteractionModes { label {Main Toolbox} xywh {5 45 135 95} box PLASTIC_UP_BOX color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 } { Fl_Button m_BtnCrosshairsMode { callback {SetToolbarMode(CROSSHAIRS_MODE);} tooltip {Crosshairs tool: left clicking moves the cursor} image {Artwork/crosshair.gif} xywh {15 55 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX value 1 color 180 selection_color 180 align 16 when 1 } Fl_Button m_BtnNavigationMode { callback {SetToolbarMode(NAVIGATION_MODE);} tooltip {Navigation tool: zoom and pan around the 2D slices} image {Artwork/zoom.gif} xywh {55 55 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 180 align 16 when 1 } Fl_Button m_BtnPolygonMode { callback {SetToolbarMode(POLYGON_DRAWING_MODE);} tooltip {Polygon tool: slice-by-slice manual segmentation} image {Artwork/poly.gif} xywh {95 55 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 180 align 16 when 1 } Fl_Button m_BtnSNAPMode { callback {SetToolbarMode(ROI_MODE);} tooltip {Snake ROI tool: select a region of interest for automatic segmentation} image {Artwork/snake.gif} xywh {15 95 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 180 labelsize 10 align 144 when 1 } Fl_Button m_BtnPaintbrushMode { callback {SetToolbarMode(PAINTBRUSH_MODE);} tooltip {Paintbrush tool} image {Artwork/paintbrush.gif} xywh {55 95 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 180 labelfont 1 labelsize 20 align 16 when 1 } Fl_Button m_BtnAnnotationMode { label A callback {SetToolbarMode(ANNOTATION_MODE);} tooltip {Annotation tool (in development)} xywh {95 95 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x36 color 180 selection_color 180 labeltype EMBOSSED_LABEL labelfont 1 labelsize 18 align 144 when 1 hide } } Fl_Group {} { label {Tool Options} open xywh {5 164 135 167} box PLASTIC_UP_BOX color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 } { Fl_Wizard m_TabsToolOptions { label {Active Tool Settings} open xywh {5 165 135 166} box NO_BOX color 22 labeltype NO_LABEL labelsize 12 labelcolor 4 } { Fl_Group m_GrpToolOptionCrosshairs { label 1 xywh {5 165 135 165} labeltype NO_LABEL labelfont 1 labelsize 12 align 17 hide } { Fl_Output m_OutGreyProbe { label {Intensity:} tooltip {Intensity value at cursor position} xywh {15 214 50 20} box THIN_DOWN_BOX color 52 selection_color 48 labelsize 11 align 5 textsize 11 } Fl_Output m_OutLabelProbe { label {Label:} tooltip {Label at current position} xywh {80 214 50 20} box THIN_DOWN_BOX color 52 selection_color 48 labelsize 11 align 5 textsize 11 } Fl_Output m_OutLabelProbeText { label {Label description:} tooltip {Label at current position} xywh {15 254 115 20} box THIN_DOWN_BOX color 52 selection_color 48 labelsize 11 align 5 textsize 11 } Fl_Group {} { label {Crosshairs Tool} open tooltip {This tool navigates around the image. Press and drag with the left mouse button to reposition the cursor in the three slice views. Use the mouse wheel to change the slice. The arrow and PgUp/PgDn also work.} xywh {10 170 125 25} box PLASTIC_DOWN_BOX color 61 labelsize 12 align 16 } {} Fl_Check_Button m_BtnSynchronizeCursor { label {Multisession cursor} callback {this->OnSynchronizeCursorAction();} tooltip {Synchronizes the crosshair position across multple sessions of ITK-SNAP} xywh {15 285 115 20} down_box DOWN_BOX value 1 labelsize 11 } } Fl_Group m_GrpToolOptionPolygon { label 3 xywh {5 165 135 165} color 22 labelsize 10 labelcolor 4 hide } { Fl_Group {} { label {Polygon Tool} open tooltip {Use this tool to draw polygons. The left mouse button is used to add points to the polygon, the right button will close the polygon. After the polygon is closed you select and move control points. Once you 'accept' a polygon, it is added to the segmentation.} xywh {10 170 125 25} box PLASTIC_DOWN_BOX color 243 labelsize 12 align 16 } {} Fl_Group {} { label {Freehand drawing:} open xywh {15 217 120 78} labelsize 11 align 5 } { Fl_Round_Button m_InIRISFreehandContinuous { label continuous callback {OnIRISFreehandFittingRateUpdate()} xywh {20 220 115 15} type Radio down_box ROUND_DOWN_BOX labeltype NO_LABEL labelsize 12 } Fl_Round_Button m_InIRISFreehandLineSegment { label {line segments:} callback {OnIRISFreehandFittingRateUpdate()} xywh {20 235 115 15} type Radio down_box ROUND_DOWN_BOX value 1 labeltype NO_LABEL labelsize 12 } Fl_Value_Slider m_InIRISFreehandFittingRate { label {segment length} callback {OnIRISFreehandFittingRateUpdate()} tooltip {Changes what happens when you draw a freehand curve. The larger this number, the fewer line segments will be used to represent the freehand drawing.} xywh {40 255 90 15} type Horizontal labelsize 11 minimum 1 maximum 16 step 1 value 8 } Fl_Group {} { label {continuous curve} open xywh {35 220 15 15} labelsize 11 align 20 } {} Fl_Group {} { label {piecewise linear} open xywh {35 235 15 15} labelsize 11 align 20 } {} } Fl_Button {} { label {?} callback {this->ShowHTMLPage("ContextHelp_Freehand.html");} xywh {116 202 18 18} box PLASTIC_ROUND_UP_BOX color 180 labelfont 1 labelsize 12 align 16 } } Fl_Group m_GrpToolOptionSNAP { label 5 open xywh {5 165 135 165} labelsize 10 hide } { Fl_Group {} { label {Snake ROI Tool} open tooltip {Use this tool to enter the automatic segmentation mode. You can select a region of the image that contains the structure of interest by moving the sides and corners of the region's bounding box. Then enter the automatic segmentation mode. You can optionally change the voxel size of the region before running segmentation.} xywh {10 170 125 25} box PLASTIC_DOWN_BOX color 178 labelsize 12 align 16 } {} Fl_Button m_BtnResetROI { label {Reset ROI} callback {OnResetROIAction();} tooltip {Set region of interest to entire volume} xywh {20 265 105 25} box PLASTIC_UP_BOX color 181 labelsize 11 } Fl_Check_Button m_ChkResampleRegion { label {Resample ROI} tooltip {Check this is you want to change the voxel size of the ROI before entering automatic segmentation. This is recommended for large ROIs and anisotropic images.} xywh {20 200 110 25} down_box DOWN_BOX labelsize 11 } Fl_Button m_BtnStartSnake { label {Segment 3D} callback {OnSnakeStartAction();} xywh {20 295 105 25} box PLASTIC_UP_BOX shortcut 0x40061 color 181 labelfont 1 labelsize 11 } } Fl_Group m_GrpToolOptionZoomPan { label 2 open xywh {5 165 135 165} labelsize 10 } { Fl_Group {} { label {Zoom / Pan Tool} open tooltip {Use this tool to change the zoom level and to pan around the image. Hold and drag the left mouse button to zoom and hold and drag the right button to pan.} xywh {10 170 125 25} box PLASTIC_DOWN_BOX color 91 labelsize 12 align 16 } {} Fl_Check_Button m_ChkLinkedZoom { label { Zoom views together} callback {OnLinkedZoomChange();} tooltip {Select to use the same zoom factor in all three orthogonal projections of the image} xywh {10 200 120 15} down_box DOWN_BOX labelsize 10 } Fl_Check_Button m_ChkMultisessionZoom { label { Multi-session zoom} callback {OnMultisessionZoomChange();} tooltip {Select to synchronize zooming across multiple ITK-SNAP sessions} xywh {10 220 120 15} down_box DOWN_BOX labelsize 10 when 1 deactivate } Fl_Check_Button m_ChkMultisessionPan { label { Multi-session pan} callback {OnMultisessionPanChange();} tooltip {Select to synchronize panning across multiple ITK-SNAP sessions} xywh {10 240 120 15} down_box DOWN_BOX labelsize 10 when 1 deactivate } Fl_Value_Input m_InZoomLevel { label {Zoom:} callback {OnZoomLevelChange();} tooltip {Zoom is specified as the number of screen pixels per millimeter in patient space} xywh {55 265 45 20} color 52 labelsize 11 when 8 step 0.001 textsize 11 deactivate } Fl_Group m_GrpLinkedZoomUnits { label {px/mm} open xywh {100 265 30 20} labelsize 10 align 20 deactivate } {} Fl_Button {} { label {Reset all views} callback {this->OnResetAllViews2DAction();} xywh {25 295 95 25} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x40066 color 180 selection_color 94 labelsize 11 align 128 } } Fl_Group m_GrpToolOptionBrush { label 6 xywh {5 165 135 165} color 22 labelsize 10 labelcolor 4 hide } { Fl_Group {} { label {Paintbrush Tool} open tooltip {Use this tool to paint with the currently selected label. You can choose between different brush shapes and sizes. Use the right mouse button to paint with the clear label.} xywh {10 170 125 25} box PLASTIC_DOWN_BOX color 243 labelsize 12 align 16 } {} Fl_Choice m_InPaintbrushShape { label {Shape:} callback {OnPaintbrushAttributesUpdate()} open tooltip {Choose the shape of the paintbrush tool} xywh {55 200 80 20} down_box BORDER_BOX labelsize 12 when 1 textsize 12 } { MenuItem {m_ChoicePaintbrush[0]} { label Square xywh {5 5 31 21} labelsize 12 } MenuItem {m_ChoicePaintbrush[1]} { label Round xywh {5 5 31 21} labelsize 12 } MenuItem {m_ChoicePaintbrush[2]} { label Adaptive xywh {15 15 31 21} labelsize 12 } } Fl_Counter m_InPaintbrushSize { label {Size:} callback {OnPaintbrushAttributesUpdate()} xywh {55 224 80 16} type Simple box PLASTIC_THIN_UP_BOX color 180 selection_color 25 labelsize 12 align 4 minimum 1 maximum 100 step 1 value 1 textsize 11 } Fl_Check_Button m_BtnPaintbrushIsotropic { label Isotropic callback {OnPaintbrushAttributesUpdate()} tooltip {Enable this in order to make the brush size more or less independent of the voxel size} xywh {75 250 64 15} down_box DOWN_BOX labelsize 11 } Fl_Check_Button m_BtnPaintbrushChaseMode { label {Cursor chases brush} callback {OnPaintbrushAttributesUpdate()} tooltip {Enable this to make the crosshairs (the cursor) follow the paintbrush} xywh {10 267 120 15} down_box DOWN_BOX labelsize 11 } Fl_Check_Button m_BtnPaintbrush3D { label {3D brush} callback {OnPaintbrushAttributesUpdate()} tooltip {Enable to make the brush three-dimensional, so it paints in all three views at once} xywh {10 250 64 15} down_box DOWN_BOX labelsize 11 } Fl_Wizard m_WizPaintbrushParameters {open xywh {10 285 125 40} box NO_BOX } { Fl_Group {m_GrpPaintbrushParameters[0]} {open xywh {10 285 125 40} hide } {} Fl_Group {m_GrpPaintbrushParameters[1]} {open xywh {10 285 125 40} hide } {} Fl_Group {m_GrpPaintbrushParameters[2]} {open xywh {10 285 125 40} } { Fl_Value_Input m_InPaintbrushSmoothing { label {Smoothing:} callback {OnPaintbrushAttributesUpdate()} xywh {95 307 40 18} labelsize 11 minimum 1 maximum 100 step 1 value 15 textsize 11 } Fl_Value_Input m_InPaintbrushGranularity { label {Granularity:} callback {OnPaintbrushAttributesUpdate()} xywh {95 287 40 18} labelsize 11 step 0.01 value 0.2 textsize 11 } } } } Fl_Group m_GrpToolOptionAnnotation { xywh {5 165 135 165} labelsize 10 hide } { Fl_Group {} { label {Annotation Tool} open tooltip {To add a new line: Left-click and drag to draw line; use left mouse to edit line; right click to accept line. (2) To delete drawn lines: Right click(s) to delete the lines one by one in reverse order; or delete all the lines at once by the "delete" key. THIS IS A WORK IN PROGRESS.} xywh {10 170 125 25} box PLASTIC_DOWN_BOX color 243 labelsize 12 align 16 } {} Fl_Check_Button m_BtnAnnotationShownOnAllSlices { label {Shown on all slices} callback {OnAnnotationAttributesUpdate()} tooltip {Enable this in order to make the annotation drawn on a particular slice level to be shown on all other slices} xywh {10 205 120 15} down_box DOWN_BOX labelsize 11 } } } } Fl_Group {} { label {Segmentation Options} open xywh {5 354 135 211} box PLASTIC_UP_BOX color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 } { Fl_Button {} { label {Label editor} callback {OnEditLabelsAction();} tooltip {Press this button to bring up the label editor} xywh {20 500 105 25} box PLASTIC_UP_BOX color 180 labelsize 11 } Fl_Value_Slider m_InIRISLabelOpacity { label {Overall label opacity:} callback {o->take_focus(); this->OnIRISLabelOpacityChange();} tooltip {Change the transparency of the labels Keyboard shortcuts: 'a' to decrease, 'd' to increase, 's' to toggle.} xywh {15 475 115 15} type Horizontal labelsize 11 align 5 maximum 255 step 1 value 128 } Fl_Choice m_InDrawingColor { label {Active drawing label:} callback {o->take_focus(); OnDrawingLabelUpdate();} open tooltip {This selects the color label with which you paint when using manual tools and automatic segmentation} xywh {15 377 115 18} down_box BORDER_BOX color 52 labelsize 11 align 5 textsize 11 } {} Fl_Choice m_InDrawOverColor { label {Draw over:} callback {o->take_focus(); OnDrawOverLabelUpdate();} open tooltip {This selects the label OVER WHICH you paint. Normally you want to paint over all labels, but you can also paint over a specific label, only over unlabeled voxels (clear label) or over all visible labels. To change a label's visibility choose 'Edit Labels'.} xywh {15 412 115 18} down_box BORDER_BOX color 52 labelsize 11 align 5 textsize 11 } {} Fl_Check_Button m_ChkInvertPolygon { label {Draw inverted} callback {m_GlobalState->SetPolygonInvert(o->value() != 0);} tooltip {When "draw inverted" is checked, paint operations (polygon drawing, 3D tools, automatic segmentation) are applied to the inversly: the outside of the polygon is painted, etc.} xywh {15 435 115 20} down_box DOWN_BOX labelsize 11 } Fl_Button m_BtnIRISUndo { label Undo callback {OnUndoAction();} tooltip {Undo the last drawing operation} xywh {20 530 50 25} box PLASTIC_UP_BOX color 180 labelsize 11 deactivate } Fl_Button m_BtnIRISRedo { label Redo callback {OnRedoAction();} tooltip {Redo the last drawing operation} xywh {75 530 50 25} box PLASTIC_UP_BOX color 180 labelsize 11 deactivate } Fl_Button {} { label {?} callback {this->ShowHTMLPage("ContextHelp_SegmentationOptions.html");} xywh {115 437 18 18} box PLASTIC_ROUND_UP_BOX color 180 labelfont 1 labelsize 12 align 16 } } Fl_Group {} { label {3D Toolbox} open xywh {5 590 135 95} box PLASTIC_UP_BOX color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 } { Fl_Button m_BtnCrosshair3DMode { callback {SetToolbarMode3D(CROSSHAIRS_3D_MODE);} tooltip {3D navigation tool: use the left mouse button to position the crosshairs at point on the segmentation surface} image {Artwork/crosshair3D.gif} xywh {55 600 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 180 align 16 } Fl_Button m_BtnTrackballMode { callback {SetToolbarMode3D(TRACKBALL_MODE);} tooltip {3D trackball tool: left mouse button rotates the 3D view, right mouse button zooms and the middle button pans.} image {Artwork/rotate3d.gif} xywh {15 600 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX value 1 color 180 selection_color 180 align 16 } Fl_Button m_BtnScalpelMode { callback {SetToolbarMode3D(SPRAYPAINT_MODE);} tooltip {3D spraypaint tool. Click (and hold) the left mouse button to relabel voxels on the boundaries of segmented objects} image {Artwork/spray.gif} xywh {15 640 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 180 align 16 } Fl_Button m_BtnSpraypaintMode { callback {SetToolbarMode3D(SCALPEL_MODE);} tooltip {3D scalpel tool. Click at two locations in the 3D window to define a cutplane, then choose 'accept' to apply a paint operation on one side of the cutplane} image {Artwork/scalpel.gif} xywh {95 600 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 180 align 16 } } } Fl_Group m_GrpWelcomePage { xywh {0 25 145 695} color 53 hide } { Fl_Group {} { label {ITK-SNAP} open xywh {5 195 135 35} labelfont 3 labelsize 20 align 0 } {} Fl_Group m_InWelcomePageVersion { label {Version X.XX Build Date X.XXX itksnap.org} open xywh {5 225 135 70} labelsize 12 align 145 } {} Fl_Group {} { label {Tip of the day:} open xywh {5 455 135 254} color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 align 133 } { Fl_Button {} { label {Next Tip} callback {this->DisplayTips();} xywh {75 675 65 25} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 134 labelsize 12 } Fl_Help_View m_OutTips { xywh {5 460 135 210} } } } Fl_Group m_GrpSNAPPage {open xywh {0 25 145 680} color 53 hide } { Fl_Group {} { label {Image Interaction} open xywh {5 44 135 116} box PLASTIC_UP_BOX color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 } { Fl_Button m_BtnSNAPCrosshairs { callback {SetToolbarMode(CROSSHAIRS_MODE);} tooltip {Crosshairs: left clicking moves the cursor} image {Artwork/crosshair.gif} xywh {15 54 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x63 value 1 color 179 selection_color 180 align 16 } Fl_Button m_BtnSNAPNavigation { callback {SetToolbarMode(NAVIGATION_MODE);} tooltip {Navigation: zoom, pan, and rotate} image {Artwork/zoom.gif} xywh {55 54 35 35} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x6e color 180 selection_color 180 align 16 } Fl_Output m_OutSNAPProbe { label {Grey:} tooltip {Intensity value at cursor position} xywh {10 135 40 20} box THIN_DOWN_BOX color 52 selection_color 48 labelsize 10 align 5 textsize 10 } Fl_Output m_OutSNAPLabelProbe { label {Label:} tooltip {Label at cursor position} xywh {105 135 30 20} box THIN_DOWN_BOX color 52 selection_color 48 labelsize 10 align 5 textsize 10 } Fl_Group m_GrpSNAPCurrentColor { label {Segmentation Label:} open tooltip {Color of current label} xywh {10 110 125 10} box BORDER_BOX color 23 selection_color 57 labelsize 10 align 5 } {} Fl_Output m_OutSNAPSpeedProbe { label {Preproc.:} tooltip {Value of the 'speed' image under the cursor. The speed image affects how fast the active contour propagates at each point in the image.} xywh {55 135 45 20} box THIN_DOWN_BOX color 52 selection_color 48 labelsize 10 align 5 textsize 10 } } Fl_Group {} { label {Segmentation Pipeline} open xywh {5 185 135 350} box PLASTIC_UP_BOX color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 } { Fl_Wizard m_WizSegmentationPipeline { label {Segmentation Pipeline} open xywh {5 185 135 350} box NO_BOX color 23 labeltype NO_LABEL labelsize 12 labelcolor 4 } { Fl_Group m_GrpSNAPStepPreprocess { label {Preprocess Image} open xywh {10 190 125 340} color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 hide } { Fl_Button m_BtnPreprocess { label {Preprocess Image} callback {this->OnPreprocessAction();} tooltip {Use this button to preprocess the grayscale image to genereate the 'speed' image. The speed image affects how fast the active contour propagates at each point in the image.} xywh {15 400 115 25} box PLASTIC_UP_BOX color 180 labelsize 11 align 144 } Fl_Group m_GrpSnakeChoiceRadio { label {Snake mode:} open xywh {10 279 125 60} labeltype NO_LABEL labelsize 12 align 5 } { Fl_Round_Button m_RadSnakeInOut { callback {OnInOutSnakeSelect();} tooltip {In the inside/outside segmentation mode, the speed image is close to +1 at voxels that are likely to be in the structure of interest and is close to -1 in the background. The snake is attracted to places where the speed function is close to 0.} xywh {11 294 120 25} down_box ROUND_DOWN_BOX value 1 color 205 align 148 } Fl_Round_Button m_RadSnakeEdge { callback {OnEdgeSnakeSelect();} tooltip {In the edge-based segmentation mode, the speed image is close to 0 at 'edges' in the input image and is close to 1 in regions where the intensity is nearly constant. The snake is attracted to the edges in the image.} xywh {11 314 105 25} down_box ROUND_DOWN_BOX } } Fl_Button m_BtnAcceptPreprocessing { label { Next @-1>} callback {OnAcceptPreprocessingAction();} tooltip {Accept the 'speed' image and proceed to specifying the initial active contour.} xywh {75 505 55 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 align 16 } Fl_Button {} { label {@-1< Back } tooltip {Go to the previous step in the active snake pipeline} xywh {15 505 55 25} box PLASTIC_UP_BOX color 180 labelsize 12 labelcolor 32 align 16 deactivate } Fl_Button m_BtnLoadPreprocessedImage { label {Load from File} callback {this->OnLoadPreprocessedImageAction();} tooltip {Load the 'speed' image from an external file.} xywh {15 445 115 25} box PLASTIC_UP_BOX color 180 labelsize 11 align 144 } Fl_Group {} { label or open xywh {10 425 125 20} labelsize 11 align 16 } {} Fl_Group {} { label Preprocessing open xywh {10 190 125 40} box PLASTIC_DOWN_BOX color 78 labelsize 12 align 18 } { Fl_Group {} { label {Step 1 of 3} open xywh {15 190 115 25} labelfont 1 align 16 } {} } Fl_Group {} { label {A. Choose what kinds of image features will drive active contour evolution:} open xywh {10 235 125 45} labelsize 11 align 21 } {} Fl_Group {} { label {B. Use buttons below to define image regions or edges:} open xywh {10 351 125 45} labelsize 11 align 149 } {} Fl_Group {} {open xywh {10 325 125 10} color 78 labelsize 12 align 18 } {} Fl_Group {} {open xywh {10 460 125 10} color 78 labelsize 12 align 18 } {} Fl_Group {} { label {Intensity regions} open xywh {28 294 100 25} labelsize 12 labelcolor 136 align 20 } {} Fl_Group {} { label {Image edges} open xywh {28 314 100 25} labelsize 12 labelcolor 136 align 148 } {} Fl_Group {} { label {C. Press 'Next' to accept.} open xywh {10 476 125 25} labelsize 11 align 21 } {} } Fl_Group m_GrpSNAPStepInitialize { label {Initialize Segmentation} open xywh {10 190 125 340} color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 hide } { Fl_Button m_BtnAddBubble { label {Add Bubble} callback {OnAddBubbleAction()} tooltip {Add bubble at cursor position (shortcut: +)} xywh {15 285 55 35} box PLASTIC_UP_BOX color 180 labelsize 11 align 128 } Fl_Button m_BtnRemoveBubble { label {Remove Bubble} callback {OnRemoveBubbleAction()} tooltip {Remove highlighted bubble from list (shortcut: -)} xywh {75 285 55 35} box PLASTIC_UP_BOX color 180 labelsize 11 align 128 } Fl_Value_Slider m_InBubbleRadius { label {Radius:} callback {o->take_focus(); OnBubbleRadiusChange()} xywh {15 340 115 20} type Horizontal color 52 selection_color 197 labelsize 12 labelcolor 136 align 5 maximum 255 step 1 value 128 } Fl_Browser m_BrsActiveBubbles { label {Active bubbles:} callback {OnActiveBubblesChange()} tooltip {Click a bubble to modify radius or remove} xywh {15 380 115 75} type Hold color 52 labelsize 12 labelcolor 136 align 5 textsize 9 } Fl_Button m_BtnAcceptInitialization { label { Next @-1>} callback {OnAcceptInitializationAction();} tooltip {Accept initialization, begin running snake} xywh {75 505 55 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 align 16 } Fl_Button m_BtnRestartPreprocessing { label {@-1< Back } callback {OnRestartPreprocessingAction();} tooltip {Accept initialization, begin running snake} xywh {15 505 55 25} box PLASTIC_UP_BOX color 180 labelsize 12 align 16 } Fl_Group {} { label {A. Place bubbles in the image to initialize the active contour:} open xywh {10 235 125 45} labelsize 11 align 21 } {} Fl_Group {} { label {Snake Initialization} open tooltip {In this step, the active contour is initialized. It can be initialized by placing bubbles (seeds) inside of the structure of interest. Alternatively, results from manual segmentation can be used as initialization.} xywh {10 190 125 40} box PLASTIC_DOWN_BOX color 90 labelsize 12 align 18 } { Fl_Group {} { label {Step 2 of 3} open xywh {15 190 115 25} labelfont 1 align 16 } {} } Fl_Group {} { label {B. Press 'Next' to begin segmentation:} open xywh {10 470 125 30} labelsize 11 align 149 } {} Fl_Group {} {open xywh {10 455 125 10} color 90 labelsize 12 align 18 } {} } Fl_Group m_GrpSNAPStepSegment { label {Perform Segmentation} open xywh {5 185 135 350} color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 } { Fl_Output m_OutCurrentIteration { label {Iteration:} tooltip {Current iteration of active contour segmentation} xywh {80 425 50 20} color 52 selection_color 48 labelsize 12 labelcolor 136 align 5 textsize 10 } Fl_Button m_BtnSnakeParameters { label {Set Parameters} callback {OnSnakeParametersAction();} tooltip {Change active contour evolution parameters} xywh {15 282 115 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Choice m_InStepSize { label {Step size:} callback {OnSnakeStepSizeChange();} open tooltip {Number of iterations per step - affects how often the screen updates} xywh {15 425 55 20} down_box BORDER_BOX color 52 selection_color 7 labelsize 12 labelcolor 136 align 5 textsize 10 } {} Fl_Button m_BtnAcceptSegmentation { label Finish callback {OnAcceptSegmentationAction();} tooltip {Accept segmentation and return to the main program} xywh {75 505 55 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 } Fl_Group m_GrpSnakeControl { label {Snake controls:} open xywh {15 375 120 30} labeltype NO_LABEL labelsize 12 labelcolor 136 align 5 } { Fl_Button m_BtnSnakeRewind { label {@<<} callback {OnSnakeRewindAction();} tooltip {Reset the segmentation to the initial contour} xywh {15 377 28 25} box PLASTIC_UP_BOX color 180 align 18 } Fl_Button m_BtnSnakePlay { label {@>} callback {OnSnakePlayAction();} tooltip {Iterate continuously} xywh {45 377 28 25} box PLASTIC_UP_BOX color 180 align 22 } Fl_Button m_BtnSnakeStop { label {@square} callback {OnSnakeStopAction();} tooltip {Stop or pause active contour evolution} xywh {75 377 28 25} box PLASTIC_UP_BOX color 180 labelsize 8 } Fl_Button m_BtnSnakeStep { label {@|>} callback {OnSnakeStepAction();} tooltip {Perform one step in active contour evolution} xywh {105 377 28 25} box PLASTIC_UP_BOX color 180 align 18 } } Fl_Button m_BtnRestartInitialization { label {@-1< Back } callback {OnRestartInitializationAction();} tooltip {Return to active contour initialization step} xywh {15 505 55 25} box PLASTIC_UP_BOX color 180 selection_color 48 labelsize 12 align 16 } Fl_Group {} { label Segmentation open xywh {10 190 125 40} box PLASTIC_DOWN_BOX color 93 labelsize 12 align 18 } { Fl_Group {} { label {Step 3 of 3} open xywh {15 190 115 25} labelfont 1 align 16 } {} } Fl_Group {} { label {A. Edit the parameters of the snake evolution equation:} open xywh {10 235 125 40} labelsize 11 align 21 } {} Fl_Group {} { label {B. Use the buttons below to control snake evolution:} open xywh {10 330 125 45} labelsize 11 align 149 } {} Fl_Group {} { label {C. Press 'Finish' to accept segmentation:} open xywh {10 471 125 30} labelsize 11 align 21 } {} Fl_Group {} {open xywh {10 315 125 10} box PLASTIC_DOWN_BOX color 93 labelsize 12 align 18 } {} Fl_Group {} {open xywh {10 456 125 10} box PLASTIC_DOWN_BOX color 93 labelsize 12 align 18 } {} } } } Fl_Group {} { label {Display Options} open xywh {5 560 135 110} box PLASTIC_UP_BOX color 22 labeltype EMBOSSED_LABEL labelsize 12 labelcolor 4 } { Fl_Value_Slider m_InSNAPLabelOpacity { label {Label opacity:} callback {o->take_focus(); this->OnSNAPLabelOpacityChange();} tooltip {Transparency of the segmentation overlay} xywh {10 650 125 15} type Horizontal labelsize 12 align 5 maximum 255 step 1 value 128 } Fl_Button m_BtnPreprocessedColorMap { label {Color Map} callback {OnPreprocessedColorMapAction();} tooltip {Change the color map used to display the 'speed' image} xywh {65 605 70 20} box PLASTIC_UP_BOX color 180 labelsize 11 align 144 } Fl_Choice m_ChoiceSNAPView {open tooltip {Select whether to display the speed image or the original grayscale image} xywh {10 580 125 20} down_box BORDER_BOX labelsize 12 align 5 textsize 12 resizable } { MenuItem m_MenuSNAPViewOriginal { label {Original Grayscale} callback {OnSNAPViewOriginalSelect();} xywh {5 5 100 20} labelsize 11 } MenuItem m_MenuSNAPViewPreprocessed { label {Preprocessed Image} callback {OnViewPreprocessedSelect();} xywh {15 15 100 20} labelsize 11 } } Fl_Group {} { label {Image to display:} open xywh {10 565 125 15} labelsize 12 align 22 } {} } Fl_Button m_BtnCancelSegmentation { label {Cancel Segmentation} callback {OnCancelSegmentationAction();} tooltip {Cancel segmentation and return} xywh {5 680 135 25} box PLASTIC_UP_BOX color 180 labelsize 12 } } } Fl_Group {} { label Spacer open xywh {0 730 145 5} color 53 labeltype NO_LABEL resizable } {} } Fl_Group m_GrpRightPanePlaceholderNormal {open xywh {145 25 730 710} resizable } { Fl_Group m_GrpRightPane {open xywh {145 25 730 710} box FLAT_BOX color 166 } { Fl_Wizard {m_WizSliceLayout[0]} {open xywh {145 25 365 355} box UP_BOX } { Fl_Group {m_GrpSliceLayoutNormal[0]} {open xywh {145 25 365 355} } { Fl_Group {m_GrpSlicePlaceholder[0]} { xywh {150 30 333 320} resizable } { Fl_Box {m_SliceWindow[0]} { label {m_GrpIRISAxial(0)} xywh {150 30 333 320} box BORDER_BOX color 0 labelcolor 7 code0 {\#include "FLTKCanvas.h"} class FLTKCanvas } } Fl_Group {m_GrpSliceToolbar[0]} {open xywh {145 350 365 30} color 48 } { Fl_Button {m_BtnResetView[0]} { label {zoom to fit} callback {this->OnResetView2DAction(0);} tooltip {Restore the view to default zoom level} xywh {365 355 65 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 146 } Fl_Output {m_OutSliceIndex[0]} { xywh {435 355 70 20} box FLAT_BOX color 49 align 0 textsize 10 deactivate } Fl_Wizard {m_WizPolygon[0]} {open selected xywh {150 350 210 30} box NO_BOX hide } { Fl_Group {m_GrpPolygonInit[0]} {open xywh {150 350 210 30} hide } { Fl_Button {m_BtnPolygonPaste[0]} { label {paste polygon} callback {OnPastePolygonAction(0);} tooltip {Paste the previously used polygon into the window} xywh {270 355 80 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } Fl_Group {m_GrpPolygonDraw[0]} {open xywh {150 350 210 30} hide } { Fl_Button {m_BtnPolygonClose[0]} { label {close loop} callback {OnClosePolygonAction(0);} tooltip {Connect last point to the first point and enter polygon editing mode} xywh {180 355 60 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonUndoPoint[0]} { label {undo point} callback {OnUndoPointPolygonAction(0);} tooltip {Remove the last point from the polygon} xywh {245 355 60 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonClearDrawing[0]} { label clear callback {OnClearPolygonAction(0);} tooltip {Clear the polygon so you can start over} xywh {310 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } Fl_Group {m_GrpPolygonEdit[0]} {open xywh {150 350 210 30} } { Fl_Button {m_BtnPolygonAccept[0]} { label accept callback {OnAcceptPolygonAction(0);} tooltip {Fill in the polygon using the currently selected label} xywh {175 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonInsert[0]} { label split callback {OnInsertIntoPolygonSelectedAction(0);} tooltip {Insert a new vertex between pairs of selected vertices} xywh {265 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonDelete[0]} { label delete callback {OnDeletePolygonSelectedAction(0);} tooltip {Delete the selected vertices} xywh {220 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonClearEditing[0]} { label clear callback {OnClearPolygonAction(0);} tooltip {Clear the polygon so you can start over} xywh {310 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } } Fl_Group {} { xywh {145 350 5 30} color 154 resizable } {} } Fl_Group {m_GrpSliceSidebar[0]} { xywh {485 30 21 320} } { Fl_Pack {} {open xywh {485 30 21 75} } { Fl_Button {m_BtnPanelZoom[0]} { label {@+} callback {OnWindowFocus(0);} tooltip {Expand this view to occupy the entire screen} xywh {486 30 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 align 18 } Fl_Group {} {open xywh {486 50 20 5} } {} Fl_Button {m_BtnPanelCollapse[0]} { label {@\#+19<-} callback {OnWindowCollapse(0);} tooltip {Collapse ITK-SNAP so only this view is shown} xywh {486 55 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 align 18 } Fl_Group {} {open xywh {486 75 20 5} } {} Fl_Button {m_BtnSaveAsPNG[0]} { callback {OnActiveWindowSaveSnapshot(0);} tooltip {Save a snapshot of this window as a PNG image} image {Artwork/screencapture.gif} deimage {Artwork/screencapture2.gif} xywh {486 80 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 deactivate } Fl_Group {} {open xywh {486 100 20 5} } {} } Fl_Scrollbar {m_InSliceSlider[0]} { callback {OnSliceSliderChange(0);} xywh {487 105 18 245} box THIN_DOWN_BOX minimum 1 maximum 0 resizable } } } Fl_Group {m_GrpSliceLayoutTight[0]} { xywh {145 25 365 355} hide } {} } Fl_Wizard {m_WizSliceLayout[1]} {open xywh {510 25 365 355} box UP_BOX } { Fl_Group {m_GrpSliceLayoutNormal[1]} {open xywh {510 25 365 355} } { Fl_Group {m_GrpSlicePlaceholder[1]} {open xywh {515 30 333 320} resizable } { Fl_Box {m_SliceWindow[1]} { label {m_GrpIRISSagittal(1)} xywh {515 30 333 320} box BORDER_BOX color 0 labelcolor 7 code0 {\#include "FLTKCanvas.h"} class FLTKCanvas } } Fl_Group {m_GrpSliceToolbar[1]} {open xywh {510 350 365 30} } { Fl_Button {m_BtnResetView[1]} { label {zoom to fit} callback {this->OnResetView2DAction(1);} tooltip {Restore the view to default zoom level} xywh {730 355 62 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 146 } Fl_Output {m_OutSliceIndex[1]} { xywh {797 355 70 20} box FLAT_BOX color 49 align 0 textsize 10 deactivate } Fl_Wizard {m_WizPolygon[1]} {open xywh {515 350 210 30} box NO_BOX hide } { Fl_Group {m_GrpPolygonInit[1]} {open xywh {515 350 210 30} } { Fl_Button {m_BtnPolygonPaste[1]} { label {paste polygon} callback {OnPastePolygonAction(1);} tooltip {Paste the previously used polygon into the window} xywh {635 355 80 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } Fl_Group {m_GrpPolygonDraw[1]} { xywh {515 350 210 30} hide } { Fl_Button {m_BtnPolygonClose[1]} { label {close loop} callback {OnClosePolygonAction(1);} tooltip {Connect last point to the first point and enter polygon editing mode} xywh {545 355 60 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonUndoPoint[1]} { label {undo point} callback {OnUndoPointPolygonAction(1);} tooltip {Remove the last point from the polygon} xywh {610 355 60 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonClearDrawing[1]} { label clear callback {OnClearPolygonAction(1);} tooltip {Clear the polygon so you can start over} xywh {675 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } Fl_Group {m_GrpPolygonEdit[1]} {open xywh {515 350 210 30} hide } { Fl_Button {m_BtnPolygonAccept[1]} { label accept callback {OnAcceptPolygonAction(1);} tooltip {Fill in the polygon using the currently selected label} xywh {540 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonInsert[1]} { label split callback {OnInsertIntoPolygonSelectedAction(1);} tooltip {Insert a new vertex between pairs of selected vertices} xywh {630 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonDelete[1]} { label delete callback {OnDeletePolygonSelectedAction(1);} tooltip {Delete the selected vertices} xywh {585 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonClearEditing[1]} { label clear callback {OnClearPolygonAction(1);} tooltip {Clear the polygon so you can start over} xywh {675 355 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } } Fl_Group {} {open xywh {510 350 5 30} color 154 resizable } {} } Fl_Group {m_GrpSliceSidebar[1]} {open xywh {850 30 23 320} } { Fl_Pack {} {open xywh {850 30 21 75} } { Fl_Button {m_BtnPanelZoom[1]} { label {@+} callback {OnWindowFocus(1);} tooltip {Expand this view to occupy the entire screen} xywh {851 30 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 } Fl_Group {} {open xywh {851 50 20 5} } {} Fl_Button {m_BtnPanelCollapse[1]} { label {@\#+19<-} callback {OnWindowCollapse(1);} tooltip {Collapse ITK-SNAP so only this view is shown} xywh {851 55 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 align 18 } Fl_Group {} {open xywh {851 75 20 5} } {} Fl_Button {m_BtnSaveAsPNG[1]} { callback {OnActiveWindowSaveSnapshot(1);} tooltip {Save a snapshot of this window as a PNG image} image {Artwork/screencapture.gif} deimage {Artwork/screencapture2.gif} xywh {851 80 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 deactivate } Fl_Group {} {open xywh {851 100 20 5} } {} } Fl_Scrollbar {m_InSliceSlider[1]} { callback {this->OnSliceSliderChange(1);} xywh {852 105 18 245} box THIN_DOWN_BOX minimum 1 maximum 0 resizable } } } Fl_Group {m_GrpSliceLayoutTight[1]} {open xywh {510 25 365 355} hide } {} } Fl_Wizard {m_WizSliceLayout[2]} {open xywh {510 380 365 355} box UP_BOX } { Fl_Group {m_GrpSliceLayoutNormal[2]} {open xywh {510 380 365 355} } { Fl_Group {m_GrpSlicePlaceholder[2]} {open xywh {515 385 333 320} resizable } { Fl_Box {m_SliceWindow[2]} { label {m_GrpIRISCoronal(2)} xywh {515 385 333 320} box BORDER_BOX color 0 labelcolor 7 code0 {\#include "FLTKCanvas.h"} class FLTKCanvas } } Fl_Group {m_GrpSliceToolbar[2]} {open xywh {510 705 365 30} } { Fl_Button {m_BtnResetView[2]} { label {zoom to fit} callback {this->OnResetView2DAction(2);} tooltip {Restore the view to default zoom level} xywh {730 710 65 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 146 } Fl_Output {m_OutSliceIndex[2]} { xywh {800 710 70 20} box FLAT_BOX color 49 align 0 textsize 10 deactivate } Fl_Wizard {m_WizPolygon[2]} {open xywh {515 705 210 30} box NO_BOX hide } { Fl_Group {m_GrpPolygonInit[2]} {open xywh {515 705 210 30} } { Fl_Button {m_BtnPolygonPaste[2]} { label {paste polygon} callback {OnPastePolygonAction(2);} tooltip {Paste the previously used polygon into the window} xywh {635 710 80 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } Fl_Group {m_GrpPolygonDraw[2]} { xywh {515 705 210 30} hide } { Fl_Button {m_BtnPolygonClose[2]} { label {close loop} callback {OnClosePolygonAction(2);} tooltip {Connect last point to the first point and enter polygon editing mode} xywh {545 710 60 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonUndoPoint[2]} { label {undo point} callback {OnUndoPointPolygonAction(2);} tooltip {Remove the last point from the polygon} xywh {610 710 60 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonClearDrawing[2]} { label clear callback {OnClearPolygonAction(2);} tooltip {Clear the polygon so you can start over} xywh {675 710 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } Fl_Group {m_GrpPolygonEdit[2]} {open xywh {515 705 210 30} hide } { Fl_Button {m_BtnPolygonAccept[2]} { label accept callback {OnAcceptPolygonAction(2);} tooltip {Fill in the polygon using the currently selected label} xywh {540 710 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonInsert[2]} { label split callback {OnInsertIntoPolygonSelectedAction(2);} tooltip {Insert a new vertex between pairs of selected vertices} xywh {630 710 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonDelete[2]} { label delete callback {OnDeletePolygonSelectedAction(2);} tooltip {Delete the selected vertices} xywh {585 710 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } Fl_Button {m_BtnPolygonClearEditing[2]} { label clear callback {OnClearPolygonAction(2);} tooltip {Clear the polygon so you can start over} xywh {675 710 40 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 18 } } } Fl_Group {} {open xywh {510 705 5 30} resizable } {} } Fl_Group {m_GrpSliceSidebar[2]} {open xywh {851 385 20 320} } { Fl_Pack {} {open xywh {851 385 20 75} } { Fl_Button {m_BtnPanelZoom[2]} { label {@+} callback {OnWindowFocus(2);} tooltip {Expand this view to occupy the entire screen} xywh {851 385 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 } Fl_Group {} {open xywh {851 405 20 5} } {} Fl_Button {m_BtnPanelCollapse[2]} { label {@\#+19<-} callback {OnWindowCollapse(2);} tooltip {Collapse ITK-SNAP so only this view is shown} xywh {851 410 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 align 18 } Fl_Group {} {open xywh {851 430 20 5} } {} Fl_Button {m_BtnSaveAsPNG[2]} { callback {OnActiveWindowSaveSnapshot(2);} tooltip {Save a snapshot of this window as a PNG image} image {Artwork/screencapture.gif} deimage {Artwork/screencapture2.gif} xywh {851 435 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 deactivate } Fl_Group {} {open xywh {851 455 20 5} } {} } Fl_Scrollbar {m_InSliceSlider[2]} { callback {this->OnSliceSliderChange(2);} xywh {852 460 18 245} box THIN_DOWN_BOX minimum 1 maximum 0 resizable } } } Fl_Group {m_GrpSliceLayoutTight[2]} {open xywh {510 380 365 355} hide } {} } Fl_Wizard {m_WizSliceLayout[3]} {open xywh {145 380 365 355} box UP_BOX } { Fl_Group {m_GrpSliceLayoutNormal[3]} {open xywh {145 380 365 355} } { Fl_Group {m_GrpSlicePlaceholder[3]} {open xywh {150 385 355 320} resizable } { Fl_Box {m_SliceWindow[3]} { label 3D xywh {150 385 355 320} box BORDER_BOX color 0 labelcolor 7 class FLTKCanvas } } Fl_Group m_View3DToolbar {open xywh {145 705 365 30} color 214 } { Fl_Pack m_ToolbarRenderWindow {open xywh {355 710 148 20} type HORIZONTAL color 80 } { Fl_Button {m_BtnResetView[3]} { label {reset view} callback {OnMeshResetViewAction();} tooltip {Reset the 3D view to default zoom and camera position} xywh {355 710 65 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 146 } Fl_Button {m_BtnSaveAsPNG[3]} { callback {OnActiveWindowSaveSnapshot(3);} tooltip {Save a snapshot of this window as a PNG image} image {Artwork/screencapture.gif} deimage {Artwork/screencapture2.gif} xywh {483 710 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 deactivate } Fl_Button {m_BtnPanelCollapse[3]} { label {@\#+19<-} callback {OnWindowCollapse(3);} tooltip {Collapse ITK-SNAP so only this view is shown} xywh {458 710 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 } Fl_Button {m_BtnPanelZoom[3]} { label {@+} callback {OnWindowFocus(3);} tooltip {Expand this view to occupy the entire screen} xywh {433 710 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 } } Fl_Wizard m_WizRenderingToolbar { xywh {150 705 205 30} box NO_BOX } { Fl_Group m_GrpRenderingIRISPage {open xywh {150 705 205 30} } { Fl_Button m_BtnMeshUpdate { label {update mesh} callback {OnIRISMeshUpdateAction();} tooltip {Update the 3D rendering from the segmentation data} xywh {270 710 77 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 146 } Fl_Button m_BtnAccept3D { label accept callback {OnIRISMeshAcceptAction();} tooltip {Apply the last 3D paint operation to the image} xywh {210 710 55 20} box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX color 180 selection_color 94 labelsize 10 align 146 deactivate } } Fl_Group m_GrpRenderingSNAPPage {open xywh {150 705 205 30} hide } { Fl_Button m_BtnSNAPCrosshairsMode3D { callback {SetToolbarMode3D(CROSSHAIRS_3D_MODE);} tooltip {3D crosshair tool: click on the segmentation to move cursor} image {Artwork/crosshair3Dtiny.gif} xywh {155 707 25 25} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x63 color 180 selection_color 180 align 16 } Fl_Button m_BtnSNAPTrackballMode3D { callback {SetToolbarMode3D(TRACKBALL_MODE);} tooltip {3D trackball tool: rotate, zoom and pan 3D view} image {Artwork/rotate3dTiny.gif} xywh {185 707 25 25} type Radio box PLASTIC_UP_BOX down_box PLASTIC_DOWN_BOX shortcut 0x63 value 1 color 180 selection_color 180 align 16 } Fl_Button m_BtnSNAPMeshUpdate { label {update mesh} callback {OnSNAPMeshUpdateAction();} tooltip {Update the 3D mesh of the segmentation} xywh {220 710 75 20} box PLASTIC_UP_BOX color 180 labelsize 10 align 128 deactivate } Fl_Check_Button m_ChkContinuousView3DUpdate { label {auto- update} callback {OnSNAPMeshContinuousUpdateAction();} tooltip {Select this to have the mesh update automatically after every step in active contour evolution. This will slow down the program!} xywh {300 707 55 25} down_box DOWN_BOX labelsize 11 deactivate } } } Fl_Group {} {open xywh {145 705 5 30} resizable } {} } } Fl_Group {m_GrpSliceLayoutTight[3]} {open xywh {145 380 365 355} hide } {} } } } } Fl_Group m_GrpMainLayoutTight {open xywh {0 0 875 735} hide } { Fl_Group m_GrpRightPanePlaceholderTight {open xywh {0 0 875 735} resizable } {} } } } Fl_Window m_WinSplash { label {Welcome to ITK-SNAP} xywh {60 512 430 195} type Double align 16 resizable visible } { Fl_Group {} {open image {Artwork/logo_new.gif} xywh {5 5 420 155} color 0 align 16 } {} Fl_Group m_OutSplashMessage { label {Loading progress} open xywh {5 165 420 25} box BORDER_BOX color 55 labelsize 11 align 20 } {} Fl_Group {} { label {Initializing...} open xywh {325 15 95 20} labeltype ENGRAVED_LABEL align 24 } {} } Fl_Window m_WinProgress { label {SNAP Progress Meter} xywh {434 461 485 70} type Double box BORDER_BOX color 53 modal noborder visible } { Fl_Group {} { label {Working... Please Wait!} open xywh {0 0 480 25} labeltype EMBOSSED_LABEL labelfont 1 labelsize 12 align 16 } {} Fl_Group {} {open xywh {10 25 470 40} box ENGRAVED_BOX } { Fl_Slider m_OutProgressMeter { xywh {20 35 395 20} type {Horz Fill} box PLASTIC_DOWN_BOX selection_color 62 align 16 maximum 100 step 0.1 value 50 } Fl_Value_Output m_OutProgressCounter { label {%} xywh {420 35 40 20} color 7 labelsize 12 align 8 maximum 100 step 0.1 value 50 textsize 12 } } } Fl_Window m_WinColorMap { label {Preprocessed Image Color Map} xywh {60 50 315 105} type Double non_modal visible } { Fl_Wizard m_WizColorMap {open xywh {0 0 325 115} } { Fl_Group {m_GrpColorMapPage[0]} {open xywh {0 0 315 105} hide } { Fl_Box {m_BoxColorMap[0]} { xywh {15 25 285 25} box FLAT_BOX color 5 align 5 code0 {\#include "ColorMapBox.h"} class ColorMapBox } Fl_Group {} {open xywh {0 45 315 25} } { Fl_Group {} { label {-1.0} open xywh {0 45 35 25} labelsize 12 align 0 } {} Fl_Group {} { label {0.5} open xywh {205 45 40 25} labelsize 12 align 0 } {} Fl_Group {} { label {-0.5} open xywh {65 45 40 25} labelsize 12 align 0 } {} Fl_Group {} { label {0.0} open xywh {135 45 40 25} labelsize 12 align 0 } {} Fl_Group {} { label {1.0} open xywh {280 45 35 25} labelsize 12 align 0 } {} } Fl_Group {} { label {Preprocessed image intensity:} open xywh {15 5 285 20} labelsize 12 align 20 } {} Fl_Button {} { label Close callback {OnColorMapCloseAction();} xywh {235 75 65 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 } Fl_Choice {m_ChoiceColorMap[0]} { label {Color map:} callback {OnColorMapSelectAction();} open xywh {70 75 150 20} down_box BORDER_BOX labelsize 12 when 1 textsize 12 } { MenuItem {} { label {Black to White} xywh {20 20 100 20} labelsize 11 } MenuItem {} { label {Blue -- Black -- White} xywh {10 10 100 20} labelsize 11 } MenuItem {} { label {Blue -- White -- Red} xywh {40 40 100 20} labelsize 11 } } } Fl_Group {m_GrpColorMapPage[1]} {open xywh {0 0 325 110} } { Fl_Box {m_BoxColorMap[1]} { xywh {15 25 285 25} box FLAT_BOX color 5 align 5 code0 {\#include "ColorMapBox.h"} class ColorMapBox } Fl_Group {} {open xywh {0 45 315 25} } { Fl_Group {} { label {0.0} open xywh {0 45 35 25} labelsize 12 align 0 } {} Fl_Group {} { label {0.75} open xywh {205 45 40 25} labelsize 12 align 0 } {} Fl_Group {} { label {0.25} open xywh {65 45 40 25} labelsize 12 align 0 } {} Fl_Group {} { label {0.5} open xywh {135 45 40 25} labelsize 12 align 0 } {} Fl_Group {} { label {1.0} open xywh {280 45 35 25} labelsize 12 align 0 } {} } Fl_Group {} { label {Preprocessed image intensity:} open xywh {15 5 285 20} labelsize 12 align 20 } {} Fl_Button {} { label Close callback {OnColorMapCloseAction();} xywh {235 75 65 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 } Fl_Choice {m_ChoiceColorMap[1]} { label {Color map:} callback {OnColorMapSelectAction();} open xywh {70 75 150 20} down_box BORDER_BOX labelsize 12 when 1 textsize 12 } { MenuItem {} { label {Black to White} xywh {30 30 100 20} labelsize 11 } MenuItem {} { label {Black -- Yellow -- White} xywh {40 40 100 20} labelsize 11 } } } } } Fl_Window m_WinAbout { label {About ITK-SNAP} xywh {523 551 430 195} type Double code0 {\#include "FL/filename.H"} visible } { Fl_Group {} {open image {Artwork/logo_new.gif} xywh {6 5 420 155} color 0 align 16 } {} Fl_Group m_InAboutPageVersion { label {Version X.XX Build Date X.XX} open xywh {309 10 115 35} labeltype SHADOW_LABEL labelsize 11 align 153 } {} Fl_Group {} { label {ITK-SNAP on the web:} open xywh {10 170 185 20} labelsize 12 align 24 } {} Fl_Button {} { label {http://www.itksnap.org} xywh {200 170 220 20} box BORDER_FRAME down_box BORDER_FRAME color 176 selection_color 176 labelsize 12 labelcolor 176 } } Fl_Window m_WinConfirmDiscard { label {Discard Unsaved Changes?} callback {m_ChkHiddenDiscardChanges->value(0); m_WinConfirmDiscard->hide();} open xywh {920 369 500 130} type Double box PLASTIC_DOWN_BOX modal visible } { Fl_Button {} { label {Go Back} callback {m_ChkHiddenDiscardChanges->value(0); m_WinConfirmDiscard->hide();} xywh {250 90 90 30} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 hotspot } Fl_Button {} { label {Discard Changes} callback {m_ChkHiddenDiscardChanges->value(1); m_WinConfirmDiscard->hide();} xywh {350 90 125 30} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Group {} { label {The changes you have made to the segmentation will be lost if you continue. Please confirm that you want to discard these changes, or press 'go back' in order to save the changes first.} open xywh {85 15 400 70} align 149 } {} Fl_Group {} { label {!} open xywh {15 20 55 55} box OFLAT_BOX color 224 selection_color 37 labelfont 9 labelsize 48 labelcolor 7 align 16 } {} Fl_Check_Button m_ChkHiddenDiscardChanges { label button xywh {35 100 64 15} down_box DOWN_BOX hide deactivate } } Fl_Window m_WinOpenDropped { label {ITK-SNAP: Open Document} xywh {461 285 530 310} type Double hide modal } { Fl_Group {} { label {What should ITK-SNAP do with this image?} open xywh {10 40 455 25} labelsize 12 align 20 } {} Fl_Group {} { label {A. Open it in the current window} open xywh {15 90 500 50} box ENGRAVED_FRAME labelsize 12 align 5 } { Fl_Button {} { label {Load as Main Image} callback {this->OnOpenDroppedAction(1);} xywh {25 100 150 30} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label {Load as Segmentation} callback {this->OnOpenDroppedAction(2);} xywh {190 100 150 30} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label {Load as Overlay} callback {this->OnOpenDroppedAction(3);} xywh {355 100 150 30} box PLASTIC_UP_BOX color 180 labelsize 12 } } Fl_Group {} { label {B. Open it in a new ITK-SNAP window} open xywh {15 168 500 82} box ENGRAVED_FRAME labelsize 12 align 5 } { Fl_Button {} { label {Open in New ITK-SNAP} callback {this->OnOpenDroppedAction(4);} xywh {25 185 150 30} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Group {} { label {Initial layout of new window:} open xywh {190 200 305 50} labelsize 12 align 5 } { Fl_Round_Button {m_InOpenDroppedViewMode[0]} { label {Default 4-pane layout} xywh {195 205 140 20} type Radio down_box ROUND_DOWN_BOX value 1 labelsize 12 } Fl_Round_Button {m_InOpenDroppedViewMode[1]} { label {Compact layout, axial} xywh {195 225 140 20} type Radio down_box ROUND_DOWN_BOX labelsize 12 } Fl_Round_Button {m_InOpenDroppedViewMode[2]} { label {Compact layout, coronal} xywh {345 205 150 20} type Radio down_box ROUND_DOWN_BOX labelsize 12 } Fl_Round_Button {m_InOpenDroppedViewMode[3]} { label {Compact layout, sagittal} xywh {345 225 150 20} type Radio down_box ROUND_DOWN_BOX labelsize 12 } } } Fl_Group m_OutOpenDroppedFileName { label Filename open xywh {15 31 490 1} box BORDER_FRAME color 35 labeltype ENGRAVED_LABEL labelfont 3 labelsize 16 align 5 } {} Fl_Button {} { label Cancel callback {this->OnOpenDroppedAction(0);} xywh {425 270 80 30} box PLASTIC_UP_BOX color 35 labelsize 12 } } Fl_Window m_WinStatistics { label {ITK-SNAP Volumes and Statistics} xywh {458 232 680 320} type Double hide resizable } { Fl_Button m_BtnStatisticsUpdate { label Update callback {OnStatisticsUpdateAction();} xywh {85 285 120 25} box PLASTIC_UP_BOX color 180 labelfont 1 labelsize 12 } Fl_Button m_BtnStatisticsExport { label {Export to File...} callback {OnStatisticsExportAction();} xywh {345 285 120 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button m_BtnStatisticsClose { label Close callback {m_WinStatistics->hide();} xywh {475 285 120 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Box m_TableStatistics { label {Segmentation volumes and statistics: } xywh {10 40 660 231} color 53 selection_color 246 labeltype EMBOSSED_LABEL labelcolor 4 align 5 resizable code0 {\#include "StatisticsTable.h"} class StatisticsTable } Fl_Button m_BtnStatisticsCopy { label {Copy to Clipboard} callback {OnStatisticsCopyAction();} xywh {215 285 120 25} box PLASTIC_UP_BOX shortcut 0x400063 color 180 labelsize 12 } } Fl_Window m_WinTestPop { label Test open xywh {1247 403 45 20} type Single align 2 non_modal noborder visible } { Fl_Button m_BtnCollapsedViewRestore { label {@\#+11<-} callback {this->OnMenuViewRestoreDefault();} tooltip {Collapse ITK-SNAP so only this view is shown} xywh {2 2 20 20} box PLASTIC_UP_BOX color 180 labelsize 9 labelcolor 139 align 18 code0 {\#include "FL/Fl_Menu_Window.H"} } Fl_Button m_BtnCollapsedViewMenu { label {...} callback {this->OnCollapsedViewPopupMenu();} tooltip {Display ITK-SNAP Main Menu} xywh {24 2 20 20} box PLASTIC_UP_BOX color 180 labelsize 12 labelcolor 139 align 18 code0 {\#include "FL/Fl_Menu_Window.H"} } } Fl_Window m_WinPrecisionWarning { label {Insufficient Precision Warning} open xywh {344 409 495 155} type Double box PLASTIC_DOWN_BOX hide modal } { Fl_Button {} { label Ok callback {m_WinPrecisionWarning->hide();} xywh {380 115 100 30} box PLASTIC_UP_BOX shortcut 0xff0d color 180 labelfont 1 labelsize 12 hotspot } Fl_Group {} { label {The numerical precision of the image you loaded is greater than the 16 bit precision used by ITK-SNAP. Intensity values reported by ITK-SNAP will be approximate.} open xywh {85 10 400 75} align 149 } {} Fl_Group {} { label {!} open xywh {15 20 55 55} box OFLAT_BOX color 224 selection_color 37 labelfont 9 labelsize 48 labelcolor 7 align 16 } {} Fl_Check_Button m_ChkPrecisionWarningDisable { label {Do not show this warning again} xywh {275 85 210 20} down_box DOWN_BOX labelsize 12 } Fl_Button {} { label {Learn more...} callback {fl_open_uri("http://www.itksnap.org/pmwiki/pmwiki.php?n=Documentation.PrecisionWarning");} xywh {270 115 100 30} box PLASTIC_UP_BOX color 180 labelsize 12 } } } } itksnap/UserInterface/MainComponents/UserInterfaceBase.h0000644000076500000240000002701411553051537022751 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: UserInterfaceBase.h,v $ Language: C++ Date: $Date: 2011/04/18 15:06:07 $ Version: $Revision: 1.45 $ 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 __UserInterfaceBase__h_ #define __UserInterfaceBase__h_ // This should all be gone #include "SNAPCommonUI.h" #include "GlobalState.h" #include "FL/Fl_Double_Window.H" // Borland compiler stuff. Note to whoever went through the code and added all // these borland things: you just can't add ITK headers to headers like this one // that get included in lots of files. This makes compilation time insane!!! #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif // Forward refences to some classes class IRISApplication; class SystemInterface; class SNAPAppearanceSettings; class SliceWindowCoordinator; class Fl_Window; class Fl_Menu_Bar; class UserInterfaceLogic; struct Fl_Menu_Item; namespace itk { class Object; }; // Layout properties enum SliceViewConfiguration { FOUR_SLICE=0, AXIAL, SAGITTAL, CORONAL, THREED }; enum WindowSize { FULL_SIZE, HALF_SIZE, CUSTOM_SIZE }; struct DisplayLayout { bool full_screen; bool show_main_ui, show_panel_ui; SliceViewConfiguration slice_config; WindowSize size; int fs_restore[4]; }; class SNAPMainWindow : public Fl_Double_Window { public: SNAPMainWindow(int w, int h, const char *label=0) : Fl_Double_Window(w,h,label) { m_ParentUI = NULL; } SNAPMainWindow(int x, int y, int w, int h, const char *label=0) : Fl_Double_Window(x,y,w,h,label) { m_ParentUI = NULL; } virtual ~SNAPMainWindow() {} void resize(int x, int y, int w, int h); irisSetMacro(ParentUI, UserInterfaceLogic *); irisGetMacro(ParentUI, UserInterfaceLogic *); private: UserInterfaceLogic *m_ParentUI; }; /** * \class UserInterfaceBase * \brief Base class for the main user interface. */ class UserInterfaceBase { public: virtual ~UserInterfaceBase(void) {} /* Needed to avoid compiler warning */ // Methods for switching between SNAP and IRIS modes virtual void ShowIRIS() = 0; virtual void ShowSNAP() = 0; // Method for exiting IRIS virtual void OnMainWindowCloseAction() = 0; // Methods that set the IRIS Toolbar and 3D Toolbar modes virtual void SetToolbarMode(ToolbarModeType mode) = 0; virtual void SetToolbarMode3D(ToolbarMode3DType mode) = 0; // Menu item callbacks virtual void OnMenuNewSegmentation() = 0; virtual void OnMenuLoadGrey() = 0; virtual void OnMenuSaveGrey() = 0; virtual void OnMenuSaveGreyROI() = 0; virtual void OnMenuLoadRGB() = 0; virtual void OnMenuLoadGreyOverlay() = 0; virtual void OnMenuLoadRGBOverlay() = 0; virtual void OnMenuUnloadOverlayLast() = 0; virtual void OnMenuUnloadOverlays() = 0; virtual void OnMenuLoadSegmentation() = 0; virtual void OnMenuSaveSegmentation() = 0; virtual void OnMenuSaveSegmentationMesh() = 0; virtual void OnMenuSavePreprocessed() = 0; virtual void OnMenuSaveLevelSet() = 0; virtual void OnMenuLoadLabels() = 0; virtual void OnMenuSaveLabels() = 0; virtual void OnMenuSaveScreenshot(unsigned int iSlice) = 0; virtual void OnMenuSaveScreenshotSeries(unsigned int iSlice) = 0; virtual void OnMenuLoadAdvection() = 0; virtual void OnMenuWriteVoxelCounts() = 0; virtual void OnLoadRecentAction(unsigned int iRecent) = 0; virtual void OnMenuIntensityCurve() = 0; virtual void OnMenuColorMap() = 0; virtual void OnMenuShowDisplayOptions() = 0; virtual void OnMenuShowLayerInspector() = 0; virtual void OnMenuExportSlice(unsigned int iSlice) = 0; virtual void OnMenuImageInfo() = 0; virtual void OnMenuReorientImage() = 0; virtual void OnMenuShowVolumes() = 0; virtual void OnMenuQuit() = 0; virtual void OnMenuCheckForUpdate() = 0; virtual void OnMenuResetAll() = 0; virtual void OnMenuViewToggleUI() = 0; virtual void OnMenuViewToggleFullscreen() = 0; virtual void OnMenuViewRestoreDefault() = 0; virtual void OnMenuLaunchNewInstance() = 0; // IRIS: Slice selection actions virtual void OnSliceSliderChange(int id) = 0; virtual void UpdatePositionDisplay(int id) = 0; virtual void OnSynchronizeCursorAction() = 0; // IRIS: Zoom/pan interaction callbacks virtual void OnResetView2DAction(unsigned int window) = 0; virtual void OnResetAllViews2DAction() = 0; virtual void OnLinkedZoomChange() = 0; virtual void OnZoomLevelChange() = 0; virtual void OnMultisessionZoomChange() = 0; virtual void OnMultisessionPanChange() = 0; // IRIS: Color label selection and editing callbacks virtual void OnDrawingLabelUpdate() = 0; virtual void OnDrawOverLabelUpdate() = 0; virtual void UpdateEditLabelWindow() = 0; virtual void OnEditLabelsAction() = 0; // IRIS: Polygon buttons callbacks virtual void OnClosePolygonAction(unsigned int window) = 0; virtual void OnUndoPointPolygonAction(unsigned int window) = 0; virtual void OnClearPolygonAction(unsigned int window) = 0; virtual void OnAcceptPolygonAction(unsigned int window) = 0; virtual void OnDeletePolygonSelectedAction(unsigned int window) = 0; virtual void OnInsertIntoPolygonSelectedAction(unsigned int window) = 0; virtual void OnPastePolygonAction(unsigned int window) = 0; virtual void OnIRISFreehandFittingRateUpdate() = 0; // IRIS: Paintbrush tools virtual void OnPaintbrushAttributesUpdate() = 0; virtual void OnPaintbrushPaint() = 0; virtual void UpdatePaintbrushAttributes() = 0; // IRIS: Annotation mode virtual void OnAnnotationAttributesUpdate() = 0; // IRIS: 3D Window callbacks virtual void OnMeshResetViewAction() = 0; virtual void OnIRISMeshUpdateAction() = 0; virtual void OnIRISMeshAcceptAction() = 0; // IRIS: ROI manipulation callbacks virtual void OnResetROIAction() = 0; virtual void OnSnakeStartAction() = 0; // SNAP Preprocessing page actions virtual void OnInOutSnakeSelect() = 0; virtual void OnEdgeSnakeSelect() = 0; virtual void OnPreprocessAction() = 0; virtual void OnPreprocessClose() = 0; virtual void OnLoadPreprocessedImageAction() = 0; virtual void OnAcceptPreprocessingAction() = 0; // SNAP Color map operations virtual void OnPreprocessedColorMapAction() = 0; virtual void OnColorMapCloseAction() = 0; virtual void OnColorMapSelectAction() = 0; // SNAP Initialization page actions virtual void OnAddBubbleAction() = 0; virtual void OnRemoveBubbleAction() = 0; virtual void OnActiveBubblesChange() = 0; virtual void OnBubbleRadiusChange() = 0; virtual void OnAcceptInitializationAction() = 0; // SNAP Segmentation page actions virtual void OnRestartInitializationAction() = 0; virtual void OnSnakeParametersAction() = 0; virtual void OnAcceptSegmentationAction() = 0; virtual void OnSnakeRewindAction() = 0; virtual void OnSnakeStopAction() = 0; virtual void OnSnakePlayAction() = 0; virtual void OnSnakeStepAction() = 0; virtual void OnSnakeStepSizeChange() = 0; virtual void OnRestartPreprocessingAction() = 0; virtual void OnCancelSegmentationAction() = 0; // SNAP: display interaction actions virtual void OnSNAPViewOriginalSelect() = 0; virtual void OnViewPreprocessedSelect() = 0; virtual void UpdateMainLabel() = 0; virtual void DeleteColorLabelMenu(Fl_Menu_Item *menu) = 0; virtual Fl_Menu_Item *GenerateColorLabelMenu(bool,bool,bool) = 0; // Opacity sliders virtual void OnIRISLabelOpacityChange() = 0; virtual void OnSNAPLabelOpacityChange() = 0; // Undo/Redo buttons virtual void OnUndoAction() = 0; virtual void OnRedoAction() = 0; // SNAP: 3D window related callbacks virtual void OnSNAPMeshUpdateAction() = 0; virtual void OnSNAPMeshContinuousUpdateAction() = 0; // Volume and statistics actions virtual void OnStatisticsUpdateAction() = 0; virtual void OnStatisticsExportAction() = 0; virtual void OnStatisticsCopyAction() = 0; // virtual void Activate3DAccept(bool on) = 0; // Help related callbacks virtual void OnLaunchTutorialAction() = 0; virtual void ShowHTMLPage(const char *link) = 0; // Window size manipulation calls virtual void OnWindowFocus(int i) = 0; virtual void OnWindowCollapse(int i) = 0; // Save as PNG virtual void OnActiveWindowSaveSnapshot(unsigned int window) = 0; // The following methods are not referenced in the .fl file, but are defined here // so that other classes in the project can include UserInterfaceBase.h instead of // UserInterfaceLogic.h; this way there is not a huge build every time we change // a user interface element virtual IRISApplication *GetDriver() const = 0; virtual SystemInterface *GetSystemInterface() const = 0; virtual SNAPAppearanceSettings *GetAppearanceSettings() const = 0; virtual SliceWindowCoordinator *GetSliceCoordinator() const = 0; virtual void OnMainImageUpdate() = 0; virtual void OnOverlayImageUpdate() = 0; virtual void OnImageGeometryUpdate() = 0; virtual void RedrawWindows() = 0; virtual void OnIRISMeshDisplaySettingsUpdate() = 0; virtual void ResetScrollbars() = 0; virtual void UpdateImageProbe() = 0; virtual void OnLabelListUpdate() = 0; virtual void OnSegmentationImageUpdate(bool) = 0; virtual void CenterChildWindowInMainWindow(Fl_Window *) = 0; virtual void OnPreprocessingPreviewStatusUpdate(bool) = 0; virtual void OnSpeedImageUpdate() = 0; virtual void OnCrosshairPositionUpdate(bool flagBroadcastUpdate = true) = 0; virtual void OnViewPositionsUpdate(bool flagBroadcastUpdate = true) = 0; virtual void OnTrackballUpdate(bool flagBroadcastUpdate = true) = 0; virtual void OnPolygonStateUpdate(unsigned int) = 0; virtual void OnZoomUpdate(bool flagBroadcastUpdate = true) = 0; virtual void OnIRISMeshEditingAction() = 0; // Progress callbacks virtual void OnITKProgressEvent(itk::Object *source, const itk::EventObject &event) = 0; // Non-callbacks virtual double GetFreehandFittingRate() = 0; virtual void StoreUndoPoint(const char *text) = 0; virtual void ClearUndoPoints() = 0; virtual void OnHiddenFeaturesToggleAction() = 0; virtual void DisplayTips() = 0; virtual void OpenDraggedContent(const char *fn_open, bool interactive) = 0; virtual void OnOpenDroppedAction(int selection) = 0; virtual DisplayLayout GetDisplayLayout() const = 0; virtual void SetDisplayLayout(DisplayLayout dlo) = 0; virtual Fl_Menu_Bar* GetMainMenuBar() = 0; virtual Fl_Window* GetMainWindow() = 0; virtual Fl_Window* GetPopupToolbarWindow() = 0; virtual void OnCollapsedViewPopupMenu() = 0; protected: GlobalState *m_GlobalState; }; #endif // __UserInterfaceBase__h_ itksnap/UserInterface/MainComponents/UserInterfaceLogic.cxx0000644000076500000240000062756211560267766023540 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: UserInterfaceLogic.cxx,v $ Language: C++ Date: $Date: 2011/05/04 15:25:42 $ Version: $Revision: 1.124 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #if defined(_MSC_VER) #pragma warning ( disable : 4996 ) #endif #ifdef __APPLE__ #include #endif #include "FL/Fl.H" #include "FL/Fl_Native_File_Chooser.H" #include "FL/filename.H" #include "FL/x.H" #include "UserInterfaceLogic.h" #include "GlobalState.h" #include "GreyImageWrapper.h" #include "RGBImageWrapper.h" #include "EdgePreprocessingImageFilter.h" #include "LayerInspectorUILogic.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "IRISVectorTypesToITKConversion.h" #include "LabelEditorUILogic.h" #include "RestrictedImageIOWizardLogic.h" #include "SNAPImageData.h" #include "SNAPLevelSetDriver.h" #include "SNAPRegistryIO.h" #include "SmoothBinaryThresholdImageFilter.h" #include "SystemInterface.h" #include "IRISSliceWindow.h" #include "SNAPSliceWindow.h" #include "Window3D.h" // Additional UI component inludes #include "AppearanceDialogUILogic.h" #include "HelpViewerLogic.h" #include "PreprocessingUILogic.h" #include "SnakeParametersUILogic.h" #include "ResizeRegionDialogLogic.h" #include "RestoreSettingsDialogLogic.h" #include "ReorientImageUILogic.h" #include "MeshIOWizardUILogic.h" #include "SimpleFileDialogLogic.h" #include "SliceWindowCoordinator.h" #include "SNAPAppearanceSettings.h" #include "FLTKWidgetActivationManager.h" #include "itkImageIOBase.h" #include // #include #include #include #include #include // Global pointer to the 'current' UI object UserInterfaceLogic* UserInterfaceLogic::m_GlobalUI = NULL; // Disable some utterly annoying windows messages #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #pragma warning ( disable : 4503 ) #endif using namespace std; #define COLORBAR_LABEL FL_FREE_LABELTYPE void xyz_draw(const Fl_Label *label, int x, int y, int w, int h, Fl_Align align) { // We can't trust the label's color because it can be changed when the menu item // is selected. Instead, we encode the color in the label itself using octal notation unsigned int box_color; sscanf(label->value,"%x",&box_color); fl_font(label->font, label->size); fl_color((Fl_Color)label->color); fl_draw(label->value + 9, x + h + label->size / 2, y, w-h, h, FL_ALIGN_LEFT); fl_draw_box(FL_BORDER_BOX, x, y, h, h, (Fl_Color) box_color); } void xyz_measure(const Fl_Label *label, int &w, int &h) { fl_font(label->font, label->size); fl_measure(label->value + 9, w, h); w += h + label->size / 2; } /** * \class GreyImageInfoCallback * \brief Adapter for the interface ImageInfoCallbackInterface, used to * pass on registry information to the IO Wizard. */ class GreyImageInfoCallback : public ImageInfoCallbackInterface { public: virtual ~GreyImageInfoCallback() {} GreyImageInfoCallback(SystemInterface *system) { m_SystemInterface = system; } // This method finds the registry associated with a file bool FindRegistryAssociatedWithImage(const char *file, Registry ®istry) { m_Registry.Clear(); if(!m_SystemInterface->FindRegistryAssociatedWithFile(file, m_Registry)) return false; registry = m_Registry.Folder("Files.Grey"); return true; } // This method updates the registry with values that the user specified void UpdateRegistryAssociatedWithImage(const char *file, Registry &folder) { // Create a registry into which to load the values Registry regNew; m_SystemInterface->FindRegistryAssociatedWithFile(file, regNew); // Update the corresponding folder regNew.Folder("Files.Grey").Update(folder); // Associate the settings m_SystemInterface->AssociateRegistryWithFile(file, regNew); } private: SystemInterface *m_SystemInterface; Registry m_Registry; }; class UserInterfaceLogicMemberObserver : public FLTKWidgetActivationManager::Observer { public: // Member function type typedef void (UserInterfaceLogic::*TMemberFunctionPointer)( UserInterfaceLogic::UIStateFlags flag, bool value); // Constructor UserInterfaceLogicMemberObserver( UserInterfaceLogic *p, TMemberFunctionPointer member) { m_Pointer = p; m_Member = member; } // Callback void OnStateChange(UserInterfaceLogic::UIStateFlags flag, bool state) { if(m_Member && m_Pointer) { ((*m_Pointer).*(m_Member))(flag, state); } } private: UserInterfaceLogic *m_Pointer; TMemberFunctionPointer m_Member; }; void UserInterfaceLogic ::InitializeActivationFlags() { unsigned int i; // --------------------------------------------------------- // Intialize activation object // --------------------------------------------------------- m_Activation = new FLTKWidgetActivationManager; // --------------------------------------------------------- // Configure Flag Relationships // --------------------------------------------------------- // Set the parent-child relationships between flags m_Activation->SetFlagImplies(UIF_SNAP_SPEED_AVAILABLE, UIF_SNAP_ACTIVE); m_Activation->SetFlagImplies(UIF_SNAP_MESH_CONTINUOUS_UPDATE, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->SetFlagImplies(UIF_GRAY_LOADED, UIF_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_RGB_LOADED, UIF_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_IRIS_WITH_GRAY_LOADED, UIF_GRAY_LOADED); m_Activation->SetFlagImplies(UIF_IRIS_WITH_BASEIMG_LOADED, UIF_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_IRIS_WITH_BASEIMG_LOADED, UIF_IRIS_ACTIVE); m_Activation->SetFlagImplies(UIF_IRIS_WITH_GRAY_LOADED, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_IRIS_MESH_DIRTY, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_IRIS_MESH_ACTION_PENDING, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_IRIS_ROI_VALID, UIF_GRAY_LOADED); // m_Activation->SetFlagImplies(UIF_LINKED_ZOOM, UIF_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_SNAP_PREPROCESSING_ACTIVE, UIF_SNAP_ACTIVE); m_Activation->SetFlagImplies(UIF_SNAP_PAGE_PREPROCESSING, UIF_SNAP_ACTIVE); m_Activation->SetFlagImplies(UIF_SNAP_PAGE_BUBBLES, UIF_SNAP_ACTIVE); m_Activation->SetFlagImplies(UIF_SNAP_PAGE_SEGMENTATION, UIF_SNAP_ACTIVE); m_Activation->SetFlagImplies(UIF_SNAP_SNAKE_RUNNING, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->SetFlagImplies(UIF_SNAP_SNAKE_EDITABLE, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->SetFlagImplies(UIF_SNAP_SNAKE_EDITABLE, UIF_SNAP_SNAKE_RUNNING, true, false); m_Activation->SetFlagImplies(UIF_SNAP_SNAKE_INITIALIZED, UIF_SNAP_ACTIVE); m_Activation->SetFlagImplies(UIF_UNDO_POSSIBLE, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_REDO_POSSIBLE, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->SetFlagImplies(UIF_UNSAVED_CHANGES, UIF_IRIS_WITH_BASEIMG_LOADED); // Set up the exclusive relationships between flags m_Activation->SetFlagImplies( UIF_SNAP_ACTIVE, UIF_IRIS_ACTIVE, true, false ); m_Activation->SetFlagImplies(UIF_SNAP_PREPROCESSING_DONE, UIF_SNAP_PREPROCESSING_ACTIVE, true, false ); m_Activation->SetFlagsMutuallyExclusive( UIF_SNAP_PAGE_SEGMENTATION, UIF_SNAP_PAGE_BUBBLES, UIF_SNAP_PAGE_PREPROCESSING ); // --------------------------------------------------------- // Add observers to flags // --------------------------------------------------------- m_Activation->AddObserver( new UserInterfaceLogicMemberObserver( this, &UserInterfaceLogic::OnUnsavedChangesStateChange), UIF_UNSAVED_CHANGES); m_Activation->AddObserver( new UserInterfaceLogicMemberObserver( this, &UserInterfaceLogic::OnMeshAvailabilityStateChange), UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddObserver( new UserInterfaceLogicMemberObserver( this, &UserInterfaceLogic::OnMeshAvailabilityStateChange), UIF_SNAP_SNAKE_INITIALIZED); // --------------------------------------------------------- // Relate flags to widgets // --------------------------------------------------------- // Link widget activation to flags m_Activation->AddWidget(m_BtnAcceptPreprocessing, UIF_SNAP_PREPROCESSING_DONE); m_Activation->AddWidget(m_ChoiceSNAPView, UIF_SNAP_PREPROCESSING_DONE); m_Activation->AddWidget(m_BtnPreprocessedColorMap, UIF_SNAP_SPEED_AVAILABLE); m_Activation->AddWidget(m_BtnSNAPMeshUpdate, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->AddWidget(m_BtnMeshUpdate, UIF_IRIS_MESH_DIRTY); m_Activation->AddWidget(m_BtnStartSnake, UIF_IRIS_ROI_VALID); m_Activation->AddWidget(m_InZoomLevel, UIF_LINKED_ZOOM); m_Activation->AddWidget(m_GrpLinkedZoomUnits, UIF_LINKED_ZOOM); m_Activation->AddWidget(m_ChkMultisessionZoom, UIF_LINKED_ZOOM); m_Activation->AddWidget(m_ChkMultisessionPan, UIF_LINKED_ZOOM); m_Activation->AddWidget(m_BtnAccept3D, UIF_IRIS_MESH_ACTION_PENDING); m_Activation->AddWidget(m_BtnAcceptSegmentation, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->AddWidget(m_BtnRestartInitialization, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->AddWidget(m_BtnSnakePlay, UIF_SNAP_SNAKE_EDITABLE); m_Activation->AddWidget(m_BtnSnakeRewind, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->AddWidget(m_BtnSnakeStop, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->AddWidget(m_BtnSnakeStep, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->AddWidget(m_BtnIRISUndo, UIF_UNDO_POSSIBLE); m_Activation->AddWidget(m_BtnIRISRedo, UIF_REDO_POSSIBLE); m_Activation->AddWidget(m_BtnSNAPMode, UIF_GRAY_LOADED); // Add more complex relationships m_Activation->AddCheckBox(m_ChkContinuousView3DUpdate, UIF_SNAP_SNAKE_INITIALIZED, false, false); // Activate slice-related widgets indexed by dimension for(i = 0; i < 3; i++) { m_Activation->AddWidget(m_InSliceSlider[i], UIF_BASEIMG_LOADED); m_Activation->AddWidget(m_OutSliceIndex[i], UIF_BASEIMG_LOADED); } // Activate the widgets that have four copies for(i = 0; i < 4; i++) { m_Activation->AddWidget(m_BtnSaveAsPNG[i], UIF_BASEIMG_LOADED); m_Activation->AddWidget(m_BtnResetView[i], UIF_BASEIMG_LOADED); m_Activation->AddWidget(m_BtnPanelZoom[i], UIF_BASEIMG_LOADED); m_Activation->AddWidget(m_BtnPanelCollapse[i], UIF_BASEIMG_LOADED); } // Link menu items to flags m_Activation->AddMenuItem(m_MenuLoadGrey, UIF_IRIS_ACTIVE); m_Activation->AddMenuItem(m_MenuLoadRGB, UIF_IRIS_ACTIVE); m_Activation->AddMenuItem(m_MenuExport, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuResetAll, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSave, UIF_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSaveGrey, UIF_IRIS_WITH_GRAY_LOADED); m_Activation->AddMenuItem(m_MenuLoadGreyOverlay, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuUnloadOverlayLast, UIF_OVERLAY_LOADED); m_Activation->AddMenuItem(m_MenuUnloadOverlays, UIF_OVERLAY_LOADED); m_Activation->AddMenuItem(m_MenuLoadRGBOverlay, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuLoadSegmentation, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuNewSegmentation, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSaveGreyROI, UIF_SNAP_ACTIVE); m_Activation->AddMenuItem(m_MenuSaveSegmentation, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSaveSegmentationMesh, UIF_MESH_SAVEABLE); m_Activation->AddMenuItem(m_MenuSaveLabels, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuLoadLabels, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSaveVoxelCounts, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuShowVolumes, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSaveScreenshot, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSaveScreenshotSeries, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuIntensityCurve, UIF_GRAY_LOADED); m_Activation->AddMenuItem(m_MenuColorMap, UIF_GRAY_LOADED); m_Activation->AddMenuItem(m_MenuExportSlice, UIF_GRAY_LOADED); m_Activation->AddMenuItem(m_MenuSavePreprocessed, UIF_SNAP_PREPROCESSING_DONE); m_Activation->AddMenuItem(m_MenuSaveLevelSet, UIF_SNAP_SNAKE_INITIALIZED); m_Activation->AddMenuItem(m_MenuLoadAdvection, UIF_SNAP_PAGE_PREPROCESSING); m_Activation->AddMenuItem(m_MenuImageInfo, UIF_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuReorientImage, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_ChoicePaintbrush[2], UIF_IRIS_WITH_GRAY_LOADED); for (unsigned int i = 0; i < 5; i++) { m_Activation->AddMenuItem(m_MenuLoadPreviousFirst + i, UIF_IRIS_ACTIVE); } m_Activation->AddMenuItem(m_MenuLayerInspector, UIF_BASEIMG_LOADED); // Toolbar menu items m_Activation->AddMenuItem(m_MenuCrosshairsMode, UIF_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuZoomPanMode, UIF_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuPolygonMode, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSNAPMode, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuPaintbrushMode, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuAnnotationMode, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuTrackballMode, UIF_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuCrosshair3DMode, UIF_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuScalpelMode, UIF_IRIS_WITH_BASEIMG_LOADED); m_Activation->AddMenuItem(m_MenuSpraypaintMode, UIF_IRIS_WITH_BASEIMG_LOADED); } UserInterfaceLogic ::UserInterfaceLogic(IRISApplication *iris) : UserInterface() { // Initialize the menu lists to NULL m_MenuDrawingLabels = NULL; m_MenuDrawOverLabels = NULL; // TODO: move this! Fl::set_labeltype(COLORBAR_LABEL, xyz_draw, xyz_measure); // Store the pointers to application and system high level objects m_Driver = iris; m_SystemInterface = iris->GetSystemInterface(); // This is just done for shorthand m_GlobalState = iris->GetGlobalState(); // Load the appearance settings from the system interface m_AppearanceSettings = new SNAPAppearanceSettings(); Registry ®Appearance = m_SystemInterface->Folder("UserInterface.AppearanceSettings"); m_AppearanceSettings->LoadFromRegistry(regAppearance); // Instantiate the IO wizards m_WizGreyIO = new ImageIOWizardLogic; m_WizSegmentationIO = new RestrictedImageIOWizardLogic; m_WizPreprocessingIO = new RestrictedImageIOWizardLogic; m_WizLevelSetIO = new RestrictedImageIOWizardLogic; m_WizMeshExport = new MeshIOWizardUILogic; // Allow only 3 components m_WizSegmentationIO->SetNumberOfComponentsRestriction(1); m_WizPreprocessingIO->SetNumberOfComponentsRestriction(1); m_WizLevelSetIO->SetNumberOfComponentsRestriction(1); // Initialize the Image IO wizards m_WizGreyIO->MakeWindow(); m_WizSegmentationIO->MakeWindow(); m_WizPreprocessingIO->MakeWindow(); m_WizLevelSetIO->MakeWindow(); m_WizMeshExport->MakeWindow(); // Provide the registry callback for the greyscale image wizard m_GreyCallbackInterface = new GreyImageInfoCallback(m_SystemInterface); m_WizGreyIO->SetImageInfoCallback(m_GreyCallbackInterface); // Create the layer editor m_LayerUI = new LayerInspectorUILogic(this); m_LayerUI->MakeWindow(); m_LayerUI->Initialize(); // Create the label editor window m_LabelEditorUI = new LabelEditorUILogic(); m_LabelEditorUI->MakeWindow(); m_LabelEditorUI->Register(this); // Initialize the progress command m_ProgressCommand = ProgressCommandType::New(); m_ProgressCommand->SetCallbackFunction( this,&UserInterfaceLogic::OnITKProgressEvent); // Initialize the preprocessing windows m_PreprocessingUI = new PreprocessingUILogic; m_PreprocessingUI->MakeWindow(); m_PreprocessingUI->Register(this); // Initialize the snake parameter window m_SnakeParametersUI = new SnakeParametersUILogic; m_SnakeParametersUI->MakeWindow(); m_SnakeParametersUI->Register(this); // Initialize the restore settings dialog m_DlgRestoreSettings = new RestoreSettingsDialogLogic; m_DlgRestoreSettings->MakeWindow(); // Initialize the resample dialog m_DlgResampleRegion = new ResizeRegionDialogLogic; m_DlgResampleRegion->MakeWindow(); // Initialize the appearance dialog m_DlgAppearance = new AppearanceDialogUILogic; m_DlgAppearance->MakeWindow(); m_DlgAppearance->Register(this); // Initialize the reorientation dialog m_DlgReorientImage = new ReorientImageUILogic; m_DlgReorientImage->MakeWindow(); m_DlgReorientImage->Register(this); // Create the window managers for SNAP and IRIS. Start in IRIS mode for(int i=0; i<3; i++) { m_IRISWindowManager2D[i] = new IRISSliceWindow(i, this, m_SliceWindow[i]); m_SNAPWindowManager2D[i] = new SNAPSliceWindow(i, this, m_SliceWindow[i]); m_SliceWindow[i]->PushInteractionMode(m_IRISWindowManager2D[i]); } // Create the 3D Window managers for SNAP and IRIS m_IRISWindowManager3D = new Window3D(this, m_SliceWindow[3]); m_SNAPWindowManager3D = new Window3D(this, m_SliceWindow[3]); m_SliceWindow[3]->PushInteractionMode(m_IRISWindowManager3D); // Initialize the slice window coordinator object m_SliceCoordinator = new SliceWindowCoordinator(); // Group the three windows inside the window coordinator m_SliceCoordinator->RegisterWindows( reinterpret_cast(m_IRISWindowManager2D)); // Create a callback command for the snake loop m_PostSnakeCommand = SimpleCommandType::New(); // Initialize the Help UI m_HelpUI = new HelpViewerLogic; m_HelpUI->MakeWindow(); m_HelpUI->SetContentsLink( m_SystemInterface->GetFileInRootDirectory("HTMLHelp/Tutorial.html").c_str()); // Initialize the label IO dialog m_DlgLabelsIO = new SimpleFileDialogLogic(); m_DlgLabelsIO->MakeWindow(); m_DlgLabelsIO->SetFileBoxTitle("Label description file:"); m_DlgLabelsIO->SetPattern("All Label Files\t*.{label,lbl,lab,txt}"); m_DlgLabelsIO->SetLoadCallback(this,&UserInterfaceLogic::OnLoadLabelsAction); m_DlgLabelsIO->SetSaveCallback(this,&UserInterfaceLogic::OnSaveLabelsAction); /** Write voxels dialog */ m_DlgVoxelCountsIO = new SimpleFileDialogLogic(); m_DlgVoxelCountsIO->MakeWindow(); m_DlgVoxelCountsIO->SetFileBoxTitle("Voxel count file:"); m_DlgVoxelCountsIO->SetPattern("Text files\t*.txt"); m_DlgVoxelCountsIO->SetSaveCallback( this,&UserInterfaceLogic::OnWriteVoxelCountsAction); // Set the welcome page to display m_WizControlPane->value(m_GrpWelcomePage); InitializeActivationFlags(); InitializeUI(); // Update the recent files menu GenerateRecentFilesMenu(); // Enter the IRIS-ACTiVE state m_Activation->UpdateFlag(UIF_IRIS_ACTIVE, true); // Opacity toggle value set to default m_OpacityToggleValue = 128; // Configure the display layout m_DisplayLayout.full_screen = false; m_DisplayLayout.show_main_ui = true; m_DisplayLayout.show_panel_ui = true; m_DisplayLayout.slice_config = FOUR_SLICE; m_DisplayLayout.size = FULL_SIZE; // Not fullscreen m_FullScreen = false; } UserInterfaceLogic ::~UserInterfaceLogic() { // Delete the menus DeleteColorLabelMenu(m_MenuDrawingLabels); DeleteColorLabelMenu(m_MenuDrawOverLabels); // Delete the IO wizards delete m_WizGreyIO; delete m_WizSegmentationIO; delete m_WizPreprocessingIO; delete m_WizLevelSetIO; delete m_WizMeshExport; // Delete the IO wizard registry adaptor delete m_GreyCallbackInterface; // Other IO dialogs delete m_DlgLabelsIO; delete m_DlgVoxelCountsIO; // Delete the UI's delete m_SnakeParametersUI; delete m_PreprocessingUI; delete m_DlgRestoreSettings; delete m_DlgResampleRegion; delete m_DlgAppearance; delete m_DlgReorientImage; delete m_LabelEditorUI; delete m_LayerUI; // Delete the window managers for(int i = 0; i < 3; i++) { delete m_IRISWindowManager2D[i]; delete m_SNAPWindowManager2D[i]; } delete m_IRISWindowManager3D; delete m_SNAPWindowManager3D; // Delete the window coordinator delete m_SliceCoordinator; // Delete the appearance settings delete m_AppearanceSettings; // Delete the activation manager delete m_Activation; } void UserInterfaceLogic ::OnResetROIAction() { // requires grey image if (!m_Driver->GetCurrentImageData()->IsGreyLoaded()) return; // Get the grey image's region GlobalState::RegionType roi = m_Driver->GetIRISImageData()->GetImageRegion(); // The region can not be empty! assert(roi.GetNumberOfPixels() > 0); // Set the Region of interest m_GlobalState->SetSegmentationROI(roi); m_GlobalState->SetIsValidROI(true); // Update the UI RedrawWindows(); } void UserInterfaceLogic ::OnMenuResetAll() { // Make sure the user doesn't lose any data if(!PromptBeforeLosingChanges(REASON_RESET)) return; UnloadAllImages(); m_WizControlPane->value(m_GrpWelcomePage); UpdateMainLabel(); RedrawWindows(); } void UserInterfaceLogic ::OnMenuViewToggleUI() { this->ToggleDisplayElements(); } void UserInterfaceLogic ::OnMenuViewToggleFullscreen() { this->ToggleFullScreen(); } void UserInterfaceLogic ::OnMenuViewRestoreDefault() { DisplayLayout dl = m_DisplayLayout; dl.full_screen = false; dl.show_main_ui = true; dl.show_panel_ui = true; dl.slice_config = FOUR_SLICE; dl.size = FULL_SIZE; SetDisplayLayout(dl); } void UserInterfaceLogic ::OnMenuLaunchNewInstance() { std::list args; try { m_SystemInterface->LaunchChildSNAP(args); } catch(IRISException &exc) { fl_alert("Launching another SNAP instance failed.\nReason: %s", exc.what()); } } //-------------------------------------------- // // // SEGMENT 3D BUTTON CALLBACK // // //-------------------------------------------- unsigned int UserInterfaceLogic ::GetImageAxisForDisplayWindow(unsigned int window) { return m_Driver->GetCurrentImageData()-> GetImageGeometry().GetDisplayToImageTransform(window). GetCoordinateIndexZeroBased(2); } void UserInterfaceLogic ::OnSnakeStartAction() { uchar index = m_GlobalState->GetDrawingColorLabel(); if (0 == index) { fl_alert("Cannot start snake segmentation with clear color"); return; } if (!m_Driver->GetColorLabelTable()->GetColorLabel(index).IsVisible()) { fl_alert("Current label must be visible to start snake segmentation"); return; } // Get the region of interest SNAPSegmentationROISettings roi = m_GlobalState->GetSegmentationROISettings(); // The voxel size for the resampled region Vector3d voxelSizeSrc( m_Driver->GetCurrentImageData()->GetGrey()->GetImage()->GetSpacing().GetDataPointer()); // Check if the user wants to resample the image if(m_ChkResampleRegion->value()) { // Show the resampling dialog, updating the ROI object m_DlgResampleRegion->DisplayDialog(voxelSizeSrc.data_block(), roi); } else { roi.SetVoxelScale(Vector3d(1.0)); roi.SetResampleFlag(false); } // Update the segmentation ROI m_GlobalState->SetSegmentationROISettings(roi); // The region can not be empty assert(roi.GetROI().GetNumberOfPixels() > 0); // Try allocating memory for snake try { m_Driver->InitializeSNAPImageData(roi,m_ProgressCommand); } catch(itk::MemoryAllocationError &) { fl_alert("Out of memory! Try using a smaller region of interest or subsampling."); return; } // Set the current application image mode to SNAP data m_Driver->SetCurrentImageDataToSNAP(); // Inform the global state that we're in sNAP m_GlobalState->SetSNAPActive(true); // Set bubble radius range according to volume dimensions (world dimensions) Vector3ui size = m_Driver->GetCurrentImageData()->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 double xBubbleDefault = floor(0.25 * xBubbleMax / xBubbleStep) * xBubbleStep; // Set the value for the radius slider m_InBubbleRadius->range(xBubbleMin, xBubbleMax); m_InBubbleRadius->step(xBubbleStepA, xBubbleStepB); m_InBubbleRadius->value(xBubbleDefault); m_InBubbleRadius->redraw(); // Use the current SnakeParameters to determine which type of snake to use const SnakeParameters ¶meters = m_GlobalState->GetSnakeParameters(); if(parameters.GetSnakeType() == SnakeParameters::EDGE_SNAKE) { m_RadSnakeEdge->set(); m_RadSnakeInOut->clear(); m_GlobalState->SetSnakeMode(EDGE_SNAKE); OnEdgeSnakeSelect(); } else { m_RadSnakeInOut->set(); m_RadSnakeEdge->clear(); m_GlobalState->SetSnakeMode(IN_OUT_SNAKE); OnInOutSnakeSelect(); } // The edge preprocessing settings pass through unchanged // Get the current thresholding properties ThresholdSettings threshSettings = m_GlobalState->GetThresholdSettings(); // We want to keep the current preprocessing settings, but we have to be // careful that they are in range of image intensity GreyType iMax = m_Driver->GetCurrentImageData()->GetGrey()->GetImageMax(); GreyType iMin = m_Driver->GetCurrentImageData()->GetGrey()->GetImageMin(); if(threshSettings.GetUpperThreshold() > iMax) threshSettings.SetUpperThreshold(iMax); if(threshSettings.GetLowerThreshold() < iMin) threshSettings.SetLowerThreshold(iMin); m_GlobalState->SetThresholdSettings(threshSettings); // This method basically sends the current button state from IRIS to SNAP SyncSnakeToIRIS(); // Initialize GUI widgets // TODO: WTF is this? m_ChoiceSNAPView->value(m_MenuSNAPViewOriginal); // adioSNAPViewPreprocessed->value(0); //RadioSNAPViewOriginal->value(1); m_BtnAcceptInitialization->show(); m_BtnRestartInitialization->hide(); m_GlobalState->SetShowSpeed(false); m_BrsActiveBubbles->clear(); uchar rgb[3]; m_Driver->GetColorLabelTable()->GetColorLabel(index).GetRGBVector(rgb); m_GrpSNAPCurrentColor->color(fl_rgb_color(rgb[0], rgb[1], rgb[2])); m_GrpSNAPCurrentColor->redraw(); m_SnakeStepSize = 1; m_InStepSize->value(0); // reset Mesh in IRIS window m_IRISWindowManager3D->ClearScreen(); // Hide the label editor since it's open m_LabelEditorUI->OnCloseAction(); // show the snake window, hide the IRIS window ShowSNAP(); // We are going to preserve the cursor position if it was in the ROI Vector3ui newCursor, oldCursor = m_Driver->GetCursorPosition(); // Image geometry has changed. This method also resets the cursor // position, which is something we don't want. OnImageGeometryUpdate(); // So we reset the cursor position manually here if the cursor was in the ROI if(roi.TransformImageVoxelToROIVoxel(oldCursor, newCursor)) { m_Driver->SetCursorPosition(newCursor); OnCrosshairPositionUpdate(); } } //-------------------------------------------- // // // PREPROCESSING // // //-------------------------------------------- void UserInterfaceLogic ::OnPreprocessAction() { // Disable the 'Next' button on the preprocessing page m_Activation->UpdateFlag(UIF_SNAP_PREPROCESSING_ACTIVE, true); // Display the right window if(m_GlobalState->GetSnakeMode() == EDGE_SNAKE) { m_PreprocessingUI->DisplayEdgeWindow(); } else { m_PreprocessingUI->DisplayInOutWindow(); } } void UserInterfaceLogic ::OnPreprocessClose() { // Check if the preprocessing image has been computed if(m_GlobalState->GetSpeedValid()) m_Activation->UpdateFlag(UIF_SNAP_PREPROCESSING_DONE, true); else { m_Activation->UpdateFlag(UIF_SNAP_PREPROCESSING_ACTIVE, false); m_Activation->UpdateFlag(UIF_SNAP_SPEED_AVAILABLE, false); } } void UserInterfaceLogic ::OnAcceptPreprocessingAction() { SetActiveSegmentationPipelinePage(1); } void UserInterfaceLogic ::OnPreprocessedColorMapAction() { // Set up mapping from color maps to menu items (TODO: this is hacky) static std::map menuMap; if(menuMap.size() == 0) { // Edge items menuMap[COLORMAP_BLACK_BLACK_WHITE] = 0; menuMap[COLORMAP_BLACK_YELLOW_WHITE] = 1; // Region items menuMap[COLORMAP_BLACK_GRAY_WHITE] = 0; menuMap[COLORMAP_BLUE_BLACK_WHITE] = 1; menuMap[COLORMAP_BLUE_WHITE_RED] = 2; } // Select current color maps in the menus m_ChoiceColorMap[0]->value(menuMap[m_GlobalState->GetSpeedColorMapInRegionMode()]); m_ChoiceColorMap[1]->value(menuMap[m_GlobalState->GetSpeedColorMapInEdgeMode()]); // Set the ranges for the color map boxes m_BoxColorMap[0]->SetRange(-1.0, 1.0); m_BoxColorMap[1]->SetRange(0.0, 1.0); // Show the color map box and window m_WinColorMap->show(); // Update the color map in the speed images UpdateSpeedColorMap(); } void UserInterfaceLogic ::OnColorMapCloseAction() { m_WinColorMap->hide(); } void UserInterfaceLogic ::OnColorMapSelectAction() { // Mapping from edge menu items to color maps static const ColorMapPreset edgeMenuMap[] = { COLORMAP_BLACK_BLACK_WHITE, COLORMAP_BLACK_YELLOW_WHITE }; static const ColorMapPreset regionMenuMap[] = { COLORMAP_BLACK_GRAY_WHITE, COLORMAP_BLUE_BLACK_WHITE, COLORMAP_BLUE_WHITE_RED }; // Select the appropriate page in the color map window ColorMapPreset xPreset = (m_GlobalState->GetSnakeMode() == EDGE_SNAKE) ? edgeMenuMap[m_ChoiceColorMap[1]->value()] : regionMenuMap[m_ChoiceColorMap[0]->value()] ; // Set the current color map m_GlobalState->SetSpeedColorMap(xPreset); // Update the display UpdateSpeedColorMap(); } void UserInterfaceLogic ::UpdateSpeedColorMap() { // Get the index of the color map box to update int iBox = (m_GlobalState->GetSnakeMode() == EDGE_SNAKE) ? 1 : 0; // Select the appropriate page in the color map window m_WizColorMap->value(m_GrpColorMapPage[iBox]); // Apply the color map to the preview window m_BoxColorMap[iBox]->SetColorMap( SpeedColorMap::GetPresetColorMap( m_GlobalState->GetSpeedColorMap())); m_BoxColorMap[iBox]->redraw(); // Apply the color map to the speed wrapper if(m_Driver->GetSNAPImageData()->IsSpeedLoaded()) m_Driver->GetSNAPImageData()->GetSpeed()-> SetColorMap(SpeedColorMap::GetPresetColorMap( m_GlobalState->GetSpeedColorMap())); // Also, apply the color map to the SNAP preview window if(m_Driver->GetSNAPImageData()->IsSpeedLoaded()) m_SnakeParametersUI->OnSpeedColorMapUpdate(); // If the color map window is showing, show the color boxes if(m_WinColorMap->shown()) { m_BoxColorMap[1 - iBox]->hide(); m_BoxColorMap[iBox]->show(); } // Redraw the windows RedrawWindows(); } void UserInterfaceLogic ::OnPreprocessingPreviewStatusUpdate(bool flagPreview) { // Enable the preprocessing widgets and color map button m_Activation->UpdateFlag(UIF_SNAP_SPEED_AVAILABLE, flagPreview); // Make sure that the preview mode is used m_ChoiceSNAPView->value(flagPreview ? m_MenuSNAPViewPreprocessed : m_MenuSNAPViewOriginal); // Set whether speed is shown m_GlobalState->SetShowSpeed(flagPreview); // Redraw the windows RedrawWindows(); } //-------------------------------------------- // // // SWITCH BETWEEN GREY/PREPROC DATA // // //-------------------------------------------- void UserInterfaceLogic ::OnSNAPViewOriginalSelect() { m_GlobalState->SetShowSpeed(false); RedrawWindows(); } void UserInterfaceLogic ::OnViewPreprocessedSelect() { if (!m_GlobalState->GetSpeedValid()) return; m_GlobalState->SetShowSpeed(true); RedrawWindows(); } //-------------------------------------------- // // // BUBBLES // // //-------------------------------------------- void UserInterfaceLogic ::UpdateBubbleUI() { // Fill the array of values m_BrsActiveBubbles->clear(); GlobalState::BubbleArray ba = m_GlobalState->GetBubbleArray(); for(unsigned int i = 0; i < ba.size(); i++) { std::ostringstream oss; oss << "C=" << ba[i].center << "; "; oss << "R=" << std::setprecision(3) << ba[i].radius; m_BrsActiveBubbles->add(oss.str().c_str()); } // Get the active bubble int ibub = m_GlobalState->GetActiveBubble(); // The browser uses 1-based indexing, so we add 1 m_BrsActiveBubbles->value(ibub + 1); // Set the radius slider if(ibub >= 0) m_InBubbleRadius->value(ba[ibub].radius); // Redraw the browser m_BrsActiveBubbles->redraw(); // Redraw the windows as well RedrawWindows(); } void UserInterfaceLogic ::OnAddBubbleAction() { // Create a new bubble Bubble bub; bub.center = to_int(m_Driver->GetCursorPosition()); bub.radius = m_InBubbleRadius->value(); // Add the bubble to the global state GlobalState::BubbleArray ba = m_GlobalState->GetBubbleArray(); ba.push_back(bub); m_GlobalState->SetBubbleArray(ba); // Set the bubble's position m_GlobalState->SetActiveBubble(ba.size() - 1); // Update the bubble list in the GUI UpdateBubbleUI(); } void UserInterfaceLogic ::OnRemoveBubbleAction() { int ibub = m_GlobalState->GetActiveBubble(); if(ibub >= 0) { // Remove the bubble from the global state GlobalState::BubbleArray ba = m_GlobalState->GetBubbleArray(); ba.erase(ba.begin() + ibub); m_GlobalState->SetBubbleArray(ba); // Update the active bubble if(ibub == (int) ba.size()) m_GlobalState->SetActiveBubble(ibub - 1); // Update the bubble list in the GUI UpdateBubbleUI(); } else { fl_alert("To remove a bubble, first select a bubble in the list."); } } void UserInterfaceLogic ::OnActiveBubblesChange() { // Set the active bubble in the global state m_GlobalState->SetActiveBubble(m_BrsActiveBubbles->value() - 1); // Update the user interface UpdateBubbleUI(); } void UserInterfaceLogic ::OnBubbleRadiusChange() { int ibub = m_GlobalState->GetActiveBubble(); if(ibub >= 0) { // Update the bubble in the global state GlobalState::BubbleArray ba = m_GlobalState->GetBubbleArray(); ba[ibub].radius = m_InBubbleRadius->value(); m_GlobalState->SetBubbleArray(ba); // Update the bubble list in the GUI UpdateBubbleUI(); } } //-------------------------------------------- // // // SNAKE TYPE RADIO BUTTONS // // //-------------------------------------------- void UserInterfaceLogic ::OnInOutSnakeSelect() { m_RadSnakeInOut->set(); m_RadSnakeEdge->clear(); m_GlobalState->SetSnakeMode(IN_OUT_SNAKE); //Nathan Moon if (m_GlobalState->GetSpeedValid()) { //make sure they want to do this //fl_ask is deprecated if (0 == fl_ask("Preprocessed data will be lost! Continue?")) if (0 == fl_choice("Preprocessed data will be lost! Continue?","No","Yes",NULL)) { m_RadSnakeInOut->clear(); m_RadSnakeEdge->set(); m_GlobalState->SetSnakeMode(EDGE_SNAKE); return; } } // Set parameters to default values m_GlobalState->SetSnakeParameters( SnakeParameters::GetDefaultInOutParameters()); m_Driver->GetSNAPImageData()->ClearSpeed(); m_GlobalState->SetSpeedValid(false); // Update widget state m_Activation->UpdateFlag(UIF_SNAP_SPEED_AVAILABLE, false); m_Activation->UpdateFlag(UIF_SNAP_PREPROCESSING_DONE, false); m_PreprocessingUI->HidePreprocessingWindows(); // Set the SNAP view to the grayscale mode m_ChoiceSNAPView->value(m_MenuSNAPViewOriginal); OnSNAPViewOriginalSelect(); // Update the speed color map UpdateSpeedColorMap(); // m_RadioSNAPViewOriginal->setonly(); } void UserInterfaceLogic ::OnEdgeSnakeSelect() { m_RadSnakeEdge->set(); m_RadSnakeInOut->clear(); m_GlobalState->SetSnakeMode(EDGE_SNAKE); //Nathan Moon if (m_GlobalState->GetSpeedValid()) { //make sure they want to do this //fl_ask is deprecated if (0 == fl_ask("Preprocessed data will be lost! Continue?")) if (0 == fl_choice("Preprocessed data will be lost! Continue?","No","Yes",NULL)) { m_RadSnakeInOut->set(); m_RadSnakeEdge->clear(); m_GlobalState->SetSnakeMode(IN_OUT_SNAKE); return; } } // Set parameters to default values m_GlobalState->SetSnakeParameters( SnakeParameters::GetDefaultEdgeParameters()); if (m_Driver->GetSNAPImageData()) m_Driver->GetSNAPImageData()->ClearSpeed(); m_GlobalState->SetSpeedValid(false); // Update widget state m_Activation->UpdateFlag(UIF_SNAP_SPEED_AVAILABLE, false); m_Activation->UpdateFlag(UIF_SNAP_PREPROCESSING_DONE, false); m_PreprocessingUI->HidePreprocessingWindows(); // Set the SNAP view to the grayscale mode m_ChoiceSNAPView->value(m_MenuSNAPViewOriginal); OnSNAPViewOriginalSelect(); // Update the speed color map UpdateSpeedColorMap(); // m_RadioSNAPViewOriginal->setonly(); } /* * This method is called when the user has finished adding bubbles */ void UserInterfaceLogic ::OnAcceptInitializationAction() { // Get bubbles, turn them into segmentation vector bubbles = m_GlobalState->GetBubbleArray(); // Shorthand SNAPImageData *snapData = m_Driver->GetSNAPImageData(); // Put on a wait cursor // TODO: Progress bar is needed here m_WinMain->cursor(FL_CURSOR_WAIT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Merge bubbles with the segmentation image and initialize the snake bool rc = snapData->InitializeSegmentation( m_GlobalState->GetSnakeParameters(), bubbles, m_GlobalState->GetDrawingColorLabel()); // Restore the cursor m_WinMain->cursor(FL_CURSOR_DEFAULT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Check if we need to bail out if (!rc) { // There were no voxels selected in the end fl_alert("Can not proceed without an initialization\n" "Please place a bubble into the image!"); return; } m_BtnAcceptInitialization->hide(); m_BtnRestartInitialization->show(); // Set the UI for the segmentation state m_Activation->UpdateFlag(UIF_SNAP_SNAKE_EDITABLE, true); m_SNAPWindowManager3D->ClearScreen(); // reset Mesh object in Window3D_s m_SNAPWindowManager3D->ResetView(); // reset cursor // Flip to the next page in the wizard SetActiveSegmentationPipelinePage(2); m_GlobalState->SetSnakeActive(true); OnSnakeUpdate(); } void UserInterfaceLogic ::SetActiveSegmentationPipelinePage(unsigned int page) { switch(page) { case 0 : m_WizSegmentationPipeline->value(m_GrpSNAPStepPreprocess); m_Activation->UpdateFlag(UIF_SNAP_PAGE_PREPROCESSING, true); break; case 1 : m_WizSegmentationPipeline->value(m_GrpSNAPStepInitialize); m_Activation->UpdateFlag(UIF_SNAP_PAGE_BUBBLES, true); break; case 2 : m_WizSegmentationPipeline->value(m_GrpSNAPStepSegment); m_Activation->UpdateFlag(UIF_SNAP_PAGE_SEGMENTATION, true); break; } } void UserInterfaceLogic ::OnRestartInitializationAction() { // Stop the segmentation if it's running OnSnakeStopAction(); // If the segmentation pipeline is active, deactivate it if(m_Driver->GetSNAPImageData()->IsSegmentationActive()) { // Tell the update loop to terminate m_Driver->GetSNAPImageData()->TerminateSegmentation(); } // Tell the UI to activate related widgets m_Activation->UpdateFlag(UIF_SNAP_SNAKE_INITIALIZED, false); m_GlobalState->SetSnakeActive(false); m_BtnRestartInitialization->hide(); m_BtnAcceptInitialization->show(); m_SNAPWindowManager3D->ClearScreen(); // reset Mesh object in Window3D_s m_SNAPWindowManager3D->ResetView(); // reset cursor RedrawWindows(); // Flip to the second page SetActiveSegmentationPipelinePage(1); } void UserInterfaceLogic ::OnRestartPreprocessingAction() { // Reset the active bubble (if there are any) m_GlobalState->SetActiveBubble( m_GlobalState->GetBubbleArray().size() > 0 ? 0 : -1); // Flip to the first page SetActiveSegmentationPipelinePage(0); // Repaint the screen and the bubbles UpdateBubbleUI(); } void UserInterfaceLogic ::OnSnakeParametersAction() { // Get the current parameters from the system SnakeParameters pGlobal = m_GlobalState->GetSnakeParameters(); // Send current parameter to the snake parameter setting UI m_SnakeParametersUI->SetParameters(pGlobal); // Chech whether we need to warn user about changing the solver in the // process of evolution m_SnakeParametersUI->SetWarnOnSolverUpdate( m_Driver->GetSNAPImageData()->GetElapsedSegmentationIterations() > 0); // Show the snake parameters window CenterChildWindowInParentWindow(m_SnakeParametersUI->GetWindow(),m_WinMain); m_SnakeParametersUI->DisplayWindow(); // Wait until the window has been closed while(m_SnakeParametersUI->GetWindow()->visible()) Fl::wait(); // Have the parameters been accepted by the user? if(m_SnakeParametersUI->GetUserAccepted()) { // Get the new parameters SnakeParameters pNew = m_SnakeParametersUI->GetParameters(); // Have the parameters changed? if(!(pGlobal == pNew)) { // Update the system's parameters with new values m_GlobalState->SetSnakeParameters(pNew); // Update the running snake if (m_Driver->GetSNAPImageData()->IsSegmentationActive()) { m_Driver->GetSNAPImageData()->SetSegmentationParameters(pNew); } } } } void UserInterfaceLogic ::OnSnakeRewindAction() { // Stop the snake if it's running OnSnakeStopAction(); // Basically, we tell the level set driver that we want a restart m_Driver->GetSNAPImageData()->RestartSegmentation(); // Update the display OnSnakeUpdate(); } void fnSnakeIdleFunction(void *userData) { // Get the instance of the calling class UserInterfaceLogic *uiLogic = (UserInterfaceLogic *) userData; // Request that the desired number of iterations be executed uiLogic->GetDriver()->GetSNAPImageData()->RunSegmentation( uiLogic->m_SnakeStepSize); // Update the display uiLogic->OnSnakeUpdate(); } void UserInterfaceLogic ::OnSnakeStopAction() { Fl::remove_idle(fnSnakeIdleFunction, this); m_Activation->UpdateFlag(UIF_SNAP_SNAKE_EDITABLE, true); } void UserInterfaceLogic ::OnSnakePlayAction() { m_Activation->UpdateFlag(UIF_SNAP_SNAKE_RUNNING, true); Fl::add_idle(fnSnakeIdleFunction, this); } void UserInterfaceLogic ::OnSnakeUpdate() { // Update the number of elapsed iterations std::ostringstream oss; oss << m_Driver->GetSNAPImageData()->GetElapsedSegmentationIterations(); m_OutCurrentIteration->value(oss.str().c_str()); // Update the mesh if necessary if(m_ChkContinuousView3DUpdate->value()) m_SNAPWindowManager3D->UpdateMesh(m_ProgressCommand); else m_Activation->UpdateFlag(UIF_SNAP_MESH_DIRTY, true); // Redraw the windows RedrawWindows(); } void UserInterfaceLogic ::OnSnakeStepAction() { // Stop the snake if it's running OnSnakeStopAction(); // Call the idle function directly fnSnakeIdleFunction(this); } void UserInterfaceLogic ::OnSnakeStepSizeChange() { // Save the step size m_SnakeStepSize = atoi(m_InStepSize->text()); } //-------------------------------------------- // // // SWITCHING BETWEEN WINDOWS STUFF // // //-------------------------------------------- void UserInterfaceLogic ::SyncSnakeToIRIS() { m_InSNAPLabelOpacity->value(m_InIRISLabelOpacity->value()); // Contrast_slider_s->value(Contrast_slider->value()); // Brightness_slider_s->value(Brightness_slider->value()); switch (m_GlobalState->GetToolbarMode()) { case(NAVIGATION_MODE): m_BtnSNAPNavigation->setonly(); break; default: SetToolbarMode(CROSSHAIRS_MODE); m_BtnSNAPCrosshairs->setonly(); break; } } void UserInterfaceLogic ::SyncIRISToSnake() { m_InIRISLabelOpacity->value(m_InSNAPLabelOpacity->value()); // Contrast_slider->value(Contrast_slider_s->value()); // Brightness_slider->value(Brightness_slider_s->value()); switch (m_GlobalState->GetToolbarMode()) { case(NAVIGATION_MODE): m_BtnNavigationMode->setonly(); break; default: SetToolbarMode(CROSSHAIRS_MODE); m_BtnCrosshairsMode->setonly(); break; } } void UserInterfaceLogic ::CloseSegmentationCommon() { // This makes no sense if there is no SNAP assert(m_Driver->GetSNAPImageData()); // Clean up SNAP image data m_Driver->SetCurrentImageDataToIRIS(); m_Driver->ReleaseSNAPImageData(); // Update the Layer UI OnLayerInspectorUpdate(); // Inform the global state that we're not in sNAP m_GlobalState->SetSNAPActive(false); // Speed image is no longer visible m_GlobalState->SetSpeedValid(false); m_GlobalState->SetShowSpeed(false); m_GlobalState->SetSnakeActive(false); // Updates some UI components (?) SyncIRISToSnake(); // Reset the mesh display m_IRISWindowManager3D->ClearScreen(); m_IRISWindowManager3D->ResetView(); // We are going to preserve the cursor position if it was in the ROI Vector3ui newCursor, oldCursor = m_Driver->GetCursorPosition(); // Image geometry has changed. This method also resets the cursor // position, which is something we don't want. OnImageGeometryUpdate(); // So we reset the cursor position manually here if the cursor was in the ROI SNAPSegmentationROISettings roi = m_GlobalState->GetSegmentationROISettings(); roi.TransformROIVoxelToImageVoxel(oldCursor, newCursor); m_Driver->SetCursorPosition(newCursor); OnCrosshairPositionUpdate(); // Clear the list of bubbles m_GlobalState->SetBubbleArray(GlobalState::BubbleArray()); m_GlobalState->SetActiveBubble(-1); // Activate/deactivate menu items // TODO: build a better state machine if(m_Activation->GetFlag(UIF_GRAY_LOADED)) m_Activation->UpdateFlag(UIF_IRIS_WITH_GRAY_LOADED, true); else m_Activation->UpdateFlag(UIF_IRIS_WITH_BASEIMG_LOADED, true); // The segmentation has changed, so the mesh should be updatable m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); // Hide the color map window m_WinColorMap->hide(); // Restore the SNAP view to four side-by-side windows OnWindowFocus(-1); // Show IRIS window, Hide the snake window ShowIRIS(); // Redraw the windows RedrawWindows(); } void UserInterfaceLogic ::OnAcceptSegmentationAction() { // Stop the segmentation if it's running OnSnakeStopAction(); // Turn off segmentation if it's active if(m_Driver->GetSNAPImageData()->IsSegmentationActive()) { // Tell the update loop to terminate m_Driver->GetSNAPImageData()->TerminateSegmentation(); } // Get data from SNAP back into IRIS m_Driver->UpdateIRISWithSnapImageData(m_ProgressCommand); // This is a safeguard in case the progress events do not fire m_WinProgress->hide(); // Create an undo point StoreUndoPoint("Automatic segmentation"); // Close up SNAP CloseSegmentationCommon(); } void UserInterfaceLogic ::OnITKProgressEvent(itk::Object *source, const itk::EventObject &) { static double last_progress = clock(); static const double delta = 0.25 * CLOCKS_PER_SEC; // Get the elapsed progress float progress = dynamic_cast(source)->GetProgress(); // Update the progress bar and value m_OutProgressMeter->value(100 * progress); m_OutProgressCounter->value(100 * progress); // Show or hide progress bar if necessary if(progress < 1.0f && !m_WinProgress->visible()) { CenterChildWindowInMainWindow(m_WinProgress); m_WinProgress->show(); } else if (progress == 1.0f && m_WinProgress->visible()) m_WinProgress->hide(); // Only call Fl::check() if some time has passed if(last_progress + delta < clock()) { // Update the screen Fl::check(); last_progress = clock(); } } void UserInterfaceLogic ::OnCancelSegmentationAction() { // Stop the segmentation if it's running OnSnakeStopAction(); // This callback has double functionality, depending on whether the // level set update loop is active or not if(m_Driver->GetSNAPImageData()->IsSegmentationActive()) { // Tell the update loop to terminate m_Driver->GetSNAPImageData()->TerminateSegmentation(); } // Clean up SNAP image data CloseSegmentationCommon(); } void UserInterfaceLogic ::OnMenuQuit() { OnMainWindowCloseAction(); } void UserInterfaceLogic ::OnMenuImageInfo() { m_LayerUI->DisplayImageInfoTab(); } void UserInterfaceLogic ::OnMenuReorientImage() { m_DlgReorientImage->ShowDialog(); } void UserInterfaceLogic ::OnMainWindowCloseAction() { // Make sure the user doesn't lose any data if(!PromptBeforeLosingChanges(REASON_QUIT)) return; // We don't want to just exit when users press escape if(Fl::event_key() == FL_Escape) return; // Check if there have been unsaved changes to the segmentation // TODO: // Associate the current state with the current image OnGreyImageUnload(); // Create an array of open windows vector openWindows; openWindows.push_back(Fl::first_window()); // Add all the open windows to the list while(true) { Fl_Window *win = Fl::next_window(openWindows.back()); if(win && win != openWindows.front()) openWindows.push_back(win); else break; } // Close all the windows for(unsigned int i=0;ihide(); } void UserInterfaceLogic ::GlobalOpenDocumentHandler(const char *fn_open) { m_GlobalUI->OpenDraggedContent(fn_open, false); } void UserInterfaceLogic ::OnOpenDroppedAction(int selection) { const char *fn_open = m_OutOpenDroppedFileName->label(); // 1: Load Main image if(selection == 1) { try { NonInteractiveLoadMainImage(fn_open, false, false); } catch(itk::ExceptionObject &exc) { fl_alert("Error opening main image: %s", exc.what()); this->RedrawWindows(); } } // 2: Load Segmentation else if(selection == 2) { try { NonInteractiveLoadSegmentation(fn_open); } catch(itk::ExceptionObject &exc) { fl_alert("Error opening segmentation image: %s", exc.what()); this->RedrawWindows(); } } // 3: Load Overlay else if(selection == 3) { try { NonInteractiveLoadOverlayImage(fn_open, false, false); } catch(itk::ExceptionObject &exc) { fl_alert("Error opening overlay image: %s", exc.what()); this->RedrawWindows(); } } // 4: Open in another SNAP else if(selection == 4) { // Generate the command line for the new SNAP std::list args; args.push_back("--main"); args.push_back(fn_open); if(!m_InOpenDroppedViewMode[0]->value()) { args.push_back("--compact"); if(m_InOpenDroppedViewMode[1]->value()) args.push_back("a"); else if(m_InOpenDroppedViewMode[2]->value()) args.push_back("c"); else if(m_InOpenDroppedViewMode[3]->value()) args.push_back("s"); } try { m_SystemInterface->LaunchChildSNAP(args); } catch(IRISException &exc) { fl_alert("Failed to open another ITK-SNAP instance. \nReason: %s", exc.what()); } } m_WinOpenDropped->hide(); } void UserInterfaceLogic ::OpenDraggedContent(const char *fn_open, bool interactive) { static string win_label; if(!m_Activation->GetFlag(UIF_BASEIMG_LOADED)) { NonInteractiveLoadMainImage(fn_open, false, false); } else if(interactive) { // Set the label win_label = fn_open; m_OutOpenDroppedFileName->label(win_label.c_str()); // Display modal dialog m_WinOpenDropped->show(); } else { // Generate the command line for the new SNAP std::list args; args.push_back("--main"); args.push_back(fn_open); try { m_SystemInterface->LaunchChildSNAP(args); } catch(IRISException &exc) { fl_alert("Failed to open another ITK-SNAP instance. \nReason: %s", exc.what()); } } } void UserInterfaceLogic ::GlobalIdleHandler(void *userData) { // Need base image for all of this if(m_GlobalUI->m_Activation->GetFlag(UIF_BASEIMG_LOADED)) { // Read the IPC message SystemInterface::IPCMessage ipcm; if(m_GlobalUI->m_SystemInterface->IPCReadIfNew(ipcm)) { // Update the cursor if(m_GlobalUI->m_BtnSynchronizeCursor->value()) { // Map the cursor position to the image coordinates GenericImageData *id = m_GlobalUI->m_Driver->GetCurrentImageData(); Vector3d vox = id->GetMain()->TransformNIFTICoordinatesToVoxelIndex(ipcm.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 != m_GlobalUI->m_Driver->GetCursorPosition() && id->GetImageRegion().IsInside(pos)) { m_GlobalUI->m_Driver->SetCursorPosition(vpos); m_GlobalUI->OnCrosshairPositionUpdate(false); m_GlobalUI->RedrawWindows(); } } // Update the view positions if(m_GlobalUI->m_ChkMultisessionPan->value() && m_GlobalUI->m_Activation->GetFlag(UIF_IRIS_ACTIVE)) { bool changed = false; for(size_t i = 0; i < 3; i++) { // Get the relative position of the viewport to cursor Vector2f vprel = m_GlobalUI->m_IRISWindowManager2D[i]->GetViewPositionRelativeToCursor(); // Get the relative position from the shared memory Vector2f vprel_new = ipcm.viewPositionRelative[i]; // Check if they are different if(vprel != vprel_new) { changed = true; m_GlobalUI->m_IRISWindowManager2D[i]->SetViewPositionRelativeToCursor(vprel_new); } } if(changed) m_GlobalUI->RedrawWindows(); } // Update the 3D trackball if(m_GlobalUI->m_BtnSynchronizeCursor->value()) { // Get the current 3D window object Window3D *w3d = m_GlobalUI->m_GlobalState->GetSNAPActive() ? m_GlobalUI->m_SNAPWindowManager3D : m_GlobalUI->m_IRISWindowManager3D; // Compare the two trackballs Trackball tball = w3d->GetTrackball(); char *p1 = reinterpret_cast(&tball); char *p2 = reinterpret_cast(&ipcm.trackball); if(!std::equal(p1,p1+sizeof(Trackball),p2)) { w3d->SetTrackball(ipcm.trackball); m_GlobalUI->RedrawWindows(); } } // Update the zoom factor (IRIS mode only) if(m_GlobalUI->m_ChkMultisessionZoom->value() && m_GlobalUI->m_Activation->GetFlag(UIF_IRIS_ACTIVE)) { if(ipcm.zoom_level != m_GlobalUI->m_SliceCoordinator->GetCommonZoomLevel()) { m_GlobalUI->m_SliceCoordinator->SetZoomLevelAllWindows(ipcm.zoom_level); m_GlobalUI->OnZoomUpdate(false); } } } } // This is a pretty bad hack, used as a workaround for FL_LEAVE events not being issued // when a GL window occupies the whole screen. DisplayLayout dl = m_GlobalUI->GetDisplayLayout(); if(dl.show_panel_ui == false) { int x, y; Fl::get_mouse(x,y); Fl_Window *wm = m_GlobalUI->GetMainWindow(); Fl_Window *wp = m_GlobalUI->GetPopupToolbarWindow(); if(x >= wm->x() && y >= wm->y() && x < wm->x()+wm->w() && y < wm->y()+wm->h()+10+wp->h()) { if(!wp->shown()) { wp->show(); wm->show(); } } else { if(wp->shown()) wp->hide(); } } Fl::repeat_timeout(0.03, &UserInterfaceLogic::GlobalIdleHandler); } int UserInterfaceLogic ::GlobalEventHandler(int ev) { return m_GlobalUI->OnGlobalEvent(ev); } #include "FL/Fl_Menu_Window.H" void UserInterfaceLogic ::SetDisplayLayout(DisplayLayout dl) { static int w = 0, h = 0, x = 0, y = 0; // Check compatibility rules (some states are incompatible) if(dl.size == HALF_SIZE && (dl.show_main_ui)) throw IRISException("Incorrect display layout"); // Handle full-screen toggle if(m_DisplayLayout.full_screen && !dl.full_screen) { if(m_DisplayLayout.size == FULL_SIZE) { m_WinMain->fullscreen_off(x,y,w,h); } else { m_WinMain->fullscreen_off(x,y,w / 2, h / 2); } m_FullScreen = false; } else if(!m_DisplayLayout.full_screen && dl.full_screen) { if(m_DisplayLayout.size == FULL_SIZE) { w = m_WinMain->w(); h = m_WinMain->h(); x = m_WinMain->x(); y = m_WinMain->y(); } m_WinMain->fullscreen(); m_FullScreen = true; } // Handle window size selection if(m_DisplayLayout.size == FULL_SIZE && dl.size == HALF_SIZE) { w = m_WinMain->w(); h = m_WinMain->h(); x = m_WinMain->x(); y = m_WinMain->y(); m_WinMain->size(w / 2, h / 2); } else if(m_DisplayLayout.size == HALF_SIZE && dl.size == FULL_SIZE) { // Restore the saved dimensions m_WinMain->size(w,h); m_WinMain->position(x,y); } // Handle the slice window selection (1 vs 4 slices) if(m_DisplayLayout.slice_config != dl.slice_config) { // The dimensions of the parent window int x = m_GrpRightPane->x(), y = m_GrpRightPane->y(); int w = m_GrpRightPane->w(), h = m_GrpRightPane->h(); // Restore all panels to original configuration m_WizSliceLayout[0]->resize(x, y, w >> 1, h >> 1); m_WizSliceLayout[1]->resize(x + (w >> 1), y, w - (w >> 1), h >> 1); m_WizSliceLayout[3]->resize(x, y + (h >> 1), w >> 1, h - (h >> 1)); m_WizSliceLayout[2]->resize(x + (w >> 1), y + (h >> 1), w - (w >> 1), h - (h >> 1)); // Make sure the resizable is set to self m_GrpRightPane->resizable(m_GrpRightPane); // Show everything for(unsigned int j = 0; j < 4; j++) { m_GrpRightPane->add(m_WizSliceLayout[j]); m_WizSliceLayout[j]->show(); m_SliceWindow[j]->show(); m_WizSliceLayout[j]->redraw(); } if(dl.slice_config != FOUR_SLICE) { for(int j = 0; j < 4; j++) { // We resize all the panels so that the zoom behaves // properly in linked zoom mode if(dl.slice_config != AXIAL + j) { m_WizSliceLayout[j]->hide(); m_SliceWindow[j]->hide(); m_GrpRightPane->remove(m_WizSliceLayout[j]); } else { m_WizSliceLayout[j]->show(); m_SliceWindow[j]->show(); m_WizSliceLayout[j]->resize( m_GrpRightPane->x(),m_GrpRightPane->y(), m_GrpRightPane->w(),m_GrpRightPane->h()); m_GrpRightPane->resizable(m_WizSliceLayout[j]); m_SliceWindow[j]->take_focus(); m_WizSliceLayout[j]->redraw(); } } } } // Do we need to hide the main pane if(m_DisplayLayout.show_main_ui && !dl.show_main_ui) { // Move the right pane over to the second part of the wizard m_GrpRightPanePlaceholderNormal->remove(m_GrpRightPane); m_GrpRightPanePlaceholderTight->insert(*m_GrpRightPane, 0); m_GrpRightPane->resize( m_GrpRightPanePlaceholderTight->x(), m_GrpRightPanePlaceholderTight->y(), m_GrpRightPanePlaceholderTight->w(), m_GrpRightPanePlaceholderTight->h()); m_WizMainLayout->value(m_GrpMainLayoutTight); // Shrink the main window if(!m_FullScreen) m_WinMain->size(m_WinMain->w()-145, m_WinMain->h()-25); } // Do we need to restore the main panel if(!m_DisplayLayout.show_main_ui && dl.show_main_ui) { // Restore the control panel and toolbar m_GrpRightPanePlaceholderTight->remove(m_GrpRightPane); m_GrpRightPanePlaceholderNormal->insert(*m_GrpRightPane, 0); m_GrpRightPane->resize( m_GrpRightPanePlaceholderNormal->x(), m_GrpRightPanePlaceholderNormal->y(), m_GrpRightPanePlaceholderNormal->w(), m_GrpRightPanePlaceholderNormal->h()); m_WizMainLayout->value(m_GrpMainLayoutNormal); // Grow the main window (as long as we don't grow over max size) if(!m_FullScreen) { int sx, sy, sw, sh; Fl::screen_xywh(sx, sy, sw, sh); if(m_WinMain->w() <= sw - 145 && m_WinMain->h() <= sh - 25) m_WinMain->size(m_WinMain->w()+145, m_WinMain->h()+25); } } // Do we need to hide the mini-panels if(m_DisplayLayout.show_panel_ui && !dl.show_panel_ui) { for(size_t i = 0; i < 4; i++) { // Move the slice window over to the second part of the wizard m_GrpSlicePlaceholder[i]->remove(m_SliceWindow[i]); m_GrpSliceLayoutTight[i]->insert(*m_SliceWindow[i], 0); m_SliceWindow[i]->resize( m_GrpSliceLayoutTight[i]->x(),m_GrpSliceLayoutTight[i]->y(), m_GrpSliceLayoutTight[i]->w(),m_GrpSliceLayoutTight[i]->h()); m_WizSliceLayout[i]->value(m_GrpSliceLayoutTight[i]); } // Shrink the main window if(!m_FullScreen) m_WinMain->size(m_WinMain->w()-64, m_WinMain->h()-70); } // Do we need to restore the mini-panels if(!m_DisplayLayout.show_panel_ui && dl.show_panel_ui) { // Restore the slice windows for(size_t i = 0; i < 4; i++) { m_GrpSliceLayoutTight[i]->remove(m_SliceWindow[i]); m_GrpSlicePlaceholder[i]->insert(*m_SliceWindow[i], 0); m_SliceWindow[i]->resize( m_GrpSlicePlaceholder[i]->x(), m_GrpSlicePlaceholder[i]->y(), m_GrpSlicePlaceholder[i]->w(), m_GrpSlicePlaceholder[i]->h()); m_WizSliceLayout[i]->value(m_GrpSliceLayoutNormal[i]); } // Grow the main window (as long as we don't grow over max size) if(!m_FullScreen) { int sx, sy, sw, sh; Fl::screen_xywh(sx, sy, sw, sh); if(m_WinMain->w() <= sw - 64 && m_WinMain->h() <= sh - 70) m_WinMain->size(m_WinMain->w()+64, m_WinMain->h()+70); } } if(dl.size == HALF_SIZE && !dl.show_panel_ui) { // Create a popup window so the user can close m_WinTestPop->position( m_WinMain->x() + m_WinMain->w() - (5 + m_WinTestPop->w()), m_WinMain->y() + m_WinMain->h() + 5); m_WinTestPop->show(); } else { m_WinTestPop->hide(); } // Store the new display layout m_DisplayLayout = dl; } void UserInterfaceLogic ::OnCollapsedViewPopupMenu() { // Show popup menu at the button location // Dynamically create menu, pop it up Fl_Menu_Button menu(Fl::event_x_root() - m_WinMain->x(), Fl::event_y_root() - m_WinMain->y(), 80, 1); Fl_Menu_Bar *bar = this->GetMainMenuBar(); menu.copy(bar->menu()); // Put the menu so that it's as deep in the widget hierarchy as the main menubar Fl_Group g1(0, 0, 80, 1); Fl_Group g2(0, 0, 80, 1); m_WinMain->add(g1); g1.add(g2); g2.add(menu); // Disable idle during popup Fl::remove_timeout(UserInterfaceLogic::GlobalIdleHandler); // Popup menu.popup(); m_WinMain->remove(g1); g1.remove(g2); g2.remove(menu); Fl::add_timeout(0.03, &UserInterfaceLogic::GlobalIdleHandler); } void SNAPMainWindow ::resize(int x, int y, int w, int h) { // Call parent class Fl_Double_Window::resize(x,y,w,h); // Move popup window (make more efficient) if(m_ParentUI) { DisplayLayout dl = m_ParentUI->GetDisplayLayout(); if(!dl.show_panel_ui) { Fl_Window *m = m_ParentUI->GetMainWindow(); Fl_Window *p = m_ParentUI->GetPopupToolbarWindow(); p->position( m->x() + m->w() - (5 + p->w()), m->y() + m->h() + 5 ); } } } void UserInterfaceLogic ::ToggleDisplayElements() { // Cycle through 4 visibility modes DisplayLayout dl = m_DisplayLayout; if(dl.show_main_ui && dl.show_panel_ui) { dl.show_main_ui = false; dl.show_panel_ui = true; dl.size = FULL_SIZE; } else if(!dl.show_main_ui && dl.show_panel_ui) { dl.show_main_ui = false; dl.show_panel_ui = false; dl.size = FULL_SIZE; } else if(!dl.show_main_ui && !dl.show_panel_ui && dl.size == FULL_SIZE) { dl.show_main_ui = false; dl.show_panel_ui = false; dl.size = HALF_SIZE; } else { dl.show_panel_ui = true; dl.show_main_ui = true; dl.size = FULL_SIZE; } SetDisplayLayout(dl); } /* void UserInterfaceLogic ::ToggleDisplayElements() { // First press: hide left pane and menubar if(m_WizMainLayout->value() == m_GrpMainLayoutNormal) { // Move the right pane over to the second part of the wizard m_GrpRightPanePlaceholderNormal->remove(m_GrpRightPane); m_GrpRightPanePlaceholderTight->insert(*m_GrpRightPane, 0); m_GrpRightPane->resize( m_GrpRightPanePlaceholderTight->x(), m_GrpRightPanePlaceholderTight->y(), m_GrpRightPanePlaceholderTight->w(), m_GrpRightPanePlaceholderTight->h()); m_WizMainLayout->value(m_GrpMainLayoutTight); // Shrink the main window if(!m_FullScreen) m_WinMain->size(m_WinMain->w()-145, m_WinMain->h()-25); } // Second press: hide panels for each window else if(m_WizSliceLayout[0]->value() == m_GrpSliceLayoutNormal[0]) { for(size_t i = 0; i < 4; i++) { // Move the slice window over to the second part of the wizard m_GrpSlicePlaceholder[i]->remove(m_SliceWindow[i]); m_GrpSliceLayoutTight[i]->insert(*m_SliceWindow[i], 0); m_SliceWindow[i]->resize( m_GrpSliceLayoutTight[i]->x(),m_GrpSliceLayoutTight[i]->y(), m_GrpSliceLayoutTight[i]->w(),m_GrpSliceLayoutTight[i]->h()); m_WizSliceLayout[i]->value(m_GrpSliceLayoutTight[i]); } // Shrink the main window if(!m_FullScreen) m_WinMain->size(m_WinMain->w()-64, m_WinMain->h()-70); } // Third press: restore everything else { // Restore the slice windows for(size_t i = 0; i < 4; i++) { m_GrpSliceLayoutTight[i]->remove(m_SliceWindow[i]); m_GrpSlicePlaceholder[i]->insert(*m_SliceWindow[i], 0); m_SliceWindow[i]->resize( m_GrpSlicePlaceholder[i]->x(), m_GrpSlicePlaceholder[i]->y(), m_GrpSlicePlaceholder[i]->w(), m_GrpSlicePlaceholder[i]->h()); m_WizSliceLayout[i]->value(m_GrpSliceLayoutNormal[i]); } // Restore the control panel and toolbar m_GrpRightPanePlaceholderTight->remove(m_GrpRightPane); m_GrpRightPanePlaceholderNormal->insert(*m_GrpRightPane, 0); m_GrpRightPane->resize( m_GrpRightPanePlaceholderNormal->x(), m_GrpRightPanePlaceholderNormal->y(), m_GrpRightPanePlaceholderNormal->w(), m_GrpRightPanePlaceholderNormal->h()); m_WizMainLayout->value(m_GrpMainLayoutNormal); // Grow the main window (as long as we don't grow over max size) if(!m_FullScreen) { int sx, sy, sw, sh; Fl::screen_xywh(sx, sy, sw, sh); if(m_WinMain->w() <= sw - 145 && m_WinMain->h() <= sh + 25) m_WinMain->size(m_WinMain->w()+145+64, m_WinMain->h()+25+70); } } } */ /* void UserInterfaceLogic ::ToggleFullScreen() { static int w = 0, h = 0, x = 0, y = 0; if(m_FullScreen) { m_WinMain->fullscreen_off(x,y,w,h); m_FullScreen = false; } else { w = m_WinMain->w(); h = m_WinMain->h(); x = m_WinMain->x(); y = m_WinMain->y(); m_WinMain->fullscreen(); m_FullScreen = true; } } */ void UserInterfaceLogic ::ToggleFullScreen() { DisplayLayout dl = m_DisplayLayout; dl.full_screen = !dl.full_screen; SetDisplayLayout(dl); } int UserInterfaceLogic ::OnGlobalEvent(int ev) { if(ev == FL_SHORTCUT) { // Opacity slider toggle/increase/decrease if(m_Activation->GetFlag(UIF_IRIS_WITH_BASEIMG_LOADED)) { if(Fl::test_shortcut('1')) { this->SetToolbarMode(CROSSHAIRS_MODE); return 1; } if(Fl::test_shortcut('2')) { this->SetToolbarMode(NAVIGATION_MODE); return 1; } if(Fl::test_shortcut('3')) { this->SetToolbarMode(POLYGON_DRAWING_MODE); return 1; } if(Fl::test_shortcut('4')) { this->SetToolbarMode(ROI_MODE); return 1; } if(Fl::test_shortcut('5')) { this->SetToolbarMode(PAINTBRUSH_MODE); return 1; } else if(Fl::test_shortcut('a')) { double opacity = m_InIRISLabelOpacity->value() - 8.0; if(opacity >= 0.0) m_InIRISLabelOpacity->value(opacity); OnIRISLabelOpacityChange(); return 1; } else if(Fl::test_shortcut('d')) { double opacity = m_InIRISLabelOpacity->value() + 8.0; if(opacity <= 255.0) m_InIRISLabelOpacity->value(opacity); OnIRISLabelOpacityChange(); return 1; } else if(Fl::test_shortcut('s')) { double opacity = m_InIRISLabelOpacity->value(); if(opacity > 0) { m_OpacityToggleValue = opacity; m_InIRISLabelOpacity->value(0); } else { m_InIRISLabelOpacity->value(m_OpacityToggleValue); m_OpacityToggleValue = 128; } OnIRISLabelOpacityChange(); return 1; } else if(Fl::test_shortcut('q')) { m_LayerUI->AdjustOverlayOpacity(-8.0); return 1; } else if(Fl::test_shortcut('e')) { m_LayerUI->AdjustOverlayOpacity(+8.0); return 1; } else if(Fl::test_shortcut('w')) { m_LayerUI->ToggleOverlayVisibility(); return 1; } else if(Fl::test_shortcut('z' | FL_COMMAND)) { if(m_BtnIRISUndo->active()) this->OnUndoAction(); return 1; } else if(Fl::test_shortcut('y' | FL_COMMAND)) { if(m_BtnIRISRedo->active()) this->OnRedoAction(); return 1; } // Cycle through available colormaps else if(Fl::test_shortcut('k')) { m_LayerUI->SelectNextColorMap(); return 1; } // Auto image contrast adjustment else if(Fl::test_shortcut(FL_ALT | 'i')) { m_LayerUI->OnAutoFitWindow(); return 1; } // Toggle 4 views / slice views else if(Fl::test_shortcut(FL_F + 2)) { DisplayLayout dl = m_DisplayLayout; switch(dl.slice_config) { case FOUR_SLICE : dl.slice_config = AXIAL; break; case AXIAL : dl.slice_config = CORONAL; break; case CORONAL : dl.slice_config = SAGITTAL; break; case SAGITTAL : dl.slice_config = THREED; break; case THREED : dl.slice_config = FOUR_SLICE; break; } SetDisplayLayout(dl); return 1; } // Toggle various controls else if(Fl::test_shortcut(FL_F + 3)) { ToggleDisplayElements(); return 1; } // F4 - fullscreen toggle else if(Fl::test_shortcut(FL_F + 4)) { ToggleFullScreen(); return 1; } // Ctrl-F - fit all views else if(Fl::test_shortcut(FL_COMMAND | 'f')) { this->OnResetAllViews2DAction(); return 1; } // center the 2D views (reset view positions) else if(Fl::test_shortcut(FL_COMMAND | FL_SHIFT | 'f')) { m_IRISWindowManager2D[0]->ResetViewPosition(); m_IRISWindowManager2D[1]->ResetViewPosition(); m_IRISWindowManager2D[2]->ResetViewPosition(); OnViewPositionsUpdate(); return 1; } // selecting active drawing label else if(Fl::test_shortcut(',') || Fl::test_shortcut('<')) { LabelType iDrawing = m_GlobalState->GetDrawingColorLabel(); for(size_t i = 0; i < static_cast(m_InDrawingColor->size()); i++) if(iDrawing == static_cast(reinterpret_cast(m_InDrawingColor->menu()[i].user_data())) && i > 1) { m_InDrawingColor->value(i-1); break; } OnDrawingLabelUpdate(); return 1; } else if(Fl::test_shortcut('.') || Fl::test_shortcut('>')) { LabelType iDrawing = m_GlobalState->GetDrawingColorLabel(); for(size_t i = 0; i < static_cast(m_InDrawingColor->size()); i++) if(iDrawing == static_cast(reinterpret_cast(m_InDrawingColor->menu()[i].user_data())) && i < static_cast(m_InDrawingColor->size())) { m_InDrawingColor->value(i+1); break; } OnDrawingLabelUpdate(); return 1; } // selecting drawing over label else if(Fl::test_shortcut(FL_COMMAND | ',') || Fl::test_shortcut(FL_COMMAND | '<')) { LabelType iDrawOver = m_GlobalState->GetOverWriteColorLabel(); if(m_GlobalState->GetCoverageMode() == PAINT_OVER_ALL) return 1; else if(m_GlobalState->GetCoverageMode() == PAINT_OVER_COLORS) m_InDrawOverColor->value(0); else for(size_t i = 0; i < static_cast(m_InDrawingColor->size()); i++) if(iDrawOver == static_cast(reinterpret_cast(m_InDrawingColor->menu()[i].user_data()))) { m_InDrawOverColor->value(i + 1); break; } OnDrawOverLabelUpdate(); return 1; } else if(Fl::test_shortcut(FL_COMMAND | '.') || Fl::test_shortcut(FL_COMMAND | '>')) { LabelType iDrawOver = m_GlobalState->GetOverWriteColorLabel(); if(m_GlobalState->GetCoverageMode() == PAINT_OVER_ALL) m_InDrawOverColor->value(1); else if(m_GlobalState->GetCoverageMode() == PAINT_OVER_COLORS) m_InDrawOverColor->value(2); else for(size_t i = 0; i < static_cast(m_InDrawingColor->size()); i++) if(iDrawOver == static_cast(reinterpret_cast(m_InDrawingColor->menu()[i].user_data())) && i < static_cast(m_InDrawingColor->size())) { m_InDrawOverColor->value(i + 3); break; } OnDrawOverLabelUpdate(); return 1; } // paintbrush size controls if(m_GlobalState->GetToolbarMode() == PAINTBRUSH_MODE) { if(Fl::test_shortcut('_') || Fl::test_shortcut('-')) { double pbsize = m_InPaintbrushSize->value() - 1.0; if(pbsize >= 1.0) m_InPaintbrushSize->value(pbsize); OnPaintbrushAttributesUpdate(); return 1; } else if(Fl::test_shortcut('=') || Fl::test_shortcut('+')) { double pbsize = m_InPaintbrushSize->value() + 1.0; if(pbsize <= 100.0) m_InPaintbrushSize->value(pbsize); OnPaintbrushAttributesUpdate(); return 1; } } } // if UIF_IRIS_WITH_BASEIMG_LOADED else if(m_Activation->GetFlag(UIF_SNAP_ACTIVE)) { // add/remove bubble controls if(m_GrpSNAPStepInitialize->visible()) { if(m_BtnRemoveBubble->active() && (Fl::test_shortcut('_') || Fl::test_shortcut('-'))) { this->OnRemoveBubbleAction(); return 1; } else if(m_BtnAddBubble->active() && (Fl::test_shortcut('=') || Fl::test_shortcut('+'))) { this->OnAddBubbleAction(); return 1; } if(m_InBubbleRadius->active() && (Fl::test_shortcut('{') || Fl::test_shortcut('['))) { double rad = m_InBubbleRadius->value(); if(rad >= 1) m_InBubbleRadius->value(rad - 1); this->OnBubbleRadiusChange(); return 1; } else if(m_InBubbleRadius->active() && (Fl::test_shortcut('}') || Fl::test_shortcut(']'))) { double rad = m_InBubbleRadius->value(); if(rad >= 1) m_InBubbleRadius->value(rad + 1); this->OnBubbleRadiusChange(); return 1; } } } // if UIF_SNAP_ACTIVE // The following shortcuts are general if(m_Activation->GetFlag(UIF_BASEIMG_LOADED)) { // toggle crosshairs display if(Fl::test_shortcut(FL_SHIFT | 'x')) { SNAPAppearanceSettings::Element &e = m_AppearanceSettings->GetUIElement(SNAPAppearanceSettings::CROSSHAIRS); e.Visible = !e.Visible; m_DlgAppearance->OnOptionsExternalUpdate(); RedrawWindows(); return 1; } else if(Fl::test_shortcut('x')) { m_AppearanceSettings->SetOverallVisibility( !m_AppearanceSettings->GetOverallVisibility()); m_DlgAppearance->OnOptionsExternalUpdate(); RedrawWindows(); return 1; } } } // if shortcut return 0; } void UserInterfaceLogic ::Launch() { // Add the global event handler UserInterfaceLogic::m_GlobalUI = this; Fl::add_handler(&UserInterfaceLogic::GlobalEventHandler); // Add the idle event handler Fl::add_timeout(0.03, &UserInterfaceLogic::GlobalIdleHandler); // Make sure the window is visible m_WinMain->show(); // Show all of the GL boxes for(unsigned int i = 0; i < 4; i++) m_SliceWindow[i]->show(); // Show the IRIS interface } void UserInterfaceLogic ::ShowIRIS() { // Show the right wizard page m_WizControlPane->value( m_Driver->GetCurrentImageData()->IsMainLoaded() ? m_GrpToolbarPage : m_GrpWelcomePage); // Show the right toolbar page under the render window m_WizRenderingToolbar->value(m_GrpRenderingIRISPage); // Assign the right window managers to the slice windows for(unsigned int i = 0; i < 3; i++) m_SliceWindow[i]->SetSingleInteractionMode(m_IRISWindowManager2D[i]); m_SliceWindow[3]->SetSingleInteractionMode(m_IRISWindowManager3D); // Clear the 3D window and reset the view m_IRISWindowManager3D->ClearScreen(); m_IRISWindowManager3D->ResetView(); // Change what's shown in the layer inspector OnLayerInspectorUpdate(); // Force a global redraw RedrawWindows(); } void UserInterfaceLogic ::ShowSNAP() { // Restore the IRIS view to four side-by-side windows OnWindowFocus(-1); // Swap the left-side panels m_WizControlPane->value(m_GrpSNAPPage); // Show the right toolbar page under the render window m_WizRenderingToolbar->value(m_GrpRenderingSNAPPage); // Assign the right window managers to the slice windows for(unsigned int i = 0; i < 3; i++) m_SliceWindow[i]->SetSingleInteractionMode(m_SNAPWindowManager2D[i]); m_SliceWindow[3]->SetSingleInteractionMode(m_SNAPWindowManager3D); // Clear the snap window and reset the view m_SNAPWindowManager3D->ClearScreen(); m_SNAPWindowManager3D->ResetView(); // Indicate that preprocessing is not done m_Activation->UpdateFlag(UIF_SNAP_ACTIVE, true); m_Activation->UpdateFlag(UIF_SNAP_PREPROCESSING_DONE, false); // Go to the first page in the SNAP wizard SetActiveSegmentationPipelinePage( 0 ); // Change what's shown in the layer inspector OnLayerInspectorUpdate(); // Repaint everything RedrawWindows(); } void UserInterfaceLogic ::InitializeUI() { // Make the menu bar global m_MenubarMain->global(); // Set the version text in the welcome page m_InWelcomePageVersion->label(SNAPUISoftVersion); m_InAboutPageVersion->label(SNAPUISoftVersion); // Sync global state to GUI m_BtnCrosshairsMode->setonly(); // SetToolbarMode(CROSSHAIRS_MODE); m_GlobalState->SetSegmentationAlpha(128); m_InIRISLabelOpacity->Fl_Valuator::value(128); // Initialize the color map for the first time UpdateColorLabelMenu(); // Window title UpdateMainLabel(); m_GlobalState->SetShowSpeed(false); m_InSNAPLabelOpacity->Fl_Valuator::value(128); //this should probably go into the .h, a #define or something m_InStepSize->add("1"); m_InStepSize->add("2"); m_InStepSize->add("5"); m_InStepSize->add("10"); // Initialize the toolbar at the bottom of 3D window m_ToolbarRenderWindow->spacing(5); // Apply the special appearance settings that determine startup behavior if(m_AppearanceSettings->GetFlagLinkedZoomByDefault()) { m_ChkLinkedZoom->value(1); OnLinkedZoomChange(); } if(m_AppearanceSettings->GetFlagMultisessionZoomByDefault()) { m_ChkMultisessionZoom->value(1); OnZoomUpdate(); } if(m_AppearanceSettings->GetFlagMultisessionPanByDefault()) { m_ChkMultisessionPan->value(1); OnViewPositionsUpdate(); } // Initialize the paintbrush panel UpdatePaintbrushAttributes(); // Set the anatomy to display transforms for each of the windows string rai1, rai2, rai3; m_AppearanceSettings->GetAnatomyToDisplayTransforms(rai1, rai2, rai3); // Update the user interface m_Driver->SetDisplayToAnatomyRAI(rai1.c_str(), rai2.c_str(), rai3.c_str()); OnImageGeometryUpdate(); // Initialize hidden feature usage option OnHiddenFeaturesToggleAction(); // Register the open document callback fl_open_callback(&UserInterfaceLogic::GlobalOpenDocumentHandler); // On Apple, we also want to register that handler with 'open contents' #ifdef __APPLE__ AEEventHandlerUPP handler; SRefCon refcon; AEGetEventHandler(kCoreEventClass, kAEOpenDocuments, &handler, &refcon, false); AEInstallEventHandler(kCoreEventClass, kAEOpenContents, handler, refcon, false); #endif m_WinMain->SetParentUI(this); DisplayTips(); } void UserInterfaceLogic ::UpdateColorLabelSelection() { // Set the current drawing label LabelType iDrawing = m_GlobalState->GetDrawingColorLabel(); LabelType iDrawOver = m_GlobalState->GetOverWriteColorLabel(); // Select the drawing label for(size_t i = 0; i < static_cast(m_InDrawingColor->size()); i++) if(iDrawing == static_cast(reinterpret_cast(m_InDrawingColor->menu()[i].user_data()))) m_InDrawingColor->value(i); // Set the draw over label if(m_GlobalState->GetCoverageMode() == PAINT_OVER_ALL) m_InDrawOverColor->value(0); else if (m_GlobalState->GetCoverageMode() == PAINT_OVER_COLORS) m_InDrawOverColor->value(1); else for(size_t j = 0; j < static_cast(m_InDrawingColor->size()); j++) if(iDrawOver == static_cast(reinterpret_cast(m_InDrawingColor->menu()[j].user_data()))) m_InDrawOverColor->value(j + 2); } void UserInterfaceLogic ::DeleteColorLabelMenu(Fl_Menu_Item *menu) { if(menu) { for(Fl_Menu_Item *mi = menu; mi->text != NULL; ++mi) delete mi->text; delete menu; } } void InitMenuItem(Fl_Menu_Item *p, const ColorLabel &cl, LabelType id) { char *strcopy = (char *) calloc(strlen(cl.GetLabel()) + 10, sizeof(char)); sprintf(strcopy, "%08x %s", fl_rgb_color(cl.GetRGB(0),cl.GetRGB(1),cl.GetRGB(2)), cl.GetLabel()); p->label(COLORBAR_LABEL, strcopy); p->shortcut(0); p->callback((Fl_Callback *) 0); p->user_data((void *) (size_t) id); p->flags = 0; p->labelcolor(FL_BLACK); p->labelfont(0); p->labelsize(0); } void InitMenuItem(Fl_Menu_Item *p, const char *text) { char *strcopy = (char *) calloc(strlen(text) + 1, sizeof(char)); strcpy(strcopy, text); p->label(FL_NORMAL_LABEL, strcopy); p->shortcut(0); p->callback((Fl_Callback *) 0); p->user_data(0); p->flags = 0; p->labelcolor(FL_BLACK); p->labelfont(0); p->labelsize(0); } Fl_Menu_Item * UserInterfaceLogic ::GenerateColorLabelMenu(bool all, bool visible, bool clear) { // Compute the number of labels ColorLabelTable *clt = m_Driver->GetColorLabelTable(); size_t n = clt->GetNumberOfValidLabels() - 1; if(all) n++; if(visible) n++; if(clear) n++; // Create the new menu and a pointer Fl_Menu_Item *menu = new Fl_Menu_Item[n+1], *p = menu; // Add the all labels item if(all) InitMenuItem(p++, "All labels"); if(visible) InitMenuItem(p++, "Visible labels"); if(clear) InitMenuItem(p++, clt->GetColorLabel(0), 0); for (size_t i = 1; i < MAX_COLOR_LABELS; i++) { const ColorLabel &cl = clt->GetColorLabel(i); if (cl.IsValid()) InitMenuItem(p++, cl, i); } // Last item is NULL p->text = NULL; // Return the menu return menu; } void UserInterfaceLogic ::UpdateColorLabelMenu() { // Set up the paint-over menu m_InDrawOverColor->clear(); DeleteColorLabelMenu(m_MenuDrawOverLabels); m_MenuDrawOverLabels = GenerateColorLabelMenu(true, true, true); m_InDrawOverColor->menu(m_MenuDrawOverLabels); // Set up the draw color menu m_InDrawingColor->clear(); DeleteColorLabelMenu(m_MenuDrawingLabels); m_MenuDrawingLabels = GenerateColorLabelMenu(false, false, true); m_InDrawingColor->menu(m_MenuDrawingLabels); // Update the color label that is currently selected UpdateColorLabelSelection(); } void UserInterfaceLogic ::RedrawWindows() { // Redraw the OpenGL windows m_SliceWindow[0]->redraw(); m_SliceWindow[1]->redraw(); m_SliceWindow[2]->redraw(); m_SliceWindow[3]->redraw(); // TODO: Do we really need this? // Redraw the current color swath (?) if(m_GrpSNAPCurrentColor->visible()) m_GrpSNAPCurrentColor->redraw(); } void UserInterfaceLogic ::ResetScrollbars() { // Get the cursor position in image coordinated Vector3d cursor = to_double(m_Driver->GetCursorPosition()); // Update the correct scroll bars for (unsigned int dim=0; dim<3; dim++) { // What image axis does dim correspond to? unsigned int imageAxis = GetImageAxisForDisplayWindow(dim); m_InSliceSlider[dim]->Fl_Valuator::value( -cursor[imageAxis] ); // Update the little display box at the bottom of the scroll bar UpdatePositionDisplay(dim); } } void UserInterfaceLogic ::OnCrosshairPositionUpdate(bool flagBroadcastUpdate) { ResetScrollbars(); UpdateImageProbe(); // When the crosshair position is updated, we send the information // to the inter-process communications system if(flagBroadcastUpdate && m_GlobalUI->m_Activation->GetFlag(UIF_BASEIMG_LOADED) && m_GlobalUI->m_BtnSynchronizeCursor->value()) { // Map the cursor to NIFTI coordinates Vector3d cursor = m_Driver->GetCurrentImageData()->GetMain()-> TransformVoxelIndexToNIFTICoordinates( to_double(m_Driver->GetCursorPosition())); // Write the NIFTI cursor to shared memory m_Driver->GetSystemInterface()->IPCBroadcastCursor(cursor); // Also trigger update in view positions b/c they are tied to the cursor this->OnViewPositionsUpdate(true); } } void UserInterfaceLogic ::OnMultisessionPanChange() { // Make sure we broadcast the current zoom level OnViewPositionsUpdate(); } void UserInterfaceLogic ::OnViewPositionsUpdate(bool flagBroadcastUpdate) { if(flagBroadcastUpdate && m_Activation->GetFlag(UIF_IRIS_ACTIVE) && m_ChkMultisessionPan->value()) { // For each slice, get the view position Vector2f vprel[3]; for(size_t i = 0; i < 3; i++) { // Get the view position in image coordinates vprel[i] = m_IRISWindowManager2D[i]->GetViewPositionRelativeToCursor(); } // Broadcast this view position m_SystemInterface->IPCBroadcastViewPosition(vprel); } this->RedrawWindows(); } void UserInterfaceLogic ::OnTrackballUpdate(bool flagBroadcastUpdate) { if(flagBroadcastUpdate && m_GlobalUI->m_Activation->GetFlag(UIF_BASEIMG_LOADED) && m_GlobalUI->m_BtnSynchronizeCursor->value()) { Trackball tball = m_GlobalState->GetSNAPActive() ? m_SNAPWindowManager3D->GetTrackball() : m_IRISWindowManager3D->GetTrackball(); m_Driver->GetSystemInterface()->IPCBroadcastTrackball(tball); } } void UserInterfaceLogic ::OnDrawingLabelUpdate() { // Get the drawing label that was selected LabelType iLabel = (LabelType) (size_t) m_InDrawingColor->mvalue()->user_data(); // Set the global state m_GlobalState->SetDrawingColorLabel((LabelType) iLabel); } void UserInterfaceLogic ::OnDrawOverLabelUpdate() { // See what the user selected if(m_InDrawOverColor->value() == 0) // The first menu item is 'paint over all' m_GlobalState->SetCoverageMode(PAINT_OVER_ALL); else if(m_InDrawOverColor->value() == 1) // The first menu item is 'paint over visible' m_GlobalState->SetCoverageMode(PAINT_OVER_COLORS); else { LabelType iLabel = (LabelType) (size_t) m_InDrawOverColor->mvalue()->user_data(); m_GlobalState->SetCoverageMode(PAINT_OVER_ONE); m_GlobalState->SetOverWriteColorLabel(iLabel); } } void UserInterfaceLogic ::UpdateImageProbe() { // Code common to SNAP and IRIS Vector3ui crosshairs = m_Driver->GetCursorPosition(); // String streams for different labels IRISOStringStream sGrey,sSegmentation,sSpeed; // Get the grey intensity GenericImageData *id = m_Driver->GetCurrentImageData(); if (m_Driver->GetCurrentImageData()->IsGreyLoaded()) { sGrey << id->GetGrey()->GetVoxelMappedToNative(crosshairs); } // Get the segmentation lavel intensity int iSegmentation = (int) id->GetSegmentation()->GetVoxel(crosshairs); sSegmentation << iSegmentation; // Update the cursor position in the image info window m_LayerUI->UpdateImageProbe(); // The rest depends on the current mode if(m_GlobalState->GetSNAPActive()) { // Fill the grey and label outputs m_OutSNAPProbe->value(sGrey.str().c_str()); m_OutSNAPLabelProbe->value(sSegmentation.str().c_str()); // Get a pointer to the speed image wrapper SNAPImageData *snap = m_Driver->GetSNAPImageData(); // Get the speed value (if possible) if(m_GlobalState->GetSpeedPreviewValid()) { // Speed preview is being shown. Get a preview pixel sSpeed << std::setprecision(4) << snap->GetSpeed()->GetPreviewVoxel(crosshairs); } else if(m_GlobalState->GetSpeedValid()) { // Speed image is valid, i.e., has been properly computed sSpeed << std::setprecision(4) << snap->GetSpeed()->GetVoxel(crosshairs); } else { sSpeed << "N/A"; } // Display the speed string m_OutSNAPSpeedProbe->value(sSpeed.str().c_str()); } else { m_OutGreyProbe->value(sGrey.str().c_str()); m_OutLabelProbe->value(sSegmentation.str().c_str()); // Get the label description ColorLabel cl = m_Driver->GetColorLabelTable()->GetColorLabel(iSegmentation); m_OutLabelProbeText->value(cl.GetLabel()); } } void UserInterfaceLogic ::UpdateMainLabel() { // Get the main image name string fnMain = ""; if(m_Activation->GetFlag(UIF_GRAY_LOADED)) fnMain = m_GlobalState->GetGreyFileName(); else if(m_Activation->GetFlag(UIF_RGB_LOADED)) fnMain = m_GlobalState->GetRGBFileName(); // Truncate the main image file if(fnMain.length()) fnMain = itksys::SystemTools::GetFilenameName(fnMain); else fnMain = "[No Image]"; // Get the segmentation file name string fnSeg = m_GlobalState->GetSegmentationFileName(); if(fnSeg.length()) fnSeg = itksys::SystemTools::GetFilenameName(fnSeg); else fnSeg = "[New Segmentation]"; // Build up the label std::ostringstream oss; // Print the main image oss << fnMain; // TODO: print if the main image is dirty // Print the segmentation image if(fnMain != "[No Image]") { oss << " - " << fnSeg; // Print the * if image is dirty if(m_Activation->GetFlag(UIF_UNSAVED_CHANGES)) oss << "*"; } // Print program name and version oss << " - " << SNAPSoftVersion; // Store the label m_MainWindowLabel = oss.str(); // Apply to the window m_WinMain->label(m_MainWindowLabel.c_str()); return; } void UserInterfaceLogic ::OnEditLabelsAction() { // Get the currently selected color index LabelType iLabel = (LabelType) (size_t) m_InDrawingColor->mvalue()->user_data(); m_LabelEditorUI->OnLabelListUpdate(iLabel); // Display the label editor m_LabelEditorUI->DisplayWindow(); } void UserInterfaceLogic ::UpdateEditLabelWindow() { /** // Get properties from VoxData int index = m_ColorMap[m_InDrawingColor->value()]; // Get the color label ColorLabel cl = m_Driver->GetCurrentImageData()->GetColorLabel(index); assert(cl.IsValid()); // Set widgets in EditLabel window m_InEditLabelName->value(cl.GetLabel()); m_InEditLabelAlpha->value(cl.GetAlpha() / 255.0); m_InEditLabelVisibility->value(!cl.IsVisible()); m_InEditLabelMesh->value(!cl.IsVisibleIn3D()); m_BtnEditLabelChange->setonly(); // convert from uchar [0,255] to double [0.0,1.0] m_GrpEditLabelColorChooser->rgb( cl.GetRGB(0)/255.0, cl.GetRGB(1)/255.0, cl.GetRGB(2)/255.0 ); // If this is clear label, assume we're going to add a label if (index == 0) { m_BtnEditLabelAdd->setonly(); m_InEditLabelName->value("New Label"); m_InEditLabelAlpha->value(1.0); m_InEditLabelVisibility->clear(); m_InEditLabelMesh->clear(); } char title[100]; sprintf(title, "Label No. %d", index); m_WinEditLabel->label(title); */ } /* void UserInterfaceLogic ::ChangeLabelsCallback() { // Check the new label name const char* new_name = m_InEditLabelName->value(); if (strlen(new_name) == 0) { m_OutMessage->value("You must enter a non-null name"); return; } // Update the label Choice widgets int offset; if (m_BtnEditLabelAdd->value() == 1) { // add a new label offset = m_InDrawingColor->add(new_name); m_InDrawOverColor->add(new_name); } else { // Replace an old label offset = m_InDrawingColor->value(); if (!offset) { m_OutMessage->value("You cannot recolor the clear color"); return; } m_InDrawingColor->replace(offset, new_name); m_InDrawOverColor->replace(offset+2, new_name); } m_InDrawingColor->value(offset); m_InDrawingColor->set_changed(); // Search for a place to put this new label if (m_ColorMap[offset] <0) { int i; for (i=1; i<256 && m_Driver->GetCurrentImageData()->GetColorLabel(i).IsValid(); i++) { } m_ColorMap[offset] = i; } unsigned char rgb[3]; rgb[0] = (uchar) ( 255*m_GrpEditLabelColorChooser->r() ); rgb[1] = (uchar) ( 255*m_GrpEditLabelColorChooser->g() ); rgb[2] = (uchar) ( 255*m_GrpEditLabelColorChooser->b() ); // Send changes to the Voxel Data Structure ColorLabel cl = m_Driver->GetCurrentImageData()->GetColorLabel(m_ColorMap[offset]); cl.SetRGBVector(rgb); cl.SetAlpha(static_cast(255 * m_InEditLabelAlpha->value())); cl.SetVisible(!m_InEditLabelVisibility->value()); cl.SetVisibleIn3D(!m_InEditLabelMesh->value()); cl.SetLabel(new_name); cl.SetValid(true); // Make sure that the display windows get the updated color labels m_Driver->GetCurrentImageData()->SetColorLabel(m_ColorMap[offset],cl); // Set the new current color in the GUI m_GrpCurrentColor->color(fl_rgb_color(rgb[0], rgb[1], rgb[2])); m_GlobalState->SetDrawingColorLabel((unsigned char) m_ColorMap[offset]); // The segmentation has become dirty m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); // Redraw the windows RedrawWindows(); m_WinMain->redraw(); } */ /** * This method is executed when the labels have been changed by the label editor * or by loading them from file */ void UserInterfaceLogic ::OnLabelListUpdate() { // Update the drop down box of labels to reflect the current settings UpdateColorLabelMenu(); // The label wrappers depend on color maps m_Driver->GetCurrentImageData()->GetSegmentation()->UpdateColorMappingCache(); // Repaint the windows RedrawWindows(); } void UserInterfaceLogic ::OnSliceSliderChange(int id) { // Get the new value depending on the current state unsigned int value = (unsigned int) ( - m_InSliceSlider[id]->value()); // Get the cursor position Vector3ui cursor = m_Driver->GetCursorPosition(); // Determine which image axis the display window 'id' corresponds to unsigned int imageAxis = GetImageAxisForDisplayWindow(id); // Update the cursor cursor[imageAxis] = value; m_Driver->SetCursorPosition(cursor); OnCrosshairPositionUpdate(); // Update the little display box under the scroll bar UpdatePositionDisplay(id); // Update the probe values UpdateImageProbe(); // Repaint the windows RedrawWindows(); } void UserInterfaceLogic ::UpdatePositionDisplay(int id) { // Dump out the slice index of the given window IRISOStringStream sIndex; sIndex << std::setw(4) << (1 - m_InSliceSlider[id]->value()); sIndex << " of "; sIndex << (1 - m_InSliceSlider[id]->minimum()); m_OutSliceIndex[id]->value(sIndex.str().c_str()); } void UserInterfaceLogic ::OnClosePolygonAction(unsigned int window) { m_IRISWindowManager2D[window]->GetPolygonDrawing()->ClosePolygon(); OnPolygonStateUpdate(window); } void UserInterfaceLogic ::OnUndoPointPolygonAction(unsigned int window) { m_IRISWindowManager2D[window]->GetPolygonDrawing()->DropLastPoint(); OnPolygonStateUpdate(window); } void UserInterfaceLogic ::OnClearPolygonAction(unsigned int window) { m_IRISWindowManager2D[window]->GetPolygonDrawing()->Reset(); OnPolygonStateUpdate(window); } void UserInterfaceLogic ::OnAcceptPolygonAction(unsigned int window) { if(m_IRISWindowManager2D[window]->AcceptPolygon()) { // The polygon update was successful OnPolygonStateUpdate(window); // The segmentation has been dirtied m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); // Set the undo point StoreUndoPoint("Polygon drawing"); } else { // Probably, the draw-over-label is set incorrectly! fl_message( "The segmentation was not updated. Most likely, the 'Draw Over' label \n" "is set incorrectly. Change it to 'All Labels' or 'Clear Label'."); } } void UserInterfaceLogic ::OnInsertIntoPolygonSelectedAction(unsigned int window) { m_IRISWindowManager2D[window]->InsertPolygonPoints(); OnPolygonStateUpdate(window); } void UserInterfaceLogic ::OnDeletePolygonSelectedAction(unsigned int window) { m_IRISWindowManager2D[window]->DeleteSelectedPolygonPoints(); OnPolygonStateUpdate(window); } void UserInterfaceLogic ::OnPastePolygonAction(unsigned int window) { m_IRISWindowManager2D[window]->PastePolygon(); OnPolygonStateUpdate(window); } void UserInterfaceLogic ::OnIRISFreehandFittingRateUpdate() { if(m_InIRISFreehandContinuous->value()) { m_InIRISFreehandFittingRate->deactivate(); for(size_t i = 0; i < 3; i++) { m_IRISWindowManager2D[i]->SetFreehandFittingRate(0); } } else { m_InIRISFreehandFittingRate->activate(); for(size_t i = 0; i < 3; i++) { m_IRISWindowManager2D[i]->SetFreehandFittingRate( m_InIRISFreehandFittingRate->value()); } } } void UserInterfaceLogic ::OnPolygonStateUpdate(unsigned int id) { // Get the drawing object PolygonDrawing *draw = m_IRISWindowManager2D[id]->GetPolygonDrawing(); if (draw->GetState() == PolygonDrawing::INACTIVE_STATE) { // Point to the appropriate page m_WizPolygon[id]->value(m_GrpPolygonInit[id]); // Set the activation status of paste button if(draw->GetCachedPolygon()) m_BtnPolygonPaste[id]->activate(); else m_BtnPolygonPaste[id]->deactivate(); } else if(draw->GetState() == PolygonDrawing::DRAWING_STATE) { // Point to the appropriate page m_WizPolygon[id]->value(m_GrpPolygonDraw[id]); // Activate buttons based on status if(draw->CanClosePolygon()) m_BtnPolygonClose[id]->activate(); else m_BtnPolygonClose[id]->deactivate(); if(draw->CanDropLastPoint()) m_BtnPolygonUndoPoint[id]->activate(); else m_BtnPolygonUndoPoint[id]->deactivate(); if(draw->CanDropLastPoint()) m_BtnPolygonClearDrawing[id]->activate(); else m_BtnPolygonClearDrawing[id]->deactivate(); } else { // Point to the appropriate page m_WizPolygon[id]->value(m_GrpPolygonEdit[id]); m_BtnPolygonAccept[id]->activate(); m_BtnPolygonClearEditing[id]->activate(); if(draw->GetSelectedVertices()) m_BtnPolygonDelete[id]->activate(); else m_BtnPolygonDelete[id]->deactivate(); if(draw->CanInsertVertices()) m_BtnPolygonInsert[id]->activate(); else m_BtnPolygonInsert[id]->deactivate(); } // Redraw the windows RedrawWindows(); } void UserInterfaceLogic ::UpdatePaintbrushAttributes() { PaintbrushSettings pbs = m_GlobalState->GetPaintbrushSettings(); // Get the mode (not nice C, but so be it) int mode = (int) pbs.mode; m_InPaintbrushShape->value(m_ChoicePaintbrush[mode]); m_WizPaintbrushParameters->value(m_GrpPaintbrushParameters[mode]); // Set the size m_InPaintbrushSize->value(pbs.radius * 2.0); // Set the other parameters m_BtnPaintbrushIsotropic->value(pbs.isotropic ? 1 : 0); m_BtnPaintbrushChaseMode->value(pbs.chase ? 1 : 0); m_BtnPaintbrush3D->value(pbs.flat ? 0 : 1); m_InPaintbrushSmoothing->value(pbs.watershed.smooth_iterations); m_InPaintbrushGranularity->value(pbs.watershed.level); } // Change the settings of the paintbrush tool void UserInterfaceLogic ::OnPaintbrushAttributesUpdate() { // Get the current paintbrush settings PaintbrushSettings pbs = m_GlobalState->GetPaintbrushSettings(); // Update the settings from the GUI pbs.radius = 0.5 * m_InPaintbrushSize->value(); pbs.flat = m_BtnPaintbrush3D->value() ? false : true; pbs.isotropic = m_BtnPaintbrushIsotropic->value() ? true : false; pbs.chase = m_BtnPaintbrushChaseMode->value() ? true : false; pbs.watershed.level = m_InPaintbrushGranularity->value(); pbs.watershed.smooth_iterations = (unsigned int) m_InPaintbrushSmoothing->value(); // Set the mode pbs.mode = (PaintbrushMode) (m_InPaintbrushShape->value()); // Update the wizard m_WizPaintbrushParameters->value( m_GrpPaintbrushParameters[m_InPaintbrushShape->value()]); // Set the paintbrush attributes m_GlobalState->SetPaintbrushSettings(pbs); // Update the windows RedrawWindows(); } void UserInterfaceLogic ::OnAnnotationAttributesUpdate() { // Get the current annotation settings AnnotationSettings as = m_GlobalState->GetAnnotationSettings(); // Update the settings from the GUI as.shownOnAllSlices = m_BtnAnnotationShownOnAllSlices->value() ? true : false; // Set the annotation settings m_GlobalState->SetAnnotationSettings(as); // Update the windows RedrawWindows(); } void UserInterfaceLogic ::OnPaintbrushPaint() { // The user painted something. We should make it possible to modifu the mesh m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); } void UserInterfaceLogic ::OnMenuShowDisplayOptions() { m_DlgAppearance->ShowDialog(); } void UserInterfaceLogic ::OnMenuShowLayerInspector() { // Make sure some image is loaded assert(m_Driver->GetCurrentImageData()->IsMainLoaded()); m_LayerUI->DisplayWindow(); } void UserInterfaceLogic ::OnLayerInspectorUpdate() { m_LayerUI->SetImageWrappers(); if (m_LayerUI->Shown()) { m_LayerUI->RedrawWindow(); } } void UserInterfaceLogic ::UpdateWindowFocus(Fl_Group *parent, Fl_Group **panels, Fl_Gl_Window **boxes, int iWindow) { /* // The dimensions of the parent window int x = parent->x(), y = parent->y(); int w = parent->w(), h = parent->h(); // Check if this is an expansion or a collapse operation if( iWindow < 0 || panels[iWindow]->w() == w ) { // Restore all panels to original configuration panels[0]->resize(x, y, w >> 1, h >> 1); panels[1]->resize(x + (w >> 1), y, w - (w >> 1), h >> 1); panels[3]->resize(x, y + (h >> 1), w >> 1, h - (h >> 1)); panels[2]->resize(x + (w >> 1), y + (h >> 1), w - (w >> 1), h - (h >> 1)); // Make the resizable is set to self parent->resizable(parent); // Show everything for(unsigned int j = 0; j < 4; j++) { parent->add(panels[j]); panels[j]->show(); boxes[j]->show(); panels[j]->redraw(); } } else { for(int j = 0; j < 4; j++) { // We resize all the panels so that the zoom behaves // properly in linked zoom mode if(iWindow != j) { panels[j]->hide(); boxes[j]->hide(); parent->remove(panels[j]); } } panels[iWindow]->resize( parent->x(),parent->y(), parent->w(),parent->h()); parent->resizable(panels[iWindow]); panels[iWindow]->redraw(); } */ } void UserInterfaceLogic ::OnWindowCollapse(int iWindow) { // Make only one window visible DisplayLayout dl = m_DisplayLayout; dl.slice_config = (SliceViewConfiguration) (AXIAL + iWindow); SetDisplayLayout(dl); // Hide the interface dl = m_DisplayLayout; dl.show_main_ui = false; dl.show_panel_ui = false; SetDisplayLayout(dl); // Change the layout to half-size dl = m_DisplayLayout; dl.size = HALF_SIZE; SetDisplayLayout(dl); // Lastly, get rid of unused space Vector2i optsize = m_SliceCoordinator->GetWindow(iWindow)->GetOptimalCanvasSize(); int w = std::min(optsize[0], m_WinMain->w()); int h = std::min(optsize[1], m_WinMain->h()); int x = m_WinMain->x() + (m_WinMain->w() - w) / 2; int y = m_WinMain->y() + (m_WinMain->h() - h) / 2; m_WinMain->resize(x, y, w, h); } void UserInterfaceLogic ::OnWindowFocus(int iWindow) { DisplayLayout dl = m_DisplayLayout; // If <0, enter four-slice if(iWindow < 0) dl.slice_config = FOUR_SLICE; // If 0-4, act as a toggle else if(dl.slice_config == AXIAL + iWindow) dl.slice_config = FOUR_SLICE; else dl.slice_config = (SliceViewConfiguration) (AXIAL + iWindow); // Update layout SetDisplayLayout(dl); } void UserInterfaceLogic ::OnImageGeometryUpdate() { if(!m_Driver->GetCurrentImageData()->IsMainLoaded()) return; // Set the crosshairs to the center of the image Vector3ui size = m_Driver->GetCurrentImageData()->GetVolumeExtents(); Vector3ui xCross = size / ((unsigned int) 2); m_Driver->SetCursorPosition(xCross); // Update the source for slice windows as well as scroll bars for (unsigned int i=0; i<3; i++) { if(m_GlobalState->GetSNAPActive()) m_SNAPWindowManager2D[i]->InitializeSlice(m_Driver->GetCurrentImageData()); else { m_IRISWindowManager2D[i]->InitializeSlice(m_Driver->GetCurrentImageData()); } // Get the image axis that corresponds to the display window i unsigned int imageAxis = GetImageAxisForDisplayWindow(i); // Notice the sliders have a negative range! That's so that the 1 position // is at the bottom. We need to always negate the slider values m_InSliceSlider[i]->range( 1.0 - size[imageAxis], 0.0 ); m_InSliceSlider[i]->slider_size( 1.0/ size[imageAxis] ); m_InSliceSlider[i]->linesize(1); } // Reset the view in 2D windows to fit m_SliceCoordinator->ResetViewToFitInAllWindows(); // Update the crosshairs display OnCrosshairPositionUpdate(); // Update view positions if(!m_GlobalState->GetSNAPActive()) OnViewPositionsUpdate(); // Fire zoom update event OnZoomUpdate(); } void UserInterfaceLogic ::OnMeshResetViewAction() { if(m_GlobalState->GetSNAPActive()) m_SNAPWindowManager3D->ResetView(); else m_IRISWindowManager3D->ResetView(); m_SliceWindow[3]->redraw(); } void UserInterfaceLogic ::OnIRISMeshAcceptAction() { m_IRISWindowManager3D->Accept(); RedrawWindows(); StoreUndoPoint("3D operation"); m_Activation->UpdateFlag(UIF_IRIS_MESH_ACTION_PENDING, false); m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); } void UserInterfaceLogic ::OnIRISMeshUpdateAction() { // Update the mesh and redraw the window m_IRISWindowManager3D->UpdateMesh(m_ProgressCommand); m_SliceWindow[3]->redraw(); // This is a safeguard in case the progress events do not fire m_WinProgress->hide(); m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, false); } void UserInterfaceLogic ::OnIRISMeshEditingAction() { m_Activation->UpdateFlag(UIF_IRIS_MESH_ACTION_PENDING, true); } void UserInterfaceLogic ::OnIRISMeshDisplaySettingsUpdate() { m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); } void UserInterfaceLogic ::OnSNAPMeshUpdateAction() { m_SNAPWindowManager3D->UpdateMesh(m_ProgressCommand); m_SliceWindow[3]->redraw(); // This is a safeguard in case the progress events do not fire m_WinProgress->hide(); m_Activation->UpdateFlag(UIF_SNAP_MESH_DIRTY, false); } void UserInterfaceLogic ::OnSNAPMeshContinuousUpdateAction() { if (m_ChkContinuousView3DUpdate->value()) { m_SNAPWindowManager3D->UpdateMesh(m_ProgressCommand); m_Activation->UpdateFlag(UIF_SNAP_MESH_CONTINUOUS_UPDATE, true); } else { m_Activation->UpdateFlag(UIF_SNAP_MESH_CONTINUOUS_UPDATE, false); } } void UserInterfaceLogic ::SetToolbarMode(ToolbarModeType mode) { // There must be an active image before we do this stuff assert(m_Driver->GetCurrentImageData()->IsMainLoaded()); // Set the mode on the toolbar m_GlobalState->SetToolbarMode(mode); // Set the mode of each window for(unsigned int i=0;i<3;i++) { switch(mode) { case CROSSHAIRS_MODE: m_IRISWindowManager2D[i]->EnterCrosshairsMode(); m_SNAPWindowManager2D[i]->EnterCrosshairsMode(); m_TabsToolOptions->value(m_GrpToolOptionCrosshairs); m_BtnCrosshairsMode->setonly(); m_BtnSNAPCrosshairs->setonly(); break; case NAVIGATION_MODE: m_IRISWindowManager2D[i]->EnterZoomPanMode(); m_SNAPWindowManager2D[i]->EnterZoomPanMode(); m_TabsToolOptions->value(m_GrpToolOptionZoomPan); m_BtnNavigationMode->setonly(); m_BtnSNAPNavigation->setonly(); break; case POLYGON_DRAWING_MODE: m_IRISWindowManager2D[i]->EnterPolygonMode(); m_TabsToolOptions->value(m_GrpToolOptionPolygon); m_BtnPolygonMode->setonly(); break; case PAINTBRUSH_MODE: m_IRISWindowManager2D[i]->EnterPaintbrushMode(); m_TabsToolOptions->value(m_GrpToolOptionBrush); m_BtnPaintbrushMode->setonly(); break; case ANNOTATION_MODE: m_IRISWindowManager2D[i]->EnterAnnotationMode(); m_TabsToolOptions->value(m_GrpToolOptionAnnotation); m_BtnAnnotationMode->setonly(); break; case ROI_MODE: m_IRISWindowManager2D[i]->EnterRegionMode(); m_TabsToolOptions->value(m_GrpToolOptionSNAP); m_BtnSNAPMode->setonly(); break; default: break; } // Enable the polygon editing windows if(mode == POLYGON_DRAWING_MODE) m_WizPolygon[i]->show(); else m_WizPolygon[i]->hide(); } // Redraw the windows RedrawWindows(); } void UserInterfaceLogic ::SetToolbarMode3D(ToolbarMode3DType mode) { switch (mode) { case TRACKBALL_MODE: m_IRISWindowManager3D->EnterTrackballMode(); m_SNAPWindowManager3D->EnterTrackballMode(); m_BtnTrackballMode->setonly(); m_BtnSNAPTrackballMode3D->setonly(); break; case CROSSHAIRS_3D_MODE: m_IRISWindowManager3D->EnterCrosshairsMode(); m_SNAPWindowManager3D->EnterCrosshairsMode(); m_BtnCrosshair3DMode->setonly(); m_BtnSNAPCrosshairsMode3D->setonly(); break; case SPRAYPAINT_MODE: m_IRISWindowManager3D->EnterSpraypaintMode(); m_BtnSpraypaintMode->setonly(); break; case SCALPEL_MODE: m_IRISWindowManager3D->EnterScalpelMode(); m_BtnScalpelMode->setonly(); break; default: break; } // Redraw the 3D windows m_SliceWindow[3]->redraw(); } void UserInterfaceLogic ::OnMenuShowVolumes() { // Display the load labels dialog m_WinStatistics->show(); this->OnStatisticsUpdateAction(); } void UserInterfaceLogic ::OnStatisticsUpdateAction() { SegmentationStatistics stats; stats.Compute(m_Driver->GetCurrentImageData()); m_TableStatistics->SetSegmentationStatistics(stats, *m_Driver->GetColorLabelTable()); m_TableStatistics->redraw(); } void UserInterfaceLogic ::OnStatisticsExportAction() { // Display the load labels dialog m_DlgVoxelCountsIO->SetTitle("Save Volume Statistics"); m_DlgVoxelCountsIO->DisplaySaveDialog( m_SystemInterface->GetHistory("VolumeStatistics")); } void UserInterfaceLogic ::OnStatisticsCopyAction() { // Get the selection string sel = m_TableStatistics->CopySelection(); Fl::copy(sel.c_str(), sel.length(), 1); } void UserInterfaceLogic ::OnMenuWriteVoxelCounts() { // Display the load labels dialog m_DlgVoxelCountsIO->SetTitle("Save Volume Statistics"); m_DlgVoxelCountsIO->DisplaySaveDialog( m_SystemInterface->GetHistory("VolumeStatistics")); } void UserInterfaceLogic ::OnWriteVoxelCountsAction() { // Get the selected file name const char *file = m_DlgVoxelCountsIO->GetFileName(); // Try writing try { // Compute the statistics and write them to file m_Driver->ExportSegmentationStatistics(file); // Update the history m_SystemInterface->UpdateHistory("VolumeStatistics",file); } catch (itk::ExceptionObject &exc) { // Alert the user to the failure fl_alert("Error writing volume statistics:\n%s",exc.GetDescription()); // Rethrow the exception throw; } } void UserInterfaceLogic ::OnGreyImageUnload() { // This method is called when a grey image gets unloaded. It saves the // current settings and associates them with the grey image file if (!m_Driver->GetCurrentImageData()->IsGreyLoaded()) return; // Make sure there is actually an image string fnGrey = m_GlobalState->GetGreyFileName(); if(fnGrey.length()) m_SystemInterface->AssociateCurrentSettingsWithCurrentImageFile( fnGrey.c_str(),m_Driver); } void UserInterfaceLogic ::UnloadAllImages() { // Close layer inspector if open if (m_LayerUI->Shown()) m_LayerUI->OnCloseAction(); // Close reorient image dialog if open if (m_DlgReorientImage->Shown()) m_DlgReorientImage->OnCloseAction(); // Close volumes and statistics if open if(m_WinStatistics->shown()) m_WinStatistics->hide(); // Clear the memory and reset the flags m_Driver->GetCurrentImageData()->UnloadMainImage(); if (m_GlobalState->GetSNAPActive()) { m_SNAPWindowManager2D[0]->InitializeSlice(m_Driver->GetCurrentImageData()); m_SNAPWindowManager2D[1]->InitializeSlice(m_Driver->GetCurrentImageData()); m_SNAPWindowManager2D[2]->InitializeSlice(m_Driver->GetCurrentImageData()); m_SNAPWindowManager3D->ClearScreen(); m_SNAPWindowManager3D->ResetView(); } else { m_IRISWindowManager2D[0]->InitializeSlice(m_Driver->GetCurrentImageData()); m_IRISWindowManager2D[1]->InitializeSlice(m_Driver->GetCurrentImageData()); m_IRISWindowManager2D[2]->InitializeSlice(m_Driver->GetCurrentImageData()); m_IRISWindowManager3D->ClearScreen(); m_IRISWindowManager3D->ResetView(); } m_Activation->UpdateFlag(UIF_OVERLAY_LOADED, false); m_Activation->UpdateFlag(UIF_BASEIMG_LOADED, false); } void UserInterfaceLogic ::OnMainImageUpdate() { // Update the list of recently open files GenerateRecentFilesMenu(); // Blank the screen - useful on a load of new grey data when there is // already a segmentation file present m_IRISWindowManager3D->ClearScreen(); // Flip over to the toolbar page m_WizControlPane->value(m_GrpToolbarPage); // Enable some menu items m_Activation->UpdateFlag(UIF_IRIS_WITH_BASEIMG_LOADED, true); // Disable undo and redo m_Activation->UpdateFlag(UIF_UNDO_POSSIBLE, m_Driver->IsUndoPossible()); m_Activation->UpdateFlag(UIF_REDO_POSSIBLE, m_Driver->IsRedoPossible()); // There are now no unsaved changes m_Activation->UpdateFlag(UIF_UNSAVED_CHANGES, false); // Update the layer inspector OnLayerInspectorUpdate(); // Image geometry has changed OnImageGeometryUpdate(); m_InDrawingColor->set_changed(); m_InDrawOverColor->set_changed(); // Update the label of the UI UpdateMainLabel(); m_GlobalState->SetSegmentationFileName(""); // Get the largest image region GlobalState::RegionType roi = m_Driver->GetIRISImageData()->GetImageRegion(); // Check if the region is real if (roi.GetNumberOfPixels() == 0) { m_Activation->UpdateFlag( UIF_IRIS_ROI_VALID, false ); m_GlobalState->SetIsValidROI(false); } else { m_Activation->UpdateFlag( UIF_IRIS_ROI_VALID, true ); m_GlobalState->SetSegmentationROI(roi); m_GlobalState->SetIsValidROI(true); } // Update the polygon buttons OnPolygonStateUpdate(0); OnPolygonStateUpdate(1); OnPolygonStateUpdate(2); // Now that we've loaded the image, check if there are any settings // associated with it. If there are, give the user an option to restore // these settings Registry associated; if(m_SystemInterface->FindRegistryAssociatedWithFile( m_GlobalState->GetGreyFileName(),associated)) // CHECK!!! { // Load the settings using RegistryIO SNAPRegistryIO rio; rio.ReadImageAssociatedSettings(associated,m_Driver,true,true,true,true); // Update the opacity slider m_InIRISLabelOpacity->value(m_GlobalState->GetSegmentationAlpha()); // Update the polygon inversion state m_ChkInvertPolygon->value(m_GlobalState->GetPolygonInvert() ? 1 : 0); } // Redraw the crosshairs in the 3D window m_IRISWindowManager3D->ResetView(); // Update the list of labels OnLabelListUpdate(); // Redraw the user interface RedrawWindows(); m_WinMain->redraw(); } void UserInterfaceLogic ::OnGreyImageUpdate() { // Disable/Enable some menu items m_Activation->UpdateFlag(UIF_IRIS_WITH_GRAY_LOADED, true); // Common user interface updates OnMainImageUpdate(); // Warn if the image has been scaled GreyTypeToNativeFunctor native = m_Driver->GetCurrentImageData()->GetGrey()->GetNativeMapping(); if(m_AppearanceSettings->GetFlagFloatingPointWarningByDefault() == 1 && (native.scale != 1.0 || native.shift != 0.0)) { m_WinPrecisionWarning->show(); if(m_ChkPrecisionWarningDisable->value()) m_AppearanceSettings->SetFlagFloatingPointWarningByDefault(false); } } void UserInterfaceLogic ::OnRGBImageUpdate() { // Common user interface updates OnMainImageUpdate(); // Disable/Enable some menu items m_Activation->UpdateFlag(UIF_GRAY_LOADED, false); m_Activation->UpdateFlag(UIF_RGB_LOADED, true); } void UserInterfaceLogic ::UpdateOverlaySlice() { // Update the source for slice windows if (m_GlobalState->GetSNAPActive()) { for (unsigned int i=0; i<3; i++) m_SNAPWindowManager2D[i]->InitializeOverlaySlice(m_Driver->GetCurrentImageData()); } else { for (unsigned int i=0; i<3; i++) m_IRISWindowManager2D[i]->InitializeOverlaySlice(m_Driver->GetCurrentImageData()); } if (!m_Driver->GetCurrentImageData()->IsOverlayLoaded()) m_Activation->UpdateFlag(UIF_OVERLAY_LOADED, false); } void UserInterfaceLogic ::OnOverlayImageUpdate() { // a main image has to be loaded assert(m_Driver->GetCurrentImageData()->IsMainLoaded()); // Update the source for slice windows UpdateOverlaySlice(); // Update the layer inspector OnLayerInspectorUpdate(); // Redraw the user interface RedrawWindows(); m_WinMain->redraw(); } void UserInterfaceLogic ::OnSegmentationImageUpdate(bool reloaded) { // Certain things required only if image is reloaded if(reloaded) { // When an image is reloaded, we clear the previous undo points m_Driver->ClearUndoPoints(); // The list of labels may have changed OnLabelListUpdate(); // The 3D window needs to be reset // m_IRISWindowManager3D->ResetView(); } // Toggle the undo/redo flags m_Activation->UpdateFlag(UIF_UNDO_POSSIBLE, m_Driver->IsUndoPossible()); m_Activation->UpdateFlag(UIF_REDO_POSSIBLE, m_Driver->IsRedoPossible()); // The mesh is now dirty m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); // Disable undo and redo RedrawWindows(); m_WinMain->redraw(); } /* void UserInterfaceLogic ::OnSegmentationLabelsUpdate(bool resetCurrentAndDrawOverLabels) { InitColorMap(resetCurrentAndDrawOverLabels); m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); m_WinMain->redraw(); m_OutMessage->value("Loading Label file successful"); } */ void UserInterfaceLogic ::OnSpeedImageUpdate() { if(m_GlobalState->GetSpeedValid()) { // Set UI state m_Activation->UpdateFlag(UIF_SNAP_SPEED_AVAILABLE, true); // Choose to view the preprocessed image m_ChoiceSNAPView->value(m_MenuSNAPViewPreprocessed); // m_RadioSNAPViewPreprocessed->setonly(); // Run the callback associated with that change OnViewPreprocessedSelect(); } else { // Set UI state m_Activation->UpdateFlag(UIF_SNAP_SPEED_AVAILABLE, false); // Choose to view the grey image m_ChoiceSNAPView->value(m_MenuSNAPViewOriginal); // m_RadioSNAPViewOriginal->setonly(); // Run the callback associated with that change OnSNAPViewOriginalSelect(); } // Make sure color mapping is correct UpdateSpeedColorMap(); } // This method should be called whenever the UI starts and every time that // the history is updated. void UserInterfaceLogic ::GenerateRecentFilesMenu() { // Load the list of recent files from the history file const SystemInterface::HistoryListType &history = m_SystemInterface->GetHistory("MainImage"); // Take the five most recent items and create menu items for(unsigned int i = 0; i < 5; i++) { // Get a pointer to the corresponding menu item Fl_Menu_Item *item = m_MenuLoadPreviousFirst + i; // Update it if( i < history.size()) { // Populate each of the menu items m_RecentFileNames[i] = history[history.size() - (i+1)]; item->label(m_RecentFileNames[i].c_str()); item->activate(); } else { m_RecentFileNames[i] = "Not Available"; item->label(m_RecentFileNames[i].c_str()); item->activate(); } } // Enable / disable the overall menu if(history.size()) m_MenuLoadPrevious->activate(); else m_MenuLoadPrevious->deactivate(); } void UserInterfaceLogic ::OnLoadRecentAction(unsigned int iRecent) { // Make sure the user doesn't lose any data if(!PromptBeforeLosingChanges(REASON_LOAD_MAIN)) return; // Get the history of grayscale images. Here we must be careful that every time // the history is updated, we also remember to update the recent files menu!!! const SystemInterface::HistoryListType &history = m_SystemInterface->GetHistory("MainImage"); // Check that the history is OK if(history.size() <= iRecent) { fl_alert("Unable to load recent file due to internal error!"); return; } // Get the recent file name string fnRecent = m_RecentFileNames[iRecent]; // Determine if the file is a grey or a RGB image bool isGrey = false; const SystemInterface::HistoryListType &greyHistory = m_SystemInterface->GetHistory("GreyImage"); for (unsigned int i = 0; i < greyHistory.size(); ++i) if (fnRecent == greyHistory[i]) isGrey = true; // Show a wait cursor m_WinMain->cursor(FL_CURSOR_WAIT, FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // TODO: At some point, we have to prompt the user that there are unsaved changes... try { // Load the file non-interactively if (isGrey) NonInteractiveLoadGrey(fnRecent.c_str()); else NonInteractiveLoadRGB(fnRecent.c_str()); // Restore the cursor m_WinMain->cursor(FL_CURSOR_DEFAULT, FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); } catch(itk::ExceptionObject &exc) { // Restore the cursor m_WinMain->cursor(FL_CURSOR_DEFAULT, FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Alert the user to the failure fl_alert("Error loading image:\n%s",exc.GetDescription()); this->RedrawWindows(); } } bool UserInterfaceLogic ::PromptBeforeLosingChanges(PromptReason reason) { // If there are unsaved changes, ask the user if they want to quit if(m_Activation->GetFlag(UIF_UNSAVED_CHANGES)) { // Show the dialog m_WinConfirmDiscard->show(); while(m_WinConfirmDiscard->shown()) Fl::wait(); // Check the value return m_ChkHiddenDiscardChanges->value() == 1; } else { return true; } } void UserInterfaceLogic ::NonInteractiveLoadMainImage(const char *fname, bool force_grey, bool force_rgb) { // Can't force both! assert(!force_grey || !force_rgb); // Perform the clean-up tasks before loading the image OnGreyImageUnload(); // Remember the current toolbar mode ToolbarModeType mode = m_GlobalState->GetToolbarMode(); // Unload all image data UnloadAllImages(); // Tell the driver to load the main image IRISApplication::MainImageType intype = force_grey ? IRISApplication::MAIN_SCALAR : (force_rgb ? IRISApplication::MAIN_RGB : IRISApplication::MAIN_ANY); IRISApplication::MainImageType type = m_Driver->LoadMainImage(fname, intype); if(type == IRISApplication::MAIN_SCALAR) { // Update the system's history list m_SystemInterface->UpdateHistory("GreyImage", itksys::SystemTools::CollapseFullPath(fname).c_str()); // Save the filename m_GlobalState->SetGreyFileName(fname); m_GlobalState->SetGreyExtension(fname); // Update the user interface accordingly OnGreyImageUpdate(); } else { // Add the filename to the history m_SystemInterface->UpdateHistory("RGBImage", itksys::SystemTools::CollapseFullPath(fname).c_str()); // Save the filename m_GlobalState->SetRGBFileName(fname); // Update the user interface accordingly OnRGBImageUpdate(); } // Recover the toolbar mode SetToolbarMode(mode); // Update the history for the main image m_SystemInterface->UpdateHistory("MainImage", itksys::SystemTools::CollapseFullPath(fname).c_str()); } void UserInterfaceLogic ::NonInteractiveLoadGrey(const char *fname) { // Perform the clean-up tasks before loading the image OnGreyImageUnload(); // Remember the current toolbar mode ToolbarModeType mode = m_GlobalState->GetToolbarMode(); // Unload all image data UnloadAllImages(); // Load the image on the logical side m_Driver->LoadMainImage(fname, IRISApplication::MAIN_SCALAR); // Update the system's history list m_SystemInterface->UpdateHistory("GreyImage", itksys::SystemTools::CollapseFullPath(fname).c_str()); m_SystemInterface->UpdateHistory("MainImage", itksys::SystemTools::CollapseFullPath(fname).c_str()); // Save the filename m_GlobalState->SetGreyFileName(fname); m_GlobalState->SetGreyExtension(fname); // Update the user interface accordingly OnGreyImageUpdate(); // Recover the toolbar mode SetToolbarMode(mode); } void UserInterfaceLogic ::OnMenuLoadGrey() { // Make sure the user doesn't lose any data if(!PromptBeforeLosingChanges(REASON_LOAD_MAIN)) return; // Set the history for the input wizard m_WizGreyIO->SetHistory(m_SystemInterface->GetHistory("GreyImage")); // Show the input wizard with no file selected m_WizGreyIO->DisplayInputWizard("", "Greyscale"); // If the load operation was successful, populate the data and GUI with the // new image if(m_WizGreyIO->IsImageLoaded()) { // Perform the clean-up tasks before loading the image OnGreyImageUnload(); // Remember the current toolbar mode ToolbarModeType mode = m_GlobalState->GetToolbarMode(); // Unload all image data UnloadAllImages(); // Send the image and RAI to the IRIS application driver m_Driver->UpdateIRISMainImage( m_WizGreyIO->GetNativeImageIO(), IRISApplication::MAIN_SCALAR); m_WizGreyIO->ReleaseImage(); // Update the system's history list m_SystemInterface->UpdateHistory("GreyImage", m_WizGreyIO->GetFileName()); m_SystemInterface->UpdateHistory("MainImage", m_WizGreyIO->GetFileName()); // Save the filename m_GlobalState->SetGreyFileName(m_WizGreyIO->GetFileName()); m_GlobalState->SetGreyExtension((char *)m_WizGreyIO->GetFileName()); // Update the user interface accordingly OnGreyImageUpdate(); // Recover the toolbar mode SetToolbarMode(mode); } } void UserInterfaceLogic ::NonInteractiveLoadRGB(const char *fname) { // Remember the current toolbar mode ToolbarModeType mode = m_GlobalState->GetToolbarMode(); // Unload all image data UnloadAllImages(); // Perform the loading on the Logic side m_Driver->LoadMainImage(fname, IRISApplication::MAIN_RGB); // Add the filename to the history m_SystemInterface->UpdateHistory("RGBImage", itksys::SystemTools::CollapseFullPath(fname).c_str()); m_SystemInterface->UpdateHistory("MainImage", itksys::SystemTools::CollapseFullPath(fname).c_str()); // Save the filename m_GlobalState->SetRGBFileName(fname); // Update the user interface accordingly OnRGBImageUpdate(); // Recover the toolbar mode SetToolbarMode(mode); } void UserInterfaceLogic ::NonInteractiveLoadOverlayImage(const char *fname, bool force_grey, bool force_rgb) { // Can't force both assert(!force_grey || !force_rgb); // Load using the right type IRISApplication::MainImageType intype = force_grey ? IRISApplication::MAIN_SCALAR : (force_rgb ? IRISApplication::MAIN_RGB : IRISApplication::MAIN_ANY); IRISApplication::MainImageType type = m_Driver->LoadOverlayImage(fname, intype); // Update the system's history list if(type == IRISApplication::MAIN_SCALAR) { m_SystemInterface->UpdateHistory("GreyOverlay", itksys::SystemTools::CollapseFullPath(fname).c_str()); } else { m_SystemInterface->UpdateHistory("RGBOverlay", itksys::SystemTools::CollapseFullPath(fname).c_str()); } // Update the overall overlay history m_SystemInterface->UpdateHistory("OverlayImage", itksys::SystemTools::CollapseFullPath(fname).c_str()); // Set the state m_Activation->UpdateFlag(UIF_OVERLAY_LOADED, true); // Update the user interface accordingly OnOverlayImageUpdate(); } void UserInterfaceLogic ::OnMenuLoadRGB() { // Make sure the user doesn't lose any data if(!PromptBeforeLosingChanges(REASON_LOAD_MAIN)) return; // Create the wizard RestrictedImageIOWizardLogic wizRGBIO; wizRGBIO.MakeWindow(); // Allow only 3 components wizRGBIO.SetNumberOfComponentsRestriction(3); // Set the history for the input wizard wizRGBIO.SetHistory(m_SystemInterface->GetHistory("RGBImage")); // Show the input wizard with no file selected wizRGBIO.DisplayInputWizard("", "RGB"); // If the load operation was successful, populate the data and GUI with the // new image if(wizRGBIO.IsImageLoaded()) { // Unload all image data UnloadAllImages(); // Remember the current toolbar mode ToolbarModeType mode = m_GlobalState->GetToolbarMode(); // Send the image and RAI to the IRIS application driver m_Driver->UpdateIRISMainImage( wizRGBIO.GetNativeImageIO(), IRISApplication::MAIN_RGB); // Update the system's history list m_SystemInterface->UpdateHistory("RGBImage",wizRGBIO.GetFileName()); m_SystemInterface->UpdateHistory("MainImage",wizRGBIO.GetFileName()); // Save the filename m_GlobalState->SetRGBFileName(wizRGBIO.GetFileName()); // Update the user interface accordingly OnRGBImageUpdate(); // Recover the toolbar mode SetToolbarMode(mode); } } void UserInterfaceLogic ::OnMenuLoadGreyOverlay() { // a main image should be loaded assert(m_Driver->GetCurrentImageData()->IsMainLoaded()); // Should not be in SNAP mode assert(!m_GlobalState->GetSNAPActive()); // Create the wizard RestrictedImageIOWizardLogic wizGreyOverlayIO; wizGreyOverlayIO.MakeWindow(); // Set up the input wizard with the main image wizGreyOverlayIO.SetMainImage( m_Driver->GetCurrentImageData()->GetMain()->GetImageBase()); // Set the history for the input wizard wizGreyOverlayIO.SetHistory( m_SystemInterface->GetHistory("GreyOverlay")); // Show the input wizard wizGreyOverlayIO.DisplayInputWizard( m_GlobalState->GetGreyOverlayFileName(), "Greyscale overlay"); // If the load operation was successful, populate the data and GUI with the // new image if(wizGreyOverlayIO.IsImageLoaded()) { // Send the image and RAI to the IRIS application driver m_Driver->AddIRISOverlayImage( wizGreyOverlayIO.GetNativeImageIO(), IRISApplication::MAIN_SCALAR); // Update the system's history list m_SystemInterface->UpdateHistory("GreyOverlay", wizGreyOverlayIO.GetFileName()); m_SystemInterface->UpdateHistory("OverlayImage", wizGreyOverlayIO.GetFileName()); // Save the filename m_GlobalState->SetGreyOverlayFileName(wizGreyOverlayIO.GetFileName()); // Set the state m_Activation->UpdateFlag(UIF_OVERLAY_LOADED, true); // Update the user interface accordingly OnOverlayImageUpdate(); } } void UserInterfaceLogic ::OnMenuLoadRGBOverlay() { // Grey image should be loaded assert(m_Driver->GetCurrentImageData()->IsMainLoaded()); // Should not be in SNAP mode assert(!m_GlobalState->GetSNAPActive()); // Create the wizard RestrictedImageIOWizardLogic wizRGBOverlayIO; wizRGBOverlayIO.MakeWindow(); // Allow only 3 components wizRGBOverlayIO.SetNumberOfComponentsRestriction(3); // Set up the input wizard with the main image wizRGBOverlayIO.SetMainImage( m_Driver->GetCurrentImageData()->GetMain()->GetImageBase()); // Set the history for the input wizard wizRGBOverlayIO.SetHistory( m_SystemInterface->GetHistory("RGBOverlay")); // Show the input wizard wizRGBOverlayIO.DisplayInputWizard( m_GlobalState->GetRGBOverlayFileName(), "RGB overlay"); // If the load operation was successful, populate the data and GUI with the // new image if (wizRGBOverlayIO.IsImageLoaded()) { // Update the system's history list m_SystemInterface->UpdateHistory("RGBOverlay",wizRGBOverlayIO.GetFileName()); m_SystemInterface->UpdateHistory("OverlayImage",wizRGBOverlayIO.GetFileName()); // Send the image and RAI to the IRIS application driver m_Driver->AddIRISOverlayImage( wizRGBOverlayIO.GetNativeImageIO(), IRISApplication::MAIN_RGB); // Release memory wizRGBOverlayIO.ReleaseImage(); // Save the filename m_GlobalState->SetRGBOverlayFileName(wizRGBOverlayIO.GetFileName()); // Update the user interface accordingly OnOverlayImageUpdate(); // Set the state m_Activation->UpdateFlag(UIF_OVERLAY_LOADED, true); } } void UserInterfaceLogic ::OnMenuUnloadOverlayLast() { if (m_Driver->GetCurrentImageData()->IsOverlayLoaded()) m_Driver->UnloadOverlayLast(); // Update the state if necessary if (!m_Driver->GetCurrentImageData()->IsOverlayLoaded()) m_Activation->UpdateFlag(UIF_OVERLAY_LOADED, false); OnOverlayImageUpdate(); } void UserInterfaceLogic ::OnMenuUnloadOverlays() { if (m_Driver->GetCurrentImageData()->IsOverlayLoaded()) m_Driver->UnloadOverlays(); // Update the state m_Activation->UpdateFlag(UIF_OVERLAY_LOADED, false); OnOverlayImageUpdate(); } void UserInterfaceLogic ::NonInteractiveLoadLabels(const char *file) { // Read the labels from file m_Driver->GetColorLabelTable()->LoadFromFile(file); // Reset the current drawing and overlay labels m_GlobalState->SetDrawingColorLabel( m_Driver->GetColorLabelTable()->GetFirstValidLabel()); m_GlobalState->SetOverWriteColorLabel(0); // Update the label editor window m_LabelEditorUI->OnLabelListUpdate( m_GlobalState->GetDrawingColorLabel()); // Update the user interface in response OnLabelListUpdate(); // Update the history m_SystemInterface->UpdateHistory("LabelDescriptions", itksys::SystemTools::CollapseFullPath(file).c_str()); } void UserInterfaceLogic ::OnLoadLabelsAction() { // Try reading the file try { // Read the labels from file NonInteractiveLoadLabels(m_DlgLabelsIO->GetFileName()); } catch (itk::ExceptionObject &exc) { // Alert the user to the failure fl_alert("Error loading labels:\n%s", exc.GetDescription()); // Rethrow the exception throw; } } void UserInterfaceLogic ::OnSaveLabelsAction() { // Get the selected file name const char *file = m_DlgLabelsIO->GetFileName(); // Try writing try { // Write labels to the file m_Driver->GetColorLabelTable()->SaveToFile(file); // Update the history m_SystemInterface->UpdateHistory("LabelDescriptions",file); } catch (itk::ExceptionObject &exc) { // Alert the user to the failure fl_alert("Error writing labels:\n%s",exc.GetDescription()); // Rethrow the exception throw; } } void UserInterfaceLogic ::OnMenuLoadLabels() { // Display the load labels dialog m_DlgLabelsIO->SetTitle("Load Labels"); m_DlgLabelsIO->DisplayLoadDialog( m_SystemInterface->GetHistory("LabelDescriptions")); } void UserInterfaceLogic ::OnMenuSaveLabels() { // Display the save labels dialog m_DlgLabelsIO->SetTitle("Save Labels"); m_DlgLabelsIO->DisplaySaveDialog( m_SystemInterface->GetHistory("LabelDescriptions")); } void UserInterfaceLogic ::OnActiveWindowSaveSnapshot(unsigned int window) { // Generate a filename for the screenshot std::string finput = GenerateScreenShotFilename(); // Create a file chooser std::string file = itksys::SystemTools::GetFilenameName(finput); std::string path = itksys::SystemTools::GetFilenamePath(finput); // Store the current directory std::string dir = itksys::SystemTools::GetCurrentWorkingDirectory(); if (path.size()) { itksys::SystemTools::ChangeDirectory(path.c_str()); } // We need to get a filename for the export Fl_Native_File_Chooser chooser; chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); chooser.title("Save PNG Snapshot"); chooser.filter("PNG Files\t*.png"); if (path.size()) { chooser.directory(path.c_str()); } chooser.preset_file(file.c_str()); const char *fChosen = NULL; if (chooser.show() == 0) { fChosen = chooser.filename(); } // Restore the current directory itksys::SystemTools::ChangeDirectory(dir.c_str()); if(!fChosen || !strlen(fChosen)) return; // Store the filename for incrementing numerical names m_LastSnapshotFileName = fChosen; // Check if the user omitted the extension if(itksys::SystemTools::GetFilenameExtension(m_LastSnapshotFileName) == "") m_LastSnapshotFileName = m_LastSnapshotFileName + ".png"; // Choose which window to save with if(window < 4) m_SliceWindow[window]->SaveAsPNG(m_LastSnapshotFileName.c_str()); } void UserInterfaceLogic ::OnMenuSaveScreenshot(unsigned int iSlice) { size_t iWindow = m_Driver->GetDisplayWindowForAnatomicalDirection( (AnatomicalDirection) iSlice); OnActiveWindowSaveSnapshot(iWindow); } void UserInterfaceLogic ::OnMenuSaveScreenshotSeries(unsigned int iSlice) { // iSlice needs to be between 0 and 2 assert (iSlice >= 0 && iSlice <= 2); // iSlice refers to which anatomical direction the user wants to // animate along (0 = axial, 1 = sagittal, 2 = coronal). We need // to know which window that corresponds to, as well as what image // dimension // Find the display window corresponding to this anatomical direction size_t iWindow = m_Driver->GetDisplayWindowForAnatomicalDirection( (AnatomicalDirection) iSlice); // Get the image slicing direction size_t iImageDir = m_Driver->GetImageDirectionForAnatomicalDirection( (AnatomicalDirection) iSlice); // let the user pick the directory for saving the screenshots Fl_Native_File_Chooser chooser; chooser.type(Fl_Native_File_Chooser::BROWSE_DIRECTORY); chooser.title("Select the directory to save the screenshots"); const char *path = NULL; if (chooser.show() == 0) { path = chooser.filename(); } // set up the 1st snapshot name std::string fname; if (path && strlen(path)) { fname = path; } else { return; } switch (iSlice) { default: case 0: fname += "axial0001.png"; break; case 1: fname += "sagittal0001.png"; break; case 2: fname += "coronal0001.png"; break; } // back up cursor location Vector3ui xCrossImageOld = m_Driver->GetCursorPosition(); Vector3ui xCrossImage = xCrossImageOld; Vector3ui xSize = m_Driver->GetCurrentImageData()->GetVolumeExtents(); xCrossImage[iImageDir] = 0; // turn sync off temporarily unsigned int syncValue = m_BtnSynchronizeCursor->value(); m_BtnSynchronizeCursor->value(0); for (size_t i = 0; i < xSize[iImageDir]; ++i) { m_Driver->SetCursorPosition(xCrossImage); OnCrosshairPositionUpdate(); RedrawWindows(); m_SliceWindow[iWindow]->SaveAsPNG(fname.c_str()); xCrossImage[iImageDir]++; m_LastSnapshotFileName = fname; fname = GenerateScreenShotFilename(); } // recover the original cursor position m_Driver->SetCursorPosition(xCrossImageOld); OnCrosshairPositionUpdate(); RedrawWindows(); // turn sync back on m_BtnSynchronizeCursor->value(syncValue); } void UserInterfaceLogic ::OnMenuExportSlice(unsigned int iSlice) { // Generate a default filename for this slice static const char *defpref[3] = {"axial", "sagittal", "coronal"}; char deffn[40]; // Figure out what slice it is size_t iSliceImg = m_Driver->GetImageDirectionForAnatomicalDirection((AnatomicalDirection) iSlice); sprintf(deffn,"%s_slice_%04d.png", defpref[iSlice], m_Driver->GetCursorPosition()[iSliceImg] + 1); // We need to get a filename for the export Fl_Native_File_Chooser chooser; chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); chooser.title("Export Slice As"); chooser.filter("Image Files\t*.{png,jpg,gif,tiff}"); chooser.preset_file(deffn); if (chooser.show() == 0) { const char *fName = chooser.filename(); if (fName && strlen(fName)) { m_Driver->ExportSlice((AnatomicalDirection) iSlice, fName); } } } void UserInterfaceLogic ::OnSynchronizeCursorAction() { if(m_BtnSynchronizeCursor->value()) { // Send the cursor position to the rest of the world OnCrosshairPositionUpdate(); } } void UserInterfaceLogic ::OnMenuNewSegmentation() { // Grey image should be loaded assert(m_Driver->GetCurrentImageData()->IsMainLoaded()); // Should not be in SNAP mode assert(!m_GlobalState->GetSNAPActive()); // Prompt to save changes if(!PromptBeforeLosingChanges(REASON_LOAD_SEGMENTATION)) return; // Get the driver to clear the image m_Driver->ClearIRISSegmentationImage(); // There are now no unsaved changes m_Activation->UpdateFlag(UIF_UNSAVED_CHANGES, false); // Clear the segmentation file name m_GlobalState->SetSegmentationFileName(""); // Update the main window label UpdateMainLabel(); // Save the state of the Undo manager at the time the image was updated m_UndoStateAtLastIO = m_Driver->GetUndoManager().GetState(); // Update the user interface OnSegmentationImageUpdate(true); } void UserInterfaceLogic ::LoadSegmentation(const bool noninteractive, const char *fname) { // Grey image should be loaded assert(m_Driver->GetCurrentImageData()->IsMainLoaded()); // Should not be in SNAP mode assert(!m_GlobalState->GetSNAPActive()); // Set the history for the input wizard m_WizSegmentationIO->SetHistory(m_SystemInterface->GetHistory("SegmentationImage")); // Set up the input wizard with the grey image m_WizSegmentationIO->SetMainImage( m_Driver->GetCurrentImageData()->GetMain()->GetImageBase()); if(noninteractive) { m_WizSegmentationIO->NonInteractiveInputWizard(fname); } else { // Show the input wizard m_WizSegmentationIO->DisplayInputWizard( m_GlobalState->GetLastAssociatedSegmentationFileName(), "segmentation"); } // If the load operation was successful, populate the data and GUI with the // new image if(m_WizSegmentationIO->IsImageLoaded()) { // Update the system's history list m_SystemInterface->UpdateHistory( "SegmentationImage",m_WizSegmentationIO->GetFileName()); // Send the image and RAI to the IRIS application driver try { m_Driver->UpdateIRISSegmentationImage(m_WizSegmentationIO->GetNativeImageIO()); } catch(IRISException &exc) { fl_alert("Loading segmentation failed.\nReason: %s", exc.what()); } // Discard all image data in the wizard m_WizSegmentationIO->ReleaseImage(); // Save the segmentation file name m_GlobalState->SetSegmentationFileName(m_WizSegmentationIO->GetFileName()); m_GlobalState->SetLastAssociatedSegmentationFileName(m_WizSegmentationIO->GetFileName()); // There are now no unsaved changes m_Activation->UpdateFlag(UIF_UNSAVED_CHANGES, false); // Update the label of the main window UpdateMainLabel(); // Save the state of the Undo manager at the time the image was updated m_UndoStateAtLastIO = m_Driver->GetUndoManager().GetState(); // Segmentation has been updated OnSegmentationImageUpdate(true); } // Disconnect the input wizard from the grey image m_WizSegmentationIO->SetMainImage(NULL); } void UserInterfaceLogic ::NonInteractiveLoadSegmentation(const char *fname) { LoadSegmentation(true, fname); } void UserInterfaceLogic ::OnMenuLoadSegmentation() { // Prompt to save changes if(!PromptBeforeLosingChanges(REASON_LOAD_SEGMENTATION)) return; LoadSegmentation(false); } void UserInterfaceLogic ::OnMenuSaveGrey() { // Set the history for the wizard m_WizGreyIO->SetHistory(m_SystemInterface->GetHistory("GreyImage")); // Save the segmentation if(m_WizGreyIO->DisplaySaveWizard( m_Driver->GetIRISImageData()->GetGrey()->GetImage(), NULL, "Greyscale")) { // Update the history for the wizard m_SystemInterface->UpdateHistory( "GreyImage", m_WizGreyIO->GetSaveFileName()); } } void UserInterfaceLogic ::OnMenuSaveGreyROI() { // Better be in snap assert(m_GlobalState->GetSNAPActive()); // Set the history for the wizard m_WizGreyIO->SetHistory(m_SystemInterface->GetHistory("GreyImage")); // Save the segmentation if(m_WizGreyIO->DisplaySaveWizard( m_Driver->GetCurrentImageData()->GetGrey()->GetImage(), NULL, "Greyscale")) { // Update the history for the wizard m_SystemInterface->UpdateHistory( "GreyImage",m_WizGreyIO->GetSaveFileName()); } } void UserInterfaceLogic ::OnMenuSaveSegmentation() { // Better have a segmentation image assert(m_Driver->GetIRISImageData()->IsSegmentationLoaded()); // Set the history for the wizard m_WizSegmentationIO->SetHistory( m_SystemInterface->GetHistory("SegmentationImage")); // Save the segmentation if(m_WizSegmentationIO->DisplaySaveWizard( m_Driver->GetIRISImageData()->GetSegmentation()->GetImage(), m_GlobalState->GetLastAssociatedSegmentationFileName(), "segmentation")) { // Update the history for the wizard m_SystemInterface->UpdateHistory( "SegmentationImage",m_WizSegmentationIO->GetSaveFileName()); // Store the new filename m_GlobalState->SetSegmentationFileName( m_WizSegmentationIO->GetSaveFileName()); m_GlobalState->SetLastAssociatedSegmentationFileName( m_WizSegmentationIO->GetSaveFileName()); // Save the state of the undo manager m_UndoStateAtLastIO = m_Driver->GetUndoManager().GetState(); // The segmentation is no longer dirty m_Activation->UpdateFlag(UIF_UNSAVED_CHANGES, false); } } void UserInterfaceLogic ::OnMenuSaveSegmentationMesh() { // Better have a segmentation image assert(m_Driver->GetIRISImageData()->IsSegmentationLoaded() || m_Driver->GetSNAPImageData()->IsSnakeLoaded()); // Send the history information to the wizard m_WizMeshExport->SetHistory( m_SystemInterface->GetHistory("SegmentationMesh")); // Display the segmentation wizard if(m_WizMeshExport->DisplayWizard(m_Driver, m_GlobalState->GetSNAPActive())) { // Get the save settings MeshExportSettings sets = m_WizMeshExport->GetExportSettings(); // Use the settings to save the mesh m_Driver->ExportSegmentationMesh(sets, m_ProgressCommand); // Update the history m_SystemInterface->UpdateHistory("SegmentationMesh", sets.GetMeshFileName().c_str()); } } void UserInterfaceLogic ::OnMenuLoadAdvection() { typedef itk::OrientedImage AdvectionImage; AdvectionImage::Pointer imgAdvection[3]; // Preprocessing image can only be loaded in SNAP mode assert(m_GlobalState->GetSNAPActive()); // Set up the input wizard with the grey image m_WizPreprocessingIO->SetMainImage( m_Driver->GetCurrentImageData()->GetMain()->GetImageBase()); // Load the three advection images as floating point bool flagLoadCompleted = true; for(unsigned int i=0;i<3;i++) { // Set the history for the input wizard m_WizPreprocessingIO->SetHistory( m_SystemInterface->GetHistory("AdvectionImage")); // Show the input wizard m_WizPreprocessingIO->DisplayInputWizard( m_GlobalState->GetAdvectionFileName(i), "ITK-SNAP preprocessing"); // If the load operation was successful, populate the data and GUI with the // new image if(m_WizPreprocessingIO->IsImageLoaded()) { // Update the system's history list m_SystemInterface->UpdateHistory( "AdvectionImage",m_WizPreprocessingIO->GetFileName()); // Update the application with the new speed image CastNativeImageToScalar caster; imgAdvection[i] = caster(m_WizPreprocessingIO->GetNativeImageIO()); m_WizPreprocessingIO->ReleaseImage(); // Save the segmentation file name m_GlobalState->SetAdvectionFileName(i, m_WizPreprocessingIO->GetFileName()); } else { flagLoadCompleted = false; break; } } // Disconnect the input wizard from the grey image m_WizPreprocessingIO->SetMainImage(NULL); // Add the advection image to SNAP if(flagLoadCompleted) m_Driver->GetSNAPImageData()->SetExternalAdvectionField( imgAdvection[0],imgAdvection[1],imgAdvection[2]); } void UserInterfaceLogic ::OnLoadPreprocessedImageAction() { // Preprocessing image can only be loaded in SNAP mode assert(m_GlobalState->GetSNAPActive()); // Set up the input wizard with the grey image m_WizPreprocessingIO->SetMainImage( m_Driver->GetCurrentImageData()->GetMain()->GetImageBase()); // Set the history for the input wizard m_WizPreprocessingIO->SetHistory( m_SystemInterface->GetHistory("PreprocessingImage")); // Show the input wizard m_WizPreprocessingIO->DisplayInputWizard( m_GlobalState->GetLastAssociatedPreprocessingFileName(), "ITK-SNAP preprocessing"); // If the load operation was successful, populate the data and GUI with the // new image if(m_WizPreprocessingIO->IsImageLoaded()) { // Update the system's history list m_SystemInterface->UpdateHistory( "PreprocessingImage",m_WizPreprocessingIO->GetFileName()); // Cast image to float CastNativeImageToScalar caster; IRISApplication::SpeedImageType::Pointer imgSpeed = caster(m_WizPreprocessingIO->GetNativeImageIO()); m_WizPreprocessingIO->ReleaseImage(); // Update the application with the new speed image m_Driver->UpdateSNAPSpeedImage(imgSpeed, m_RadSnakeEdge->value() ? EDGE_SNAKE : IN_OUT_SNAKE); // Save the segmentation file name m_GlobalState->SetPreprocessingFileName(m_WizPreprocessingIO->GetFileName()); m_GlobalState->SetLastAssociatedPreprocessingFileName(m_WizPreprocessingIO->GetFileName()); // Update the UI flag m_Activation->UpdateFlag(UIF_SNAP_PREPROCESSING_DONE, true); // Update the user interface accordingly OnSpeedImageUpdate(); } // Disconnect the input wizard from the grey image m_WizPreprocessingIO->SetMainImage(NULL); } void UserInterfaceLogic ::OnMenuSavePreprocessed() { // Better have a speed image to save assert(m_GlobalState->GetSpeedValid()); // Set the history for the wizard m_WizPreprocessingIO->SetHistory( m_SystemInterface->GetHistory("PreprocessingImage")); // Save the speed if(m_WizPreprocessingIO->DisplaySaveWizard( m_Driver->GetSNAPImageData()->GetSpeed()->GetImage(), m_GlobalState->GetLastAssociatedPreprocessingFileName(), "ITK-SNAP preprocessing")) { // Update the history for the wizard m_SystemInterface->UpdateHistory( "PreprocessingImage",m_WizPreprocessingIO->GetSaveFileName()); // Store the new filename m_GlobalState->SetPreprocessingFileName( m_WizPreprocessingIO->GetSaveFileName()); m_GlobalState->SetLastAssociatedPreprocessingFileName( m_WizPreprocessingIO->GetSaveFileName()); } } void UserInterfaceLogic ::OnMenuSaveLevelSet() { // Better have a snake image to save assert(m_Driver->GetSNAPImageData()->IsSnakeLoaded()); // Set the history for the wizard m_WizLevelSetIO->SetHistory( m_SystemInterface->GetHistory("LevelSetImage")); // Save the speed if(m_WizLevelSetIO->DisplaySaveWizard( m_Driver->GetSNAPImageData()->GetSnake()->GetImage(), m_GlobalState->GetLevelSetFileName(), "ITK-SNAP levelset")) { // Update the history for the wizard m_SystemInterface->UpdateHistory( "LevelSetImage", m_WizLevelSetIO->GetSaveFileName()); // Store the new filename m_GlobalState->SetLevelSetFileName( m_WizPreprocessingIO->GetSaveFileName()); } } void UserInterfaceLogic ::OnMenuIntensityCurve() { // The image should be loaded before bringing up the curve assert(m_Driver->GetCurrentImageData()->IsGreyLoaded()); // Show the window m_LayerUI->DisplayImageContrastTab(); } void UserInterfaceLogic ::OnMenuColorMap() { // The image should be loaded before bringing up the color map assert(m_Driver->GetCurrentImageData()->IsGreyLoaded()); // Show the window m_LayerUI->DisplayColorMapTab(); } void UserInterfaceLogic ::OnIRISLabelOpacityChange() { m_GlobalState->SetSegmentationAlpha( (unsigned char) m_InIRISLabelOpacity->value()); RedrawWindows(); } void UserInterfaceLogic ::OnSNAPLabelOpacityChange() { m_GlobalState->SetSegmentationAlpha( (unsigned char) m_InSNAPLabelOpacity->value()); RedrawWindows(); } void UserInterfaceLogic ::OnLaunchTutorialAction() { // Find the tutorial file name ShowHTMLPage("Tutorial.html"); } void UserInterfaceLogic ::ShowHTMLPage(const char *link) { // Get the path to the file name string completeLink = string("HTMLHelp/") + link; string file = m_SystemInterface->GetFileInRootDirectory(completeLink.c_str()); // Show the help window m_HelpUI->ShowHelp(file.c_str()); } void UserInterfaceLogic ::OnMenuCheckForUpdate() { // Show a wait cursor m_WinMain->cursor(FL_CURSOR_WAIT, FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Check for updates using new socket code std::string nver; SystemInterface::UpdateStatus us = m_SystemInterface->CheckUpdate(nver, 1, 0, true); std::string message; int response = 0; if(us == SystemInterface::US_UP_TO_DATE) { fl_message("Your version of ITK-SNAP is up to date"); } else if(us == SystemInterface::US_OUT_OF_DATE) { response = fl_choice( "A newer ITK-SNAP version %s is available!", "Download Later", "Download Now", NULL, nver.c_str()); if (response) { fl_open_uri("http://www.itksnap.org/pmwiki/pmwiki.php?n=Main.Downloads"); } } else if(us == SystemInterface::US_CONNECTION_FAILED) { response = fl_choice( "Could not connect to server.\n" "Visit itksnap.org to see if a new version is available", "Later", "Take me there now", NULL); if (response) { fl_open_uri("http://www.itksnap.org/pmwiki/pmwiki.php?n=Main.Downloads"); } } // Show a wait cursor m_WinMain->cursor(FL_CURSOR_DEFAULT, FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); } void UserInterfaceLogic ::OnCheckForUpdate() { // Show a wait cursor m_WinMain->cursor(FL_CURSOR_WAIT, FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Check for updates using new socket code std::string nver; SystemInterface::UpdateStatus us = m_SystemInterface->CheckUpdate(nver, 1, 0); std::string message; int response = 0; if (us == SystemInterface::US_OUT_OF_DATE) { message = "A newer ITK-SNAP version " + nver + " is available!"; response = fl_choice( "A newer ITK-SNAP version %s is available!", "Download Later", "Download Now", NULL, nver.c_str()); if (response) { fl_open_uri("http://www.itksnap.org/pmwiki/pmwiki.php?n=Main.Downloads"); } } // Show a wait cursor m_WinMain->cursor(FL_CURSOR_DEFAULT, FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); } void UserInterfaceLogic ::ShowSplashScreen() { // Place the window in the center of display CenterChildWindowInMainWindow(m_WinSplash); // Show the splash screen m_WinSplash->show(); // Show a wait cursor m_WinSplash->cursor(FL_CURSOR_WAIT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); m_WinMain->cursor(FL_CURSOR_WAIT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Make FL update the screen Fl::check(); // Save the time of the splash screen m_SplashScreenStartTime = clock(); } void UserInterfaceLogic ::HideSplashScreen() { // Wait a second with the splash screen while(clock() - m_SplashScreenStartTime < CLOCKS_PER_SEC) {} // Clear the cursor m_WinMain->cursor(FL_CURSOR_DEFAULT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); m_WinSplash->cursor(FL_CURSOR_DEFAULT,FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR); // Hide the screen m_WinSplash->hide(); // Check for available update int response = 0; if (m_AppearanceSettings->GetFlagEnableAutoCheckForUpdateByDefault() == -1) { response = fl_choice("ITK-SNAP can check for software update automatically. Do you want to enable this feature?", "No Thanks", "Yes, please", NULL); if (response) { m_AppearanceSettings->SetFlagEnableAutoCheckForUpdateByDefault(1); OnCheckForUpdate(); } else m_AppearanceSettings->SetFlagEnableAutoCheckForUpdateByDefault(0); m_AppearanceSettings->SaveToRegistry(m_SystemInterface->Folder("UserInterface.AppearanceSettings")); } else if (m_AppearanceSettings->GetFlagEnableAutoCheckForUpdateByDefault()) OnCheckForUpdate(); } void UserInterfaceLogic ::UpdateSplashScreen(const char *message) { m_OutSplashMessage->label(message); Fl::check(); // Save the time of the splash screen m_SplashScreenStartTime = clock(); } void UserInterfaceLogic ::CenterChildWindowInParentWindow(Fl_Window *childWindow, Fl_Window *parentWindow) { int px = parentWindow->x() + (parentWindow->w() - childWindow->w()) / 2; int py = parentWindow->y() + (parentWindow->h() - childWindow->h()) / 2; childWindow->resize(px,py,childWindow->w(),childWindow->h()); } void UserInterfaceLogic ::CenterChildWindowInMainWindow(Fl_Window *childWindow) { CenterChildWindowInParentWindow(childWindow,m_WinMain); } void UserInterfaceLogic ::OnResetView2DAction(unsigned int window) { // Resets to optimal fit m_SliceCoordinator->ResetViewToFitInOneWindow(window); // Update the zoom level display OnZoomUpdate(); // Update the view position OnViewPositionsUpdate(); } void UserInterfaceLogic ::OnResetAllViews2DAction() { m_SliceCoordinator->ResetViewToFitInAllWindows(); // Update the zoom level display OnZoomUpdate(); // Update the view position OnViewPositionsUpdate(); } void UserInterfaceLogic ::OnLinkedZoomChange() { if(m_ChkLinkedZoom->value() > 0) { m_SliceCoordinator->SetLinkedZoom(true); m_Activation->UpdateFlag(UIF_LINKED_ZOOM, true); } else { m_ChkMultisessionZoom->value(0); m_ChkMultisessionPan->value(0); m_SliceCoordinator->SetLinkedZoom(false); m_Activation->UpdateFlag(UIF_LINKED_ZOOM, false); } // Update the zoom level display OnZoomUpdate(); } void UserInterfaceLogic ::OnMultisessionZoomChange() { // Make sure we broadcast the current zoom level OnZoomUpdate(); } void UserInterfaceLogic ::SetZoomLevelAllWindows(float zoom) { m_InZoomLevel->value(zoom); this->OnZoomLevelChange(); } void UserInterfaceLogic ::OnZoomLevelChange() { float zoom = m_InZoomLevel->value(); m_SliceCoordinator->SetZoomLevelAllWindows(zoom); OnZoomUpdate(); } void UserInterfaceLogic ::OnZoomUpdate(bool flagBroadcast) { // This method should be called whenever the zoom changes if(m_SliceCoordinator->GetLinkedZoom()) { // Get the zoom from the first window and display it on the screen m_InZoomLevel->value(m_SliceCoordinator->GetCommonZoomLevel()); // Broadcast the zoom level to other sessions if(flagBroadcast && m_ChkMultisessionZoom->value() && m_Activation->GetFlag(UIF_IRIS_ACTIVE)) { m_SystemInterface->IPCBroadcastZoomLevel( m_SliceCoordinator->GetCommonZoomLevel()); } } else { // Otherwise, clear the display m_InZoomLevel->value(0); } } // Get the window under mouse focus or -1 if none int UserInterfaceLogic ::GetWindowUnderFocus(void) { for(int i = 0; i < 3; i++) if(m_SliceCoordinator->GetWindow(i)->GetCanvas()->GetFocus()) return i; return -1; } std::string UserInterfaceLogic ::GenerateScreenShotFilename() { // Get the last screen shot filename used std::string last = m_LastSnapshotFileName; 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(m_LastSnapshotFileName); 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 m_LastSnapshotFileName; // 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(m_LastSnapshotFileName); return oss.str(); } void UserInterfaceLogic ::OnUndoAction() { m_Driver->Undo(); // Update the flags in the UI m_Activation->UpdateFlag(UIF_UNDO_POSSIBLE, m_Driver->IsUndoPossible()); m_Activation->UpdateFlag(UIF_REDO_POSSIBLE, m_Driver->IsRedoPossible()); m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); // Update the saved changes flag (now true, unless equal to saved image) m_Activation->UpdateFlag(UIF_UNSAVED_CHANGES, m_UndoStateAtLastIO != m_Driver->GetUndoManager().GetState()); RedrawWindows(); } void UserInterfaceLogic ::OnRedoAction() { m_Driver->Redo(); // Update the flags in the UI m_Activation->UpdateFlag(UIF_UNDO_POSSIBLE, m_Driver->IsUndoPossible()); m_Activation->UpdateFlag(UIF_REDO_POSSIBLE, m_Driver->IsRedoPossible()); m_Activation->UpdateFlag(UIF_IRIS_MESH_DIRTY, true); // Update the saved changes flag (now true, unless equal to saved image) m_Activation->UpdateFlag(UIF_UNSAVED_CHANGES, m_UndoStateAtLastIO != m_Driver->GetUndoManager().GetState()); RedrawWindows(); } void UserInterfaceLogic ::StoreUndoPoint(const char *text) { // the actual undo point is handled in IRISApplication m_Driver->StoreUndoPoint(text); // Update the flags in the UI m_Activation->UpdateFlag(UIF_UNDO_POSSIBLE, m_Driver->IsUndoPossible()); m_Activation->UpdateFlag(UIF_REDO_POSSIBLE, m_Driver->IsRedoPossible()); // Update the saved changes flag (now true, unless equal to saved image) m_Activation->UpdateFlag(UIF_UNSAVED_CHANGES, m_UndoStateAtLastIO != m_Driver->GetUndoManager().GetState()); } void UserInterfaceLogic ::ClearUndoPoints() { m_Driver->ClearUndoPoints(); // Update the flags in the UI m_Activation->UpdateFlag(UIF_UNDO_POSSIBLE, m_Driver->IsUndoPossible()); m_Activation->UpdateFlag(UIF_REDO_POSSIBLE, m_Driver->IsRedoPossible()); // Update the saved changes flag (now true, unless equal to saved image) m_Activation->UpdateFlag(UIF_UNSAVED_CHANGES, m_UndoStateAtLastIO != m_Driver->GetUndoManager().GetState()); } void UserInterfaceLogic ::OnUnsavedChangesStateChange(UIStateFlags flag, bool value) { UpdateMainLabel(); } void UserInterfaceLogic ::OnMeshAvailabilityStateChange(UIStateFlags flag, bool value) { // UIF_MESH_SAVEABLE is available either in IRIS or SNAP modes m_Activation->UpdateFlag(UIF_MESH_SAVEABLE, m_Activation->GetFlag(UIF_IRIS_WITH_BASEIMG_LOADED) | m_Activation->GetFlag(UIF_SNAP_SNAKE_INITIALIZED)); } void UserInterfaceLogic ::OnHiddenFeaturesToggleAction() { if(m_AppearanceSettings->GetFlagEnableHiddenFeaturesByDefault()) { m_BtnAnnotationMode->show(); m_MenuAnnotationMode->show(); } else { m_BtnAnnotationMode->hide(); m_MenuAnnotationMode->hide(); } } const char *tip_link_cb(Fl_Widget *w, const char *uri) { fl_open_uri(uri); return NULL; } const char *snap_tips[] = { "There are many resources on our website,

    " "
    itksnap.org
    ," "

    " "including tutorials, mailing lists, bug reports, and much more!", "ITK-SNAP reports the cursor location in NIfTI coordinates." "

    x runs left to right; y runs posterior to anterior; and x runs inferior to superior

    ", "You can run several ITK-SNAP sessions at once. " "

    The cursor will be linked between these sessions, always pointing to the same patient coordinate.

    ", "Did you know that ITK-SNAP can display color (RGB) images?" "

    Supported formats are NIfTI and MetaImage.

    ", "Convert3D is a companion tool to ITK-SNAP. It lets you perform complex image processing operations with " "a simple command-line interface.

    Get it from

    itksnap.org/c3d

    ", "Did you know that the F3 key can be used to hide user interface panels in ITK-SNAP?", "Did you know that the F4 key toggles full-screen mode?", "Please remember to cite our 2006 Neuroimage paper when you publish results produced with ITK-SNAP!", "ITK-SNAP supports multiple image layers. Each layer has its own contrast settings, opacity, and color map." "Load layers using the 'Overlay' menu. Edit them using the Layer Inspector, under 'Tools'.", "There are many keyboard shortcuts to make your work faster. Look them up under 'Help'", "Since version 2.0, you can zoom (right mouse button) and pan (middle button) in crosshairs mode. " "There is also an automatic paning feature when you zoom into an image.", "The paintbrush mode includes an adaptive paintbrush that can be used to automate manual labeling." "

    It labels voxels of similar intensity

    ", "All changes to the segmentation can be undone and redone. But you should still save your work often!", "The little '+' icon next to each slice window is used to expand that window and hide other windows.", "You can change the appearance of the crosshairs, zoom thumbnail, ruler, and all other overlays in slice windows." "

    Select 'Display Options' under 'Tools'", NULL}; static int ntips = 15, current_tip = -1; void UserInterfaceLogic ::DisplayTips() { if(current_tip < 0) { // Today's date time_t ctm; time(&ctm); struct tm *tinfo = localtime(&ctm); current_tip = tinfo->tm_yday % ntips; } m_OutTips->value(snap_tips[current_tip]); m_OutTips->link(&tip_link_cb); current_tip++; if(snap_tips[current_tip] == NULL) current_tip = 0; } /* *$Log: UserInterfaceLogic.cxx,v $ *Revision 1.124 2011/05/04 15:25:42 pyushkevich *Fixes to build with fltk 1.3.0rc3 * *Revision 1.123 2011/05/03 20:11:12 pyushkevich *version number changed to 2.2 * *Revision 1.122 2011/04/18 17:57:08 pyushkevich *Fixed bug 3172058, a typo in 'sagittal' * *Revision 1.121 2011/04/18 17:55:20 pyushkevich *Fixed bug 3243462. We can now save the mesh in SNAP (level set) mode * *Revision 1.120 2011/04/18 17:35:30 pyushkevich *Fixed bug 3288963, problems with bubble initialization * *Revision 1.119 2011/04/18 15:06:07 pyushkevich *Added keystroke for changing colormaps * *Revision 1.118 2010/10/19 21:31:05 pyushkevich *Fixed the toolbar for collapse mode; Added menu items for the tools * *Revision 1.117 2010/10/19 19:16:30 pyushkevich *Fixed slowdowns due to progress bars in preprocessing, mesh generation * *Revision 1.116 2010/10/13 16:59:25 pyushkevich *Fixing warnings * *Revision 1.115 2010/10/13 16:52:04 pyushkevich *Improved the precision warning system * *Revision 1.114 2010/10/12 17:57:11 pyushkevich *Collapsed windows auto-shrink; changed zoom to fit behavior * *Revision 1.113 2010/10/12 16:02:05 pyushkevich *Improved handling of collapsed windows * *Revision 1.112 2010/07/01 21:40:24 pyushkevich *Increased max number of labels to 65535 * *Revision 1.111 2010/06/28 18:45:08 pyushkevich *Patch from Michael Hanke to allow ITK 3.18 builds * *Revision 1.110 2010/05/31 19:52:37 pyushkevich *Added volumes and statistics window * *Revision 1.109 2010/05/27 11:16:22 pyushkevich *Further improved polygon drawing interface * *Revision 1.108 2010/05/27 07:29:36 pyushkevich *New popup menu for polygon drawing, other improvements to polygon tool * *Revision 1.107 2010/04/16 05:14:38 pyushkevich *FIX: touched up previous checkin * *Revision 1.106 2010/04/16 04:02:35 pyushkevich *ENH: implemented drag and drop, OSX events, new command-line interface * *Revision 1.105 2010/04/14 10:06:23 pyushkevich *Added option to launch external SNAP * *Revision 1.104 2010/03/23 21:23:13 pyushkevich *Added display halving capability, *command line switches --zoom, --help, --compact * *Revision 1.103 2009/11/16 20:29:28 garyhuizhang *BUGFIX: a fix for messed up display on mac when switching between different panel zoom mode *ENH: added color map menu item * *Revision 1.102 2009/11/13 13:45:26 pyushkevich *undo bad checkin * *Revision 1.100 2009/11/13 00:59:47 pyushkevich *ENH: improved shortcuts * *Revision 1.99 2009/10/30 16:48:24 garyhuizhang *ENH: allow interacting with the main window when the reorient image dialog is open * *Revision 1.98 2009/10/30 13:49:48 pyushkevich *FIX: improved behavior of synchronized pan. it now broadcasts viewport center rel. to cursor posn. *FIX: improved IPC. only 'new' messages are now acted on. * *Revision 1.97 2009/10/28 08:05:36 pyushkevich *FIX: Multisession pan causing continuous screen updates * *Revision 1.96 2009/10/26 16:00:56 pyushkevich *ENH: improved/fixed cursor movement in all modes. added menu items for F3/F4 * *Revision 1.95 2009/10/26 08:37:31 pyushkevich *FIX(2872319): Ctrl-Z no longer steals focus from slice window * *Revision 1.94 2009/10/26 08:17:58 pyushkevich *FIX(2821319): Made intensity curve and colormap work in snake mode * *Revision 1.93 2009/10/26 07:34:10 pyushkevich *ENH: substantially reduced memory footprint when loading float NIFTI images * *Revision 1.92 2009/10/25 13:17:05 pyushkevich *FIX: bugs in SF.net, crash on mesh update in large images, bad vols/stats output * *Revision 1.91 2009/10/17 20:39:50 pyushkevich *ENH: added tip of the day * *Revision 1.90 2009/09/21 21:55:19 pyushkevich *FIX:various snow leopard warnings' * *Revision 1.89 2009/09/14 19:04:52 garyhuizhang *ENH: layer inspector support for curor position input * *Revision 1.88 2009/09/14 04:41:38 garyhuizhang *ENH: layer inspector with partial image info support * *Revision 1.87 2009/09/13 22:11:51 garyhuizhang *ENH: add layer inspector support for multiple layers * *Revision 1.86 2009/09/12 23:27:01 garyhuizhang *ENH: layer inspector now with color map support * *Revision 1.85 2009/09/10 22:42:32 garyhuizhang *ENH: handle overlays in layer inspector * *Revision 1.84 2009/09/10 21:25:24 garyhuizhang *ENH: Layer inspector now supports contrast adjustment of main image * *Revision 1.83 2009/08/29 23:17:02 garyhuizhang *BUGFIX: fix a memory leak * *Revision 1.82 2009/08/28 20:35:15 garyhuizhang *ENH: remove OverlayUI (replaced by LayerInspectorUI) * *Revision 1.81 2009/08/28 16:57:05 pyushkevich *FIX: Fixed scrollbar crash * *Revision 1.80 2009/08/28 16:33:03 garyhuizhang *ENH: rename LayerEditor as LayerInspector * *Revision 1.79 2009/08/28 16:05:43 pyushkevich *Enabled toggling of UI components with 'F3' key and fullscreen mode with 'F4' key * *Revision 1.78 2009/08/26 21:49:55 pyushkevich *Improvements to the color map widget * *Revision 1.77 2009/08/26 01:10:20 garyhuizhang *ENH: merge grey and RGB overlays into one wrapper list and modify the associated GUI codes * *Revision 1.75 2009/08/24 19:24:33 garyhuizhang *BUGFIX: menu item activation rules changed *1) m_MenuImageInfo and m_MenuSave imply UIF_BASEIMG_LOADED *2) UIF_SNAP_PREPROCESSING_DONE no longer implies UIF_SNAP_ACTIVE * *Revision 1.74 2009/07/22 21:06:24 pyushkevich *Changed the IO system and wizards, removed templating * *Revision 1.73 2009/07/15 21:35:44 pyushkevich *Fixed bug with export image slice saving wrong anatomical direction * *Revision 1.72 2009/07/13 17:26:24 pyushkevich *Fixed crash on Win32 * *Revision 1.71 2009/07/01 22:21:50 garyhuizhang *BUGFIX: main window title label not being updated! * *Revision 1.70 2009/07/01 20:15:27 garyhuizhang *BUGFIX: native file chooser bugs * *Revision 1.69 2009/06/24 00:13:55 garyhuizhang *ENH: improved auto update management * *Revision 1.68 2009/06/18 18:11:24 garyhuizhang *ENH: multisession pan ui support *BUGFIX: single session pan working again * *Revision 1.67 2009/06/16 05:57:00 garyhuizhang *ENH: initial UI for layer manager, which replacing the old RGB overlay UI * *Revision 1.66 2009/06/16 04:55:45 garyhuizhang *ENH: per overlay opacity adjustment * *Revision 1.65 2009/06/15 23:41:09 garyhuizhang *BUGFIX: make sure the 3D view does not get reset when loading/unloading overlay and segmentation images. * *Revision 1.64 2009/06/15 01:54:10 garyhuizhang *BUGFIX: linked zoom misbehaving with overlay * *Revision 1.63 2009/06/14 20:43:17 garyhuizhang *ENH: multiple RGB overlay support * *Revision 1.62 2009/06/14 06:13:20 garyhuizhang *ENH: menu item association for grey overlay unload * *Revision 1.61 2009/06/13 05:02:00 garyhuizhang *ENH: improved implementation of recent file lists that combines both grey and RGB main images * *Revision 1.60 2009/06/13 03:29:40 garyhuizhang *ENH: checking for available software update * *Revision 1.59 2009/06/12 05:11:08 garyhuizhang *ENH: reorganized user interface * *Revision 1.58 2009/06/10 02:52:46 garyhuizhang *ENH: multiple grey overlay images support * *Revision 1.57 2009/06/09 05:46:38 garyhuizhang *ENH: main image support & grey overlay support * *Revision 1.56 2009/05/25 17:09:44 garyhuizhang *ENH: switch from Fl_File_Chooser to Fl_Native_File_Chooser which requires the fltk to be patched with Fl_Native_File_Chooser add-on. * *Revision 1.55 2009/05/25 16:35:49 garyhuizhang *bug fix: history not activated when loading segmentation files * *Revision 1.54 2009/05/04 20:15:57 garyhuizhang *multisession panning support added * *Revision 1.53 2009/04/18 05:28:09 garyhuizhang *added key stroke to reset the view center to image center * *Revision 1.52 2009/02/18 01:19:13 garyhuizhang *ENH: added keyboard shortcut 'c' for toggling the visibility of crosshairs * *Revision 1.51 2009/02/18 01:14:52 garyhuizhang *ENH: support adjusting the default behavior of linked zoom and multi-session zoom * *Revision 1.50 2009/02/10 00:10:12 garyhuizhang *ENH: Support two drawing options in the Annotation mode: 1) each line shown only on the slice level it is drawn; 2) each line is shown on all slice levels * *Revision 1.49 2009/02/09 17:07:47 garyhuizhang *FIX: code refactoring -- command line and GUI loading of segmentation now shares the same code. this enables the validity checking of segmentation image on command line originally implemented for GUI. * *Revision 1.48 2009/02/06 18:56:04 garyhuizhang *ENH: add image type specific information to image load/save wizard * *Revision 1.47 2009/02/05 23:03:40 garyhuizhang *ENH: support for saving the hidden feature flag * *Revision 1.46 2009/02/05 16:21:14 pyushkevich *ENH: added hidden features button * *Revision 1.45 2009/02/05 14:58:29 pyushkevich *FIX: save slice layout appearance settings to registry; ENH: added linear interpolation option for grey images * *Revision 1.44 2009/02/03 19:51:50 pyushkevich *fixes * *Revision 1.43 2009/02/03 19:12:35 pyushkevich *ENH: added support for checking version via internet * *Revision 1.42 2009/01/30 23:44:15 garyhuizhang *FIX: clean up the remaining minor compiler warnings * *Revision 1.41 2009/01/30 23:08:21 garyhuizhang *ENH: better implementation of the keyboard shortcuts that do not require the SHIFT key * *Revision 1.40 2009/01/30 22:41:27 garyhuizhang *ENH: new keyboard shortcuts for adjusting paintbrush size, changing active/drawover labels * *Revision 1.39 2009/01/23 21:48:59 pyushkevich *ENH: Added hidden annotation mode (very bad code) * *Revision 1.38 2009/01/23 20:09:38 pyushkevich *FIX: 3D rendering now takes place in Nifti(RAS) world coordinates, rather than the VTK (x spacing + origin) coordinates. As part of this, itk::OrientedImage is now used for 3D images in SNAP. Still have to fix cut plane code in Window3D * *Revision 1.37 2009/01/23 05:04:33 garyhuizhang *Bug fix: floating point values are now correctly displayed in image *contrast dialog and image info dialog * *Revision 1.36 2009/01/22 23:14:10 garyhuizhang *1) Add an option under Options -> Display Options -> General for toggling on/off the warning when loading floating point images. *2) The warning message is modified to include the instruction for turning it off. * *Revision 1.35 2009/01/17 10:40:28 pyushkevich *Added synchronization to 3D window viewpoint * *Revision 1.34 2009/01/16 21:31:41 pyushkevich *Fixed issues with loading and creating new segmentation images; namely undo/redo bugs and update mesh button not being available. * *Revision 1.33 2008/12/02 21:43:24 pyushkevich *Reorganization of the watershed code * *Revision 1.32 2008/12/02 05:14:19 pyushkevich *New feature: watershed-based adaptive paint brush. Based on the similar tool in ITK-Grey (which was derived from ITK-SNAP). * *Revision 1.31 2008/11/20 04:24:00 pyushkevich *Bugfixes * *Revision 1.30 2008/11/20 02:41:03 pyushkevich *Now when non-native format is loaded, the intensity is mapped to original values * *Revision 1.29 2008/11/17 19:47:41 pyushkevich *Get linux to compile * *Revision 1.28 2008/11/17 19:38:23 pyushkevich *Added tools dialog to label editor window * *Revision 1.26 2008/11/01 11:32:00 pyushkevich *Compatibility with ITK 3.8 support for reading oriented images *Command line loading of RGB images *Improved load-image commands in UserInterfaceLogic * *Revision 1.25 2008/04/16 13:48:01 pyushkevich *Major bug fix: cursor was not working in automatic segmentation mode * *Revision 1.24 2008/04/01 14:25:27 pyushkevich *Minor bug fix: alt-i to do automatic intensity adjustment * *Revision 1.23 2008/03/25 19:31:31 pyushkevich *Bug fixes for release 1.6.0 * *Revision 1.22 2008/02/27 04:34:46 garyhuizhang *1) rename OnMenuSaveScreenshots to OnMenuSaveScreenshotSeries *2) support menu access to both save single screenshot and screenshot series * *Revision 1.21 2008/02/26 21:28:29 garyhuizhang *improve the behavior of savescreenshot series *1) restore the cursor location before the call *2) handle the situation when user did not provide a directory for screenshots * *Revision 1.20 2008/02/23 23:41:12 garyhuizhang *add support for saving screenshots of the whole image volume * *Revision 1.19 2008/02/16 22:38:34 pyushkevich *Bug fix with bubbles * *Revision 1.18 2008/02/15 19:55:31 pyushkevich *fixed 100% cpu usage bug (from idle function) * *Revision 1.17 2008/02/15 18:34:16 pyushkevich *scrolling bug fix; packaging includes date in filename * *Revision 1.16 2008/02/11 17:49:20 pyushkevich *Touchups * *Revision 1.15 2008/02/11 12:59:40 pyushkevich *bug fix with shared cursor on Linux * *Revision 1.14 2008/02/10 23:55:22 pyushkevich *Added "Auto" button to the intensity curve window; Added prompt before quitting on unsaved data; Fixed issues with undo on segmentation image load; Added synchronization between SNAP sessions. * *Revision 1.13 2008/01/08 20:34:52 pyushkevich *Implement toggle for opacity slider * *Revision 1.12 2007/12/30 04:05:18 pyushkevich *GPL License * *Revision 1.11 2007/12/25 15:46:23 pyushkevich *Added undo/redo functionality to itk-snap * *Revision 1.10 2007/09/18 18:42:40 pyushkevich *Added tablet drawing to polygon mode * *Revision 1.9 2007/09/15 15:59:20 pyushkevich *Improved the paintbrush mode, allowed more variety of brush sizes * *Revision 1.8 2007/09/04 16:56:13 pyushkevich *tablet support 1 * *Revision 1.7 2007/06/07 00:49:16 pyushkevich *Debugged RGB changes * *Revision 1.6 2007/06/06 22:27:22 garyhuizhang *Added support for RGB images in SNAP * *Revision 1.5 2007/05/10 20:19:50 pyushkevich *Added VTK mesh export code and GUI * *Revision 1.4 2006/12/06 14:36:18 pyushkevich *Fixes for VC6 * *Revision 1.3 2006/12/06 13:27:46 pyushkevich *Followup checking for 1.4.1 * *Revision 1.2 2006/12/06 01:26:07 pyushkevich *Preparing for 1.4.1. Seems to be stable in Windows but some bugs might be still there * *Revision 1.1 2006/12/02 04:22:23 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.59 2006/02/02 01:23:10 pauly *BUG: Fixed SNAP bugs in the last checkin * *Revision 1.58 2006/02/01 21:41:42 pauly *ENH: SNAP: bubble radius changed to floating point * *Revision 1.57 2006/02/01 20:21:26 pauly *ENH: An improvement to the main SNAP UI structure: one set of GL windows is used to support SNAP and IRIS modes * *Revision 1.56 2006/01/06 18:45:35 pauly *BUG: Progress bar in SNAP not disappearing at times * *Revision 1.55 2006/01/05 23:19:45 pauly *BUG: SNAP label file was not being written correctly * *Revision 1.54 2006/01/05 18:25:05 pauly *BUG: Fixed cursor bug in lInux * *Revision 1.53 2006/01/05 00:02:41 pauly *ENH: Now SNAP keeps cursor position whether you enter or exit auto mode * *Revision 1.52 2006/01/04 23:25:42 pauly *ENH: SNAP will keep crosshairs position when entering automatic segmentation * *Revision 1.51 2005/12/21 17:07:22 pauly *STYLE: New SNAP logo, about window * *Revision 1.50 2005/12/19 03:43:12 pauly *ENH: SNAP enhancements and bug fixes for 1.4 release * *Revision 1.49 2005/12/16 23:46:51 pauly *BUG: Silly mistake in PNG screenshot saver * *Revision 1.48 2005/12/12 13:11:39 pauly *BUG: Filename problem with talking snapshots in SNAP fixed * *Revision 1.47 2005/12/12 00:27:44 pauly *ENH: Preparing SNAP for 1.4 release. Snapshot functionality * *Revision 1.46 2005/12/08 21:15:58 pauly *COMP: SNAP not linking because whoever did previous fix did not check in SNAPCommon.cxx * *Revision 1.45 2005/12/08 18:20:46 hjohnson *COMP: Removed compiler warnings from SGI/linux/MacOSX compilers. * *Revision 1.44 2005/11/23 14:32:15 ibanez *BUG: 2404. Patch provided by Paul Yushkevish. * *Revision 1.43 2005/11/10 23:02:14 pauly *ENH: Added support for VoxBo CUB files to ITK-SNAP, as well as some cosmetic touches * *Revision 1.42 2005/11/03 20:59:15 pauly *COMP: Fixed compiler errors on newer versions of VTK * *Revision 1.41 2005/11/03 18:45:29 pauly *ENH: Enabled SNAP to read DICOM Series * *Revision 1.40 2005/10/29 14:00:15 pauly *ENH: SNAP enhacements like color maps and progress bar for 3D rendering * *Revision 1.39 2005/08/10 19:57:15 pauly *BUG: Labels not always appearing when loading an image in SNAP * *Revision 1.38 2005/08/10 03:24:20 pauly *BUG: Corrected problems with 3D window, label IO from association files * *Revision 1.37 2005/04/23 13:58:19 pauly *COMP: Fixing compile errors in the last SNAP checkin * *Revision 1.36 2005/04/23 12:56:59 lorensen *BUG: bad include. * *Revision 1.35 2005/04/21 18:52:38 pauly *ENH: Furhter improvements to SNAP label editor * *Revision 1.34 2005/04/21 14:46:30 pauly *ENH: Improved management and editing of color labels in SNAP * *Revision 1.33 2005/04/15 19:04:19 pauly *ENH: Improved the Intensity Contrast features in SNAP * *Revision 1.32 2005/04/14 16:35:10 pauly *ENH: Added Image Info window to SNAP * *Revision 1.31 2005/03/08 03:12:51 pauly *BUG: Minor bugfixes in SNAP, mostly to the user interface * *Revision 1.30 2004/09/21 15:50:40 jjomier *FIX: vector_multiply_mixed requires template parameters otherwise MSVC cannot deduce them * *Revision 1.29 2004/09/14 14:11:10 pauly *ENH: Added an activation manager to main UI class, improved snake code, various UI fixes and additions * *Revision 1.28 2004/09/08 12:09:45 pauly *ENH: Adapting SNAP to work with stop-n-go function in finite diff. framewk * *Revision 1.27 2004/08/26 19:43:27 pauly *ENH: Moved the Borland code into Common folder * *Revision 1.26 2004/08/26 18:29:19 pauly *ENH: New user interface for configuring the UI options * *Revision 1.25 2004/08/03 23:26:32 ibanez *ENH: Modification for building in multple platforms. By Julien Jomier. * *Revision 1.24 2004/07/29 14:00:36 pauly *ENH: A new interface for changing the appearance of SNAP * *Revision 1.23 2004/07/24 19:00:06 pauly *ENH: Thumbnail UI for slice zooming * *Revision 1.22 2004/07/22 19:22:49 pauly *ENH: Large image support for SNAP. This includes being able to use more screen real estate to display a slice, a fix to the bug with manual segmentation of images larger than the window size, and a thumbnail used when zooming into the image. * *Revision 1.21 2004/07/21 18:17:45 pauly *ENH: Enhancements to the way that the slices are displayed * *Revision 1.20 2004/03/19 00:54:48 pauly *ENH: Added the ability to externally load the advection image * *Revision 1.19 2004/01/24 18:21:00 king *ERR: Merged warning fixes from ITK 1.6 branch. * *Revision 1.18.2.1 2004/01/24 18:16:50 king *ERR: Fixed warnings. * *Revision 1.18 2003/12/12 19:34:01 pauly *FIX: Trying to get everything to compile again after API changes * *Revision 1.17 2003/12/10 23:20:15 hjohnson *UPD: Code changes to allow compilation under linux. * *Revision 1.16 2003/12/10 02:21:14 lorensen *ENH: Spacing is now a FixedArray. * *Revision 1.15 2003/12/07 21:19:32 pauly *ENH: SNAP can now resample the segmentation ROI, facilitating *multires segmentation and segmentation of anisotropic images * *Revision 1.14 2003/12/07 19:48:41 pauly *ENH: Resampling, multiresolution * *Revision 1.13 2003/11/29 17:06:48 pauly *ENH: Minor Help issues * *Revision 1.12 2003/11/29 14:02:42 pauly *FIX: History list and file associations faili with spaces in filenames * *Revision 1.11 2003/11/10 00:27:26 pauly *FIX: Bug with linear interpolation in PDE solver *ENH: Help viewer and tutorial * *Revision 1.10 2003/10/09 22:45:14 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.9 2003/10/06 12:30:00 pauly *ENH: Added history lists, remembering of settings, new snake parameter preview * *Revision 1.8 2003/10/02 20:57:46 pauly *FIX: Made sure that the previous check-in compiles on Linux * *Revision 1.7 2003/10/02 18:43:47 pauly *FIX: Fixed crashes with using vtkContourFilter * *Revision 1.6 2003/10/02 14:55:52 pauly *ENH: Development during the September code freeze * *Revision 1.5 2003/09/16 16:10:17 pauly *FIX: No more Internal compiler errors on VC *FIX: Intensity curve no longer crashes *ENH: Histogram display on intensity curve window * *Revision 1.4 2003/09/15 19:06:58 pauly *FIX: Trying to get last changes to compile * *Revision 1.3 2003/09/15 17:32:19 pauly *ENH: Removed ImageWrapperImplementation classes * *Revision 1.2 2003/09/13 15:18:01 pauly *FIX: Got SNAP to work properly with different image orientations * *Revision 1.1 2003/09/11 13:51:01 pauly *FIX: Enabled loading of images with different orientations *ENH: Implemented image save and load operations * *Revision 1.5 2003/08/28 22:58:30 pauly *FIX: Erratic scrollbar behavior * *Revision 1.4 2003/08/28 14:37:09 pauly *FIX: Clean 'unused parameter' and 'static keyword' warnings in gcc. *FIX: Label editor repaired * *Revision 1.3 2003/08/27 14:03:22 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:46 pauly *FIX: A large number of bugs has been fixed for 1.4 release * *Revision 1.1 2003/07/12 04:46:50 pauly *Initial checkin of the SNAP application into the InsightApplications tree. * *Revision 1.1 2003/07/11 23:33:57 pauly **** empty log message *** * *Revision 1.20 2003/07/10 14:30:26 pauly *Integrated ITK into SNAP level set segmentation * *Revision 1.19 2003/07/01 16:53:59 pauly **** empty log message *** * *Revision 1.18 2003/06/23 23:59:32 pauly *Command line argument parsing * *Revision 1.17 2003/06/14 22:42:06 pauly *Several changes. Started working on implementing the level set function *in ITK. * *Revision 1.16 2003/06/08 23:27:56 pauly *Changed variable names using combination of ctags, egrep, and perl. * *Revision 1.15 2003/06/08 16:11:42 pauly *User interface changes *Automatic mesh updating in SNAP mode * *Revision 1.14 2003/05/22 17:36:19 pauly *Edge preprocessing settings * *Revision 1.13 2003/05/17 21:39:30 pauly *Auto-update for in/out preprocessing * *Revision 1.12 2003/05/14 18:33:58 pauly *SNAP Component is working. Double thresholds have been enabled. Many other changes. * *Revision 1.11 2003/05/12 02:51:08 pauly *Got code to compile on UNIX * *Revision 1.10 2003/05/08 21:59:05 pauly *SNAP is almost working * *Revision 1.9 2003/05/07 19:14:46 pauly *More progress on getting old segmentation working in the new SNAP. Almost there, region of interest and bubbles are working. * *Revision 1.8 2003/05/05 12:30:18 pauly **** empty log message *** * *Revision 1.7 2003/04/29 14:01:42 pauly *Charlotte Trip * *Revision 1.6 2003/04/25 02:58:29 pauly *New window2d model with InteractionModes * *Revision 1.5 2003/04/23 20:36:23 pauly **** empty log message *** * *Revision 1.4 2003/04/23 06:05:18 pauly **** empty log message *** * *Revision 1.3 2003/04/18 17:32:18 pauly **** empty log message *** * *Revision 1.2 2003/04/18 00:25:37 pauly **** empty log message *** * *Revision 1.1 2003/04/16 05:04:17 pauly *Incorporated intensity modification into the snap pipeline *New IRISApplication *Random goodies * *Revision 1.2 2003/04/01 18:20:56 pauly **** empty log message *** * *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.63 2002/05/08 17:32:57 moon *I made some changes Guido wanted in the GUI, including removing *Turello/Sapiro/Schlegel options (I only hid them, the code is all still there), changing a bunch of the ranges, etc. of the snake parameters. * *Revision 1.62 2002/04/28 17:29:43 scheuerm *Added some documentation * *Revision 1.61 2002/04/27 18:30:03 moon *Finished commenting * *Revision 1.60 2002/04/27 17:48:33 bobkov *Added comments * *Revision 1.59 2002/04/27 00:08:27 talbert *Final commenting run through . . . no functional changes. * *Revision 1.58 2002/04/26 17:37:12 moon *Fixed callback on save preproc dialog cancel button. *Fixed bubble browser output. Position was zero-based, which didn't match the 2D *window slice numbers (1 based), so I changed the bubble positions to be cursor *position +1. *Disallowed starting snake window if current label in not visible. *Put in Apply+ button in threshold dialog, which changes seg overlay to be an *overlay of the positive voxels in the preproc data (a zero-level visualization). *Added more m_OutMessage and m_OutMessage messages. * *Revision 1.57 2002/04/25 14:13:13 moon *Enabled render options in snake window. *Changed snake params dialog slider (messed one up last time) *Hide r_ params in snake params dialog with in/out snake (they don't apply) *Calling segment3D with clear color is not allowed (error dialog) * *Revision 1.56 2002/04/24 19:50:22 moon *Pulled LoadGreyFileCallback out of GUI into UserInterfaceLogic, made modifications due *to change in ROI semantics. Before, the ROI was from ul to lr-1, which is a bad *decision. I changed everything to work with a ROI that is inclusive, meaning *that all voxels from ul through lr inclusive are part of the ROI. This involved *a lot of small changes to a lot of files. * *Revision 1.55 2002/04/24 17:10:33 bobkov *Added some changes to OnSnakeStartAction(), *OnAcceptInitializationAction() and *OnRestartInitializationAction() *so that ClearScreen() method is called on *m_IRISWindow3D and m_SNAPWindow3D to clear the glLists and *the previous segmentation is not shown in the 3D window * *Revision 1.54 2002/04/24 14:54:32 moon *Changed the ranges of some of the snake parameters after talking with Guido. *Put in a line to update mesh when the update continuously checkbox is checked. * *Revision 1.53 2002/04/24 14:13:26 moon *Implemented separate brightness/contrast settings for grey/preproc data * *Revision 1.52 2002/04/24 01:05:00 talbert *Changed IRIS2000 labels to SnAP. * *Revision 1.51 2002/04/23 21:56:50 moon *small bug fix with the snake params and the global state. wasn't getting *the sapiro/turello/etc. option from the dialog to put into the global state. * *Revision 1.50 2002/04/23 03:19:40 talbert *Made some changes so that the load preproc menu option is unuseable once *the snake starts. * *Revision 1.49 2002/04/22 21:54:39 moon *Closed dialogs when accept/restart initialization is pressed, or snake type is *switched. * *Revision 1.48 2002/04/22 21:24:20 talbert *Small changes so that error messages for preprocessing loading appeared in *the correct status bar. * *Revision 1.47 2002/04/20 21:56:47 talbert *Made it impossible to save preprocessed data when it doesn't make sense *(if no preprocessing has been done since the last preproc load or since *the snake win opened). Moved some checks for data type validity out of *the callbacks and into the Vox and SnakeVoxData classes where they belong. * *Revision 1.46 2002/04/19 23:03:59 moon *Changed more stuff to get the snake params state synched with the global state. *Changed the range of ground in snake params dialog. *Removed the use_del_g stuff, since it's really not necessary, I found out. * *Revision 1.45 2002/04/19 20:34:58 moon *Made preproc dialogs check global state and only preproc if parameters have changed. *So no if you hit apply, then ok, it doesn't re process on the ok. * *Revision 1.44 2002/04/18 21:36:51 scheuerm *Added documentation for my recent changes. *Fixed inverted display of edge preprocessing. * *Revision 1.43 2002/04/18 21:14:03 moon *I had changed the Cancel buttons to be Close on the Filter dialogs, and I changed *the names of the callbacks in GUI, but not in UserInterfaceLogic. So I just hooked them *up so the dialogs get closed. * *Revision 1.42 2002/04/18 21:04:51 moon *Changed the IRIS window ROI stuff. Now the ROI is always valid if an image is *loaded, but there is a toggle to show it or not. This will work better with *Konstantin's addition of being able to drag the roi box. * *I also changed a bunch of areas where I was calling InitializeSlice for the 2D windows, *when this is not at all what I should have done. Now those spots call *MakeSegTextureCurrent, or MakeGreyTextureCurrent. This means that the view is not *reset every time the snake steps, the preproc/orig radio buttons are changed, etc. * *Revision 1.41 2002/04/16 18:54:32 moon *minor bug with not stopping snake when play is pushed, and then other *buttons are pushed. Also added a function that can be called when the user *clicks the "X" on a window, but it's not what we want, I don't think. The *problem is if the user clicks the "X" on the snake window when a "non modal" *dialog is up, all the windows close, but the program doesn't quit. I think *it's a bug in FLTK, but I can't figure out how to solve it. * *Revision 1.40 2002/04/16 14:44:49 moon *Changed bubbles to be in world coordinates instead of image coordinates. * *Revision 1.39 2002/04/16 13:07:56 moon *Added tooltips to some widgets, made minor changes to enabling/disabling of *widgets, clearing 3D window when initialization is restarted in snake window, *changed kappa in edge preproc dialog to be [0..1] range instead of [0..3] * *Revision 1.38 2002/04/14 22:02:54 scheuerm *Changed loading dialog for preprocessed image data. Code restructuring *along the way: Most important is addition of *SnakeVoxDataClass::ReadRawPreprocData() * *Revision 1.37 2002/04/13 17:43:48 moon *Added some initslice calls to Win2Ds, so the redraw problem comming back *from snake to iris window (where the whole 2D window is yellow) would go away. * *Revision 1.36 2002/04/13 16:20:08 moon *Just put in a bunch of debug printouts. They'll have to come out eventually. * *Revision 1.35 2002/04/10 21:20:16 moon *just added debug comments. * *Revision 1.34 2002/04/10 20:19:40 moon *got play and stop vcr buttons to work. *put in lots of comments. * *Revision 1.33 2002/04/10 14:45:03 scheuerm *Added documentation to the methods I worked on. * *Revision 1.32 2002/04/09 21:56:42 bobkov * *modified Step button callback to display snake in 3d window * *Revision 1.31 2002/04/09 19:32:22 talbert *Added comments to the save and load preprocessed functions. Checked that *only float files entered as preprocessed. Made some small cosmetic *changes: loading a file switches to preproc view and sets snake mode. * *Revision 1.30 2002/04/09 18:59:33 moon *Put in dialog to change snake parameters. Also implemented Rewind button, which *now restarts the snake. It seems for now that changing snake parameters restarts *the snake. I don't know if this is the way it has to be, or I just did something *wrong in snakewrapper. I'll have to check with Sean. * *Revision 1.29 2002/04/09 17:59:37 talbert *Made changes to LoadPreprocessedCallback which allowed edge detection *preproc data to be loaded correctly. * *Revision 1.28 2002/04/09 03:48:51 talbert *Changed some functionality in the LoadPreprocessedCallback() so that it *would work with floating point data being loaded. Most of the stuff *is uncommented, hackish, and limited in its testing beyond verification *that it displays on the screen with the right values. These changes *will have to be cleaned up. * *Revision 1.27 2002/04/08 13:32:35 talbert *Added a preprocessed save dialog box as well as a save preprocessed menu *option in the snake window. Added the code necessary to implement the *GUI side of saving. * *Revision 1.26 2002/04/07 02:22:49 scheuerm *Improved handling of OK and Apply buttons in preprocessing dialogs. * *Revision 1.23 2002/04/05 03:42:29 scheuerm *Thresholding sort of works. Steepness needs to be made configurable. * *Revision 1.21 2002/04/04 15:30:08 moon *Put in code to get StepSize choice box filled with values and working. *AcceptSegment button callback puts snake seg data into full_data (IRIS) *Fixed a couple more UI cosmetic things. * *Revision 1.20 2002/04/03 22:12:07 moon *Added color chip, image probe, seg probe to snake window, although seg probe *maybe shouldn't be there. added update continuously checkbox to 3Dwindow. *changes accept/restart to be on top of each other, and one is shown at a time, *which I think is more intuitive. *changed snake iteration field to be text output. added callback for step size *choice. * *Revision 1.19 2002/04/02 23:51:17 scheuerm *Gradient magnitude preprocessing is implemented. Stupid, stupid VTK. *Adjusted the range and resolution of the sigma slider. Apply button *still doesn't do anything but I think we don't need it. * *Revision 1.18 2002/04/02 15:12:43 moon *Put code in the step vcr button. Now the snake can be "stepped" * *Revision 1.17 2002/04/01 22:29:54 moon *Modified OnAcceptInitializationAction, added functionality to *OnRestartInitializationAction * *Revision 1.16 2002/03/29 20:17:25 scheuerm *Implemented remapping of preprocessed data to (-1,1). The mapping can *be changed by altering the parameters to RemapPreprocData(...) in *LoadPreprocessedDataCallback() (UserInterfaceLogic.cpp). * *Revision 1.15 2002/03/29 03:33:29 scheuerm *Loaded preprocessed data is now converted to float. No remapping yet. *Stupid VTK. Added vtkImageDeepCopyFloat which copies the region of *interest out of a gray image and converts it to float as it goes. * *Revision 1.14 2002/03/27 17:59:40 moon *changed a couple things. nothing big. a callback in .fl was bool return type *which didn't compile in windows. this is the version I think will work for a *demo for Kye * *Revision 1.13 2002/03/27 17:05:04 talbert *Made changes necessary to compile in Windows 2000 using Microsoft Visual C++ 6.0. *GUI.cpp - needed to return something from function LoadPreprocessedCallback() *UserInterfaceLogic.cpp - moved definitions of for loop control variables outside of loop for *scope reasons. *SnakeWrapper.cpp - changed outdt1->data to *outdt1 and outdt2->data to *outdt because *these variables are float, not structures. Also changed a line using snprintf to *sprintf because snprintf is a GNU extension. *Added the files snake32.dsp and snake32.dsw for compiling in Windows 2000. * *Revision 1.12 2002/03/27 15:04:26 moon *Changed a bunch of stuff so that the state was basically reset when the snake *window is hidden (accept or cancel segmentation), and then opened again. for *example, the bubbles browser needed to be emptied, the active/inactive groups *needed to be set to the defaults again, the radio button for the preproc *data needed to be turned off (so original data is shown), etc. * *Added code to the acceptinitialization button that converts bubble information *into binary snake initialization image, and previous segmentation info of the *same label should also be preserved (i.e. segmentation info that comes from *IRIS can be used for the initialization as well as bubbles). The snake is *initialized, and the controls are activated. * *Still need to code the resetinitialization so that the bubble stuff, etc. is re- *enabled. * *None of the vcr buttons do anything, still. * *Revision 1.11 2002/03/26 19:22:14 moon *I don't think I really changed anything, but I had updated, and it tried to "merge" versions with and without ^M endline characters * *Revision 1.10 2002/03/26 18:16:32 scheuerm *Added loading and display of preprocessed data: *- added vtkImageDeepCopy function *- added flags indicating which dataset to display in GlobalState *- added flag indicating whether to load gray or preprocessed data * in the GUI class * *Revision 1.9 2002/03/25 02:15:57 scheuerm *Added loading of preprocessed data. It isn't being converted *to floats yet. It's not possible to actually display the data *right now. * *Revision 1.8 2002/03/24 19:27:46 talbert *Added callback the preprocess button to show dialog boxes for filtering. Added callbacks for buttons in filtering dialog boxes. Modified the AddBubbles callback so that the newest bubble is selected in the Bubble Browser. m_OutAboutCompiled and ran to verify that new bubbles are selected and that the dialogs appear over the *3d window. talbert s f * *Revision 1.7 2002/03/22 16:44:16 moon *added OpenGL display of bubbles in Window2D_s::draw * *Revision 1.6 2002/03/21 15:45:46 bobkov *implemented callbacks for buttons AddBubble and RemoveBubble, implemented callbacks for Radius slider and ActiveBubble browser, created methods getBubbles and getNumberOfBubbles e * *Revision 1.5 2002/03/19 19:35:32 moon *added snakewrapper to makefile so it gets compiled. started putting in callback, *etc. for snake vcr buttons. added snake object to IrisGlobals, instantiated in Main * *Revision 1.4 2002/03/19 17:47:10 moon *added some code to disable widgets, make the radio buttons work, etc. in the snake window. fixed the quit callback from the snake window to work (crashed before) *changed the [accept/restart]bubble_button widgets to be acceptinitialization_button and added callbacks (empty). * *Revision 1.3 2002/03/08 14:06:29 moon *Added Header and Log tags to all files **/ itksnap/UserInterface/MainComponents/UserInterfaceLogic.h0000644000076500000240000014311111553075410023125 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: UserInterfaceLogic.h,v $ Language: C++ Date: $Date: 2011/04/18 17:55:20 $ Version: $Revision: 1.56 $ 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 __UserInterfaceLogic_h_ #define __UserInterfaceLogic_h_ #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif #include "SNAPCommonUI.h" #include "UserInterface.h" // Necessary forward references class HelpViewerLogic; class PreprocessingUILogic; class RestoreSettingsDialogLogic; class SnakeParametersUILogic; class ImageIOWizardLogic; class RestrictedImageIOWizardLogic; class ImageInfoCallbackInterface; class LabelEditorUILogic; class LayerInspectorUILogic; class IRISSliceWindow; class SNAPSliceWindow; class MeshIOWizardUILogic; class SliceWindowCoordinator; class SimpleFileDialogLogic; class ResizeRegionDialogLogic; class AppearanceDialogUILogic; class ReorientImageUILogic; class Window3D; template class FLTKWidgetActivationManager; //template class ImageIOWizardLogic; //class SegmentationImageIOWizardLogic; #include // ITK forward references namespace itk { template class SimpleMemberCommand; template class MemberCommand; }; /** * \class UserInterfaceLogic * \brief Logic for the main user interface. * * This class implements virtual function from * the GUI class that is generated by fluid. * The idea is to have virtual functions for * all the GUI callbacks, and then override * them in this class, allowing us to avoid * coding inside fluid, since it's annoying. * * Note that the GUI class contains all the * windows (the IRIS window, the 3D Snake window, * and all the dialogs). */ class UserInterfaceLogic : public UserInterface { public: /** Constructor, calls GUI constructor, calls init */ UserInterfaceLogic(IRISApplication *iris); /** Destructor */ virtual ~UserInterfaceLogic(); /** This method launches the user interface */ void Launch(); /** Resets the region of interest to contain the entire volume */ void OnResetROIAction(); /** Reset to the initial state */ void OnMenuResetAll(); /** * * DESCRIPTION: * callback for the snake button * this function takes care of initializing * the global variable roi_data, sets vox_data * to point to roi_data, sets up the snake window * and calls ShowSNAP() * * PRECONDITIONS: * - global_state must have a valid ROI * * POSTCONDITIONS: * - snake window is displayed, IRIS is hidden */ void OnSnakeStartAction(); /** * * DESCRIPTION: * Callback for showing the dialog box for preprocessing the grey data. * Depending on the type of snake chosen either the dialog for thresholding * or edge detection is displayed. * * PRECONDITIONS: * - main snake window is initialized and active * * POSTCONDITIONS: * - preprocessing dialog appears on screen */ void OnPreprocessAction(); /** Callback for when the preprocessing completes */ void OnPreprocessClose(); /** Callback to display the color map window */ void OnPreprocessedColorMapAction(); void OnColorMapCloseAction(); void OnColorMapSelectAction(); /** Callback for when preprocessing preview status changes */ void OnPreprocessingPreviewStatusUpdate(bool flagPreview); /** * * DESCRIPTION: * callback to set bubbles for snake initialization * - sets a bubble at the current crosshairs position with the radius * equal to the current value on the radius slider * - adds the bubble to the Active Bubbles browser * * PRECONDITIONS: * - Snake GUI is active * - Crosshairs position is within allowed boundaries * * POSTCONDITIONS: * - all previously set bubbles as well as the new bubble are displayed * in 2d windows * - Active Bubbles browser contains all previously set bubbles as * well as the new bubble and their parameters are displayed in the browser */ void OnAddBubbleAction(); /** * * DESCRIPTION: * callback to remove a bubble * - removes a highlighted bubble from Active Bubbles browser * * PRECONDITIONS: * - Snake GUI is active * - Active Bubbles browser contains at least one bubble * * POSTCONDITIONS: * - highlighted bubble has been removed from Active Bubbles browser * and is no longer displayed in 2d windows * - Active Bubbles browser contains all remaining bubbles and their * parameters are displayed in the browser * - all remaining bubbles are displayed in 2d windows * */ void OnRemoveBubbleAction(); /** * * DESCRIPTION: * callback to browse through the bubbles in the Active Bubbles browser * - highlights a line clicked in the browser window and sets the radius * slider to the value of the highlighted bubble's radius * * PRECONDITIONS: * - Snake GUI is active * - Active Bubbles browser contains at least one bubble * * POSTCONDITIONS: * - the line clicked in the browser window is highlighted * - value of m_HighlightedBubble integer variable is set to the * number of the line highlighted in the browser window * - radius slider value is reset to the value of the highlighted * bubble's radius */ void OnActiveBubblesChange(); /** * * DESCRIPTION: * callback to set the radius of a bubble highlighted in the * Active Bubbles browser * - sets the radius of the highlighted bubble to the current value of * the radius slider * - only displays the current value of the radius slider if no bubble is * highlighted or if the Active Bubbles browser is empty * * PRECONDITIONS: * - Snake GUI is active * * POSTCONDITIONS: * - current value of the radius slider is displayed * - radius of the highlighted bubble is set to the current value of * the radius slider * - visualisation of the highlighted bubble in 2d windows changes * its radius correspondingly * - all remaining bubbles are left unchanged */ void OnBubbleRadiusChange(); /** * Opens the snake params dialog, allowing the user to modify snake params */ void OnSnakeParametersAction(); /** * * DESCRIPTION: * callback for accepting a snake segmentation * * PRECONDITIONS: * - roi_data is valid * - roi_data.snakeImageData is initialized * * POSTCONDITIONS: * - roi_data.snakeImageData has been merged with full_data.segImageData * - roi_data is deleted * - IRIS window is shown * */ void OnAcceptSegmentationAction(); /** * * DESCRIPTION: * callback for canceling a snake segmentation * * PRECONDITIONS: * * POSTCONDITIONS: * - snake window is hidden, IRIS is shown * - roi_data is deleted * */ void OnCancelSegmentationAction(); /** * * DESCRIPTION: * disables or enables the UpdateMesh button in snake win depending * on the state of the checkbox * * PRECONDITIONS: * * POSTCONDITIONS: * */ void OnContinuousViewUpdateChange(); /** * * DESCRIPTION: * Switches to InOut snake mode * * PRECONDITIONS: * - Snake UI is active * * POSTCONDITIONS: * - all current preprocessed data is invalidated */ void OnInOutSnakeSelect(); /** * * DESCRIPTION: * Switches to Edge snake mode * * PRECONDITIONS: * - Snake UI is active * * POSTCONDITIONS: * - all current preprocessed data is invalidated */ void OnEdgeSnakeSelect(); /** * * DESCRIPTION: * callback for accepting the snake initialization. The active * bubbles are voxelized. roi_data.segImageData and snakeInitImageData * are initialized and synchronized. The bubble voxels are put into * snakeInitImageData. If the grey data has been preprocessed, and * some type of initialization is present in snakeInitImageData (either * a bubble or pre-existing segmentation data from segImageData), the * snake can now be run. * * PRECONDITIONS: * - snake window is up and running * * POSTCONDITIONS: * - global_state.GetSnakeActive is true * */ void OnAcceptInitializationAction(); /** * * DESCRIPTION: * callback for restarting the snake initialization. Any current snake * state is forgotten, the snake controls are disabled, the segmentation * shown in the 2D windows reverts back to the segImageData, and the bubbles * are shown again. The initialization widgets (bubbles, preproc) are enabled * * PRECONDITIONS: * * POSTCONDITIONS: * - global_state.GetSnakeActive is false * */ void OnRestartInitializationAction(); /** * User wants to go back to the preprocessing page from the segmentation * initialization page. This callback will simply flip the page and clear * the bubbles */ void OnRestartPreprocessingAction(); /** The user has finished preprocessing the image and flips to the next * page in the wizard. The speed image must be valid for this button to be * active */ void OnAcceptPreprocessingAction(); /** * * DESCRIPTION: * callback for the snake "vcr" rewind button. Restarts the snake * at the same state as when AcceptInitialization button was pressed * * PRECONDITIONS: * * POSTCONDITIONS: * */ void OnSnakeRewindAction(); /** * * DESCRIPTION: * simply sets a flag so that the play button knows to stop * * PRECONDITIONS: * * POSTCONDITIONS: * */ void OnSnakeStopAction(); /** * * DESCRIPTION: * continuously steps the snake until m_SnakeIsRunning flag is unset * * PRECONDITIONS: * * POSTCONDITIONS: * */ void OnSnakePlayAction(); /** * * DESCRIPTION: * runs the snake for one step. * * PRECONDITIONS: * * POSTCONDITIONS: * - 2D displays are updated according to the current snake state * - if updatecontinuously checkbox is checked, 3D display is updated * */ void OnSnakeStepAction(); /** * * DESCRIPTION: * callback for choice box of how many snake iterations to run per "step" * * PRECONDITIONS: * * POSTCONDITIONS: * m_SnakeStepSize is set * */ void OnSnakeStepSizeChange(); /** * Pops up a dialog to choose a preprocessed data file * pre: LoadPreproc_win is active * post: header information is filled in in the dialog * void LoadPreprocSelectCallback(); * Fills the header information in the Load Preprocessed data dialog * pre: the filename field contains a valid filename * post: header information is filled in * void SelectPreprocFileCallback(); * Implements action performed when user presses OK * in the Load Preprocessed data dialog * PRE: nothing * POST: the preprocessed in roi_data is valid * AUTHORS: Talbert and Scheuermann * void LoadPreprocessedDataCallback(); * Implements callback for Save->Preprocessed * menu item * PRE: nothing * POST: save dialog appears * void SavePreprocessedData_Callback(); * Implements callback for Okay button in the * Save Preprocessed dialog box * PRE: nothing * POST: If a valid filename was supplied then * the preprocessed data set is saved to the * specified file * void OkaySavePreproc_button_Callback(); * callback of the radio button for selecting the grey data * for display */ void OnSNAPViewOriginalSelect(); /** * callback of the radio button for selecting the preprocessed data * for display */ void OnViewPreprocessedSelect(); /** * * DESCRIPTION: * close both windows so FL::run returns, and program exits * * PRECONDITIONS: * * POSTCONDITIONS: * - program will exit * */ void OnMenuQuit(); void OnMenuCheckForUpdate(); void OnCheckForUpdate(); void OnMenuViewToggleUI(); void OnMenuViewToggleFullscreen(); void OnMenuViewRestoreDefault(); void OnMenuLaunchNewInstance(); void OnMenuShowVolumes(); // Color label callbacks void UpdateColorLabelMenu(); void UpdateColorLabelSelection(); void OnDrawingLabelUpdate(); void OnDrawOverLabelUpdate(); void RedrawWindows(); void ResetScrollbars(); void UpdateImageProbe(); void UpdateMainLabel(); // void Activate3DAccept(bool on); void OnEditLabelsAction(); void OnLabelListUpdate(); void UpdateEditLabelWindow(); void UpdatePositionDisplay(int id); /** Set the current interaction mode */ void SetToolbarMode(ToolbarModeType mode); /** Set the current 3D interaction mode */ void SetToolbarMode3D(ToolbarMode3DType mode); /** Get the pointer to the driving application */ irisGetMacro(Driver,IRISApplication *); /** Get the pointer to the system interface */ irisGetMacro(SystemInterface,SystemInterface *); /** Get the reference to the appearance settings */ irisGetMacro(AppearanceSettings, SNAPAppearanceSettings *); /** Update slice data when loading/unloading overly */ void UpdateOverlaySlice(); /** Clear memory before loading a new main image */ void UnloadAllImages(); /** Update the common user interface after loading a new main image */ void OnMainImageUpdate(); /** Update the common user interface after loading an overlay image */ void OnOverlayImageUpdate(); /** Update the user interface after loading a new grey main image */ void OnGreyImageUpdate(); /** Update the user interface after loading a new RGB main image */ void OnRGBImageUpdate(); /** Called before unloading a grey image, saves settings associated with it */ void OnGreyImageUnload(); /** Update the user interface after loading a new segmentation image */ void OnSegmentationImageUpdate(bool reloaded); /** Update the user interface after loading a new labels file */ // void OnSegmentationLabelsUpdate(bool resetCurrentAndDrawOverLabels); /** Update the user interface after loading a new preprocessing image */ void OnSpeedImageUpdate(); // Splash screen functions void ShowSplashScreen(); void HideSplashScreen(); void UpdateSplashScreen(const char *message); /** A utility to center one window inside another */ static void CenterChildWindowInParentWindow(Fl_Window *childWindow, Fl_Window *parentWindow); void CenterChildWindowInMainWindow(Fl_Window *childWindow); // Zoom/pan management callbacks void OnResetView2DAction(unsigned int window); void OnResetAllViews2DAction(); void OnLinkedZoomChange(); void OnZoomLevelChange(); void OnMultisessionZoomChange(); void OnMultisessionPanChange(); // Method that allows global zoom to be set from the code (i.e., main) void SetZoomLevelAllWindows(float zoom); // Internal callback used to update the zoom percentage displayed void OnZoomUpdate(bool flagBroadcastUpdate = true); // Internal callback for when the crosshairs position changes. The flag // specifies if this update should be broadcast to other SNAP instances void OnCrosshairPositionUpdate(bool flagBroadcastUpdate = true); // Internal callback for when the view position changes void OnViewPositionsUpdate (bool flagBroadcastUpdate = true); // Callback for when 3D view changes void OnTrackballUpdate(bool flagBroadcastUpdate = true); // Internal method called when slices need to be re-connected to the image, // i.e., when a new image is loaded or the image-display geometry changes void OnImageGeometryUpdate(); /** Get the object used to coordinate zoom in slice windows */ irisGetMacro(SliceCoordinator,SliceWindowCoordinator *); // Polygon button callbacks void OnClosePolygonAction(unsigned int window); void OnUndoPointPolygonAction(unsigned int window); void OnClearPolygonAction(unsigned int window); void OnAcceptPolygonAction(unsigned int window); void OnInsertIntoPolygonSelectedAction(unsigned int window); void OnDeletePolygonSelectedAction(unsigned int window); void OnPastePolygonAction(unsigned int window); void OnPolygonStateUpdate(unsigned int id); void OnIRISFreehandFittingRateUpdate(); // Paintbrush action callbacks void OnPaintbrushAttributesUpdate(); void OnPaintbrushPaint(); void UpdatePaintbrushAttributes(); // IRIS: Annotation mode void OnAnnotationAttributesUpdate(); // IRIS: 3D Window callbacks void OnIRISMeshUpdateAction(); void OnIRISMeshAcceptAction(); void OnMeshResetViewAction(); void OnIRISMeshEditingAction(); void OnIRISMeshDisplaySettingsUpdate(); // SNAP: 3D Window callbacks void OnSNAPMeshUpdateAction(); void OnSNAPMeshResetViewAction(); void OnSNAPMeshContinuousUpdateAction(); // Volume and statistics actions void OnStatisticsUpdateAction(); void OnStatisticsExportAction(); void OnStatisticsCopyAction(); // Method called when user tries to close the window void OnMainWindowCloseAction(); // Show the help system void ShowHTMLPage(const char *link); // Update Layer Inspector void OnLayerInspectorUpdate(); // Get the window under mouse focus or -1 if none int GetWindowUnderFocus(void); // Opacity slider callbacks void OnIRISLabelOpacityChange(); // Undo/Redo buttons void OnUndoAction(); void OnRedoAction(); // Method for creating undo points void StoreUndoPoint(const char *text); void ClearUndoPoints(); // Methods to tweak window positions void OnWindowFocus(int iWindow); void OnWindowCollapse(int iWindow); // Save As PNG void OnActiveWindowSaveSnapshot(unsigned int window); // Get the value of the freehand drawing widget double GetFreehandFittingRate() { return m_InIRISFreehandFittingRate->value(); } // Load Images Non-Interactively void NonInteractiveLoadRGB(const char *fname); void NonInteractiveLoadGrey(const char *fname); void NonInteractiveLoadSegmentation(const char *fname); void NonInteractiveLoadLabels(const char *fname); // Load main image, whether it's RGB or Gray void NonInteractiveLoadMainImage(const char *fname, bool force_grey, bool force_rgb); void NonInteractiveLoadOverlayImage(const char *fname, bool force_grey, bool force_rgb); // Update menu of color labels // TODO: move this to a separate class in FLTK widget directory Fl_Menu_Item *GenerateColorLabelMenu(bool all, bool visible, bool clear); void DeleteColorLabelMenu(Fl_Menu_Item *menu); // Display tips and tricks void DisplayTips(); // Get the current display layout DisplayLayout GetDisplayLayout() const { return m_DisplayLayout; } // Change the layout, size of the SNAP window void SetDisplayLayout(DisplayLayout dlo); Fl_Menu_Bar* GetMainMenuBar() { return m_MenubarMain; } Fl_Window* GetMainWindow() { return m_WinMain; } Fl_Window* GetPopupToolbarWindow() { return m_WinTestPop; } void OnCollapsedViewPopupMenu(); protected: /** * * DESCRIPTION: * Initializes everything so the IRIS window can be shown. * This method is essentially the same as IRIS2000, but * we added a couple initializations of snake window stuff. * * PRECONDITIONS: * - basically assumes it's going to be called in the constructor. * * POSTCONDITIONS: * - the IRIS window can now be shown */ void InitializeUI(); /** hides the iris window and shows the snake window */ void ShowSNAP(); /** Hides the snake window and shows the iris window */ void ShowIRIS(); /** * DESCRIPTION * Runs the snake, and updates the GUI * * PRE * snake must be ready to run * * POST * * RETURNS * 1 if running is successful, 0 if an error occurred */ // int RunSnake(); // Callbacks to the simple file dialog void OnLoadLabelsAction(); void OnSaveLabelsAction(); void OnWriteVoxelCountsAction(); void OnSliceSliderChange(int id); // Menu item callbacks void OnMenuNewSegmentation(); void OnMenuSaveGrey(); void OnMenuLoadGrey(); void OnMenuLoadRGB(); void OnMenuLoadGreyOverlay(); void OnMenuLoadRGBOverlay(); void OnMenuUnloadOverlayLast(); void OnMenuUnloadOverlays(); void OnMenuLoadSegmentation(); void OnMenuLoadLabels(); void OnMenuSaveGreyROI(); void OnMenuSaveSegmentation(); void OnMenuSaveSegmentationMesh(); void OnMenuSaveLabels(); void OnMenuSaveScreenshot(unsigned int iSlice); void OnMenuSaveScreenshotSeries(unsigned int iSlice); void OnMenuWriteVoxelCounts(); void OnMenuIntensityCurve(); void OnMenuColorMap(); void OnMenuShowLayerInspector(); void OnMenuSavePreprocessed(); void OnMenuSaveLevelSet(); void OnLoadRecentAction(unsigned int iRecent); void OnLoadPreprocessedImageAction(); void OnMenuLoadAdvection(); void OnMenuImageInfo(); void OnMenuReorientImage(); // Save a slice void OnMenuExportSlice(unsigned int iSlice); // Display options callbacks void OnMenuShowDisplayOptions(); // Opacity slider callbacks void OnSNAPLabelOpacityChange(); // Help system callbacks void OnLaunchTutorialAction(); // Set the active page in the segmentation pipeline void SetActiveSegmentationPipelinePage(unsigned int page); // Restoring settings void OnRestoreSettingsAction(); void OnDoNotRestoreSettingsAction(); // Toggle cursor synchronization void OnSynchronizeCursorAction(); // Toggle hidden features void OnHiddenFeaturesToggleAction(); // Common code for loading segmentation void LoadSegmentation(const bool noninteractive, const char *fname = NULL); char *m_ChosedFile; // Set of logical states that the UI may encounter enum UIStateFlags { UIF_NULL, UIF_GRAY_LOADED, UIF_RGB_LOADED, UIF_BASEIMG_LOADED, UIF_OVERLAY_LOADED, UIF_IRIS_ACTIVE, UIF_IRIS_WITH_BASEIMG_LOADED, UIF_IRIS_WITH_GRAY_LOADED, UIF_IRIS_MESH_DIRTY, UIF_IRIS_MESH_ACTION_PENDING, UIF_IRIS_ROI_VALID, UIF_LINKED_ZOOM, UIF_UNDO_POSSIBLE, UIF_REDO_POSSIBLE, UIF_UNSAVED_CHANGES, UIF_MESH_SAVEABLE, UIF_SNAP_ACTIVE, UIF_SNAP_PAGE_PREPROCESSING, UIF_SNAP_PAGE_BUBBLES, UIF_SNAP_PAGE_SEGMENTATION, UIF_SNAP_PREPROCESSING_ACTIVE, UIF_SNAP_PREPROCESSING_DONE, UIF_SNAP_DIALOG_PARAMETERS, UIF_SNAP_DIALOG_PREPROCESSING, UIF_SNAP_SNAKE_RUNNING, UIF_SNAP_SNAKE_INITIALIZED, UIF_SNAP_SNAKE_EDITABLE, UIF_SNAP_SPEED_AVAILABLE, UIF_SNAP_MESH_DIRTY, UIF_SNAP_MESH_CONTINUOUS_UPDATE }; private: // Typedef for event callback commands typedef itk::SimpleMemberCommand SimpleCommandType; typedef itk::MemberCommand ProgressCommandType; // Pointer to the driving IRIS application object IRISApplication *m_Driver; // Pointer to the instance of the UserInterfaceLogic that has been launched // (for static members, like GlobalEventHandler) static UserInterfaceLogic *m_GlobalUI; // Pointer to the system interface object SystemInterface *m_SystemInterface; // Settings related to the cosmetic appearance of the application SNAPAppearanceSettings *m_AppearanceSettings; /** Reason why a user may be prompted to save changes */ enum PromptReason { REASON_QUIT, REASON_RESET, REASON_LOAD_MAIN, REASON_LOAD_SEGMENTATION }; // Widget activation manager - simplifies the headache of keeping widgets // activated and deactivated FLTKWidgetActivationManager *m_Activation; // how many snake iterations per step int m_SnakeStepSize; // The callback command used in the (complicated) snake VCR pipeline itk::SmartPointer m_PostSnakeCommand; // The main window label std::string m_MainWindowLabel; /** Wizard used to load grey image files */ ImageIOWizardLogic *m_WizGreyIO; /** Wizard used to load and save segmentation image files */ RestrictedImageIOWizardLogic *m_WizSegmentationIO; /** Wizard used to load and save preprocessing image files */ RestrictedImageIOWizardLogic *m_WizPreprocessingIO; /** Wizard used to load and save preprocessing image files */ RestrictedImageIOWizardLogic *m_WizLevelSetIO; /** Wizard for 3D mesh export */ MeshIOWizardUILogic *m_WizMeshExport; /** Layer dialog */ LayerInspectorUILogic *m_LayerUI; /** UI object for label editing */ LabelEditorUILogic *m_LabelEditorUI; /** Preprocessing UI object */ PreprocessingUILogic *m_PreprocessingUI; /** Parameter setting UI object */ SnakeParametersUILogic *m_SnakeParametersUI; /** A restore settings dialog */ RestoreSettingsDialogLogic *m_DlgRestoreSettings; /** A dialog for resampling the image */ ResizeRegionDialogLogic *m_DlgResampleRegion; /** A dialog for showing display options */ AppearanceDialogUILogic *m_DlgAppearance; /** Image reorientation dialog */ ReorientImageUILogic *m_DlgReorientImage; /** Help window */ HelpViewerLogic *m_HelpUI; /** Managers for the slice view windows */ IRISSliceWindow *m_IRISWindowManager2D[3]; SNAPSliceWindow *m_SNAPWindowManager2D[3]; /** A coordinator for the slice windows */ SliceWindowCoordinator *m_SliceCoordinator; /** Managers for the 3D windows */ Window3D *m_IRISWindowManager3D, *m_SNAPWindowManager3D; /** Splash screen timer */ clock_t m_SplashScreenStartTime; /** Save/load labels dialog */ SimpleFileDialogLogic *m_DlgLabelsIO; /** Write voxels dialog */ SimpleFileDialogLogic *m_DlgVoxelCountsIO; // An adapter used in association with the image IO wizard ImageInfoCallbackInterface *m_GreyCallbackInterface; // A list of recently open files shown in the load recent menu std::string m_RecentFileNames[5]; // Temporary value used for opacity slider toggling double m_OpacityToggleValue; // Whether the main window is in fullscreen mode bool m_FullScreen; // Global event handler (shortcuts, etc) static int GlobalEventHandler(int); // Global 'open document' event handler (Apple for now) static void GlobalOpenDocumentHandler(const char *fn_open); // Method to actually open a document void OpenDraggedContent(const char *fn_open, bool interactive); void OnOpenDroppedAction(int selection); // Called by the above, responds to global events int OnGlobalEvent(int); // Main idle function (for cursor sync) static void GlobalIdleHandler(void *); // Callbacks for state flags void OnUnsavedChangesStateChange(UIStateFlags flag, bool value); void OnMeshAvailabilityStateChange(UIStateFlags flag, bool value); /** Initialization subroutine that sets up the activation manager */ void InitializeActivationFlags(); /** * Some GUI state is shared between the windows, like opacity settings, * toolbar mode. This function syncs the two. */ void SyncIRISToSnake(); /** * Some GUI state is shared between the windows, like opacity settings, * toolbar mode. This function syncs the two. */ void SyncSnakeToIRIS(); /** Common code for cancelling and accepting the segmentation */ void CloseSegmentationCommon(); /** This method is used to figure out which image axis corresponds to a * given display window */ unsigned int GetImageAxisForDisplayWindow(unsigned int window); /** Progress callback for ITK methods. Will show the progress bar and fill * the bar as necessary */ void OnITKProgressEvent(itk::Object *source, const itk::EventObject &event); // This method is called when the snake image has changed. void OnSnakeUpdate(); // Set 'zoomed' window to 0..3, or pass in -1 in order to restore the display // to four side by side windows void UpdateWindowFocus( Fl_Group *parent, Fl_Group **panels, Fl_Gl_Window **boxes, int iWindow); // Update the color map of the speed image void UpdateSpeedColorMap(); // Update the bubble display void UpdateBubbleUI(); // Prompt the user if there are unsaved changes. Returns true if it's ok to // proceed without saving bool PromptBeforeLosingChanges(PromptReason reason); // Toggle display elements (hide control panel, slice window toolbars) void ToggleDisplayElements(); void ToggleFullScreen(); // Current display layout DisplayLayout m_DisplayLayout; /* Command used for progress tracking */ itk::SmartPointer m_ProgressCommand; // A function used to run the snake in the background friend void fnSnakeIdleFunction(void *userData); friend class UserInterfaceLogicMemberObserver; // Update the menu of recent files void GenerateRecentFilesMenu(); // Generate a PNG filename for use in saving snapshots std::string GenerateScreenShotFilename(); // A string where the last saved filename is stored std::string m_LastSnapshotFileName; // State of the UNDO system at the time the segmentation image // was last saved or loaded. This allows us to set the unsaved // changes flag during undo and redo operations std::list m_UndoStateAtLastIO; // Menu containing color labels, for use in FLTK menus Fl_Menu_Item *m_MenuDrawingLabels, *m_MenuDrawOverLabels; }; #endif /* *$Log: UserInterfaceLogic.h,v $ *Revision 1.56 2011/04/18 17:55:20 pyushkevich *Fixed bug 3243462. We can now save the mesh in SNAP (level set) mode * *Revision 1.55 2010/10/12 16:02:05 pyushkevich *Improved handling of collapsed windows * *Revision 1.54 2010/05/31 19:52:37 pyushkevich *Added volumes and statistics window * *Revision 1.53 2010/05/27 11:16:22 pyushkevich *Further improved polygon drawing interface * *Revision 1.52 2010/05/27 07:29:36 pyushkevich *New popup menu for polygon drawing, other improvements to polygon tool * *Revision 1.51 2010/04/16 05:14:38 pyushkevich *FIX: touched up previous checkin * *Revision 1.50 2010/04/16 04:02:35 pyushkevich *ENH: implemented drag and drop, OSX events, new command-line interface * *Revision 1.49 2010/04/14 10:06:23 pyushkevich *Added option to launch external SNAP * *Revision 1.48 2010/03/23 21:23:23 pyushkevich *Added display halving capability, *command line switches --zoom, --help, --compact * *Revision 1.47 2009/11/16 20:29:29 garyhuizhang *BUGFIX: a fix for messed up display on mac when switching between different panel zoom mode *ENH: added color map menu item * *Revision 1.46 2009/10/30 13:49:48 pyushkevich *FIX: improved behavior of synchronized pan. it now broadcasts viewport center rel. to cursor posn. *FIX: improved IPC. only 'new' messages are now acted on. * *Revision 1.45 2009/10/26 16:00:56 pyushkevich *ENH: improved/fixed cursor movement in all modes. added menu items for F3/F4 * *Revision 1.44 2009/10/17 20:39:51 pyushkevich *ENH: added tip of the day * *Revision 1.43 2009/09/14 19:04:52 garyhuizhang *ENH: layer inspector support for curor position input * *Revision 1.42 2009/09/10 21:25:24 garyhuizhang *ENH: Layer inspector now supports contrast adjustment of main image * *Revision 1.41 2009/08/29 23:17:02 garyhuizhang *BUGFIX: fix a memory leak * *Revision 1.40 2009/08/28 20:35:15 garyhuizhang *ENH: remove OverlayUI (replaced by LayerInspectorUI) * *Revision 1.39 2009/08/28 16:33:03 garyhuizhang *ENH: rename LayerEditor as LayerInspector * *Revision 1.38 2009/08/28 16:05:44 pyushkevich *Enabled toggling of UI components with 'F3' key and fullscreen mode with 'F4' key * *Revision 1.37 2009/08/26 21:49:56 pyushkevich *Improvements to the color map widget * *Revision 1.36 2009/08/26 01:10:20 garyhuizhang *ENH: merge grey and RGB overlays into one wrapper list and modify the associated GUI codes * *Revision 1.35 2009/07/22 21:06:24 pyushkevich *Changed the IO system and wizards, removed templating * *Revision 1.34 2009/06/18 18:11:24 garyhuizhang *ENH: multisession pan ui support *BUGFIX: single session pan working again * *Revision 1.33 2009/06/16 05:57:01 garyhuizhang *ENH: initial UI for layer manager, which replacing the old RGB overlay UI * *Revision 1.32 2009/06/15 01:54:10 garyhuizhang *BUGFIX: linked zoom misbehaving with overlay * *Revision 1.31 2009/06/14 20:43:17 garyhuizhang *ENH: multiple RGB overlay support * *Revision 1.30 2009/06/14 06:13:20 garyhuizhang *ENH: menu item association for grey overlay unload * *Revision 1.29 2009/06/13 05:02:00 garyhuizhang *ENH: improved implementation of recent file lists that combines both grey and RGB main images * *Revision 1.28 2009/06/13 03:29:40 garyhuizhang *ENH: checking for available software update * *Revision 1.27 2009/06/12 05:11:08 garyhuizhang *ENH: reorganized user interface * *Revision 1.26 2009/06/10 02:52:46 garyhuizhang *ENH: multiple grey overlay images support * *Revision 1.25 2009/06/09 05:46:38 garyhuizhang *ENH: main image support & grey overlay support * *Revision 1.24 2009/05/04 20:15:57 garyhuizhang *multisession panning support added * *Revision 1.23 2009/02/10 00:10:12 garyhuizhang *ENH: Support two drawing options in the Annotation mode: 1) each line shown only on the slice level it is drawn; 2) each line is shown on all slice levels * *Revision 1.22 2009/02/09 17:07:47 garyhuizhang *FIX: code refactoring -- command line and GUI loading of segmentation now shares the same code. this enables the validity checking of segmentation image on command line originally implemented for GUI. * *Revision 1.21 2009/02/05 23:03:41 garyhuizhang *ENH: support for saving the hidden feature flag * *Revision 1.20 2009/02/05 16:21:14 pyushkevich *ENH: added hidden features button * *Revision 1.19 2009/02/03 19:12:35 pyushkevich *ENH: added support for checking version via internet * *Revision 1.18 2009/01/17 10:40:28 pyushkevich *Added synchronization to 3D window viewpoint * *Revision 1.17 2008/12/02 21:43:24 pyushkevich *Reorganization of the watershed code * *Revision 1.16 2008/11/17 19:38:23 pyushkevich *Added tools dialog to label editor window * *Revision 1.15 2008/11/15 12:20:38 pyushkevich *Several new features added for release 1.8, including (1) support for reading floating point and mapping to short range; (2) use of image direction cosines to determine image orientation; (3) new reorient image dialog and changes to the IO wizard; (4) display of NIFTI world coordinates and yoking based on them; (5) multi-session zoom; (6) fixes to the way we keep track of unsaved changes to segmentation, including a new discard dialog; (7) more streamlined code for offline loading; (8) new command-line options, allowing RGB files to be read and opening SNAP by doubleclicking images; (9) versioning for IPC communications; (10) ruler for display windows; (11) bug fixes and other stuff I can't remember * *Revision 1.14 2008/11/01 11:32:00 pyushkevich *Compatibility with ITK 3.8 support for reading oriented images *Command line loading of RGB images *Improved load-image commands in UserInterfaceLogic * *Revision 1.13 2008/03/25 19:31:33 pyushkevich *Bug fixes for release 1.6.0 * *Revision 1.12 2008/02/27 04:34:46 garyhuizhang *1) rename OnMenuSaveScreenshots to OnMenuSaveScreenshotSeries *2) support menu access to both save single screenshot and screenshot series * *Revision 1.11 2008/02/23 23:41:12 garyhuizhang *add support for saving screenshots of the whole image volume * *Revision 1.10 2008/02/10 23:55:22 pyushkevich *Added "Auto" button to the intensity curve window; Added prompt before quitting on unsaved data; Fixed issues with undo on segmentation image load; Added synchronization between SNAP sessions. * *Revision 1.9 2008/01/08 20:34:52 pyushkevich *Implement toggle for opacity slider * *Revision 1.8 2007/12/30 04:05:18 pyushkevich *GPL License * *Revision 1.7 2007/12/25 15:46:23 pyushkevich *Added undo/redo functionality to itk-snap * *Revision 1.6 2007/09/18 18:42:40 pyushkevich *Added tablet drawing to polygon mode * *Revision 1.5 2007/06/07 00:49:16 pyushkevich *Debugged RGB changes * *Revision 1.4 2007/06/06 22:27:22 garyhuizhang *Added support for RGB images in SNAP * *Revision 1.3 2007/05/10 20:19:50 pyushkevich *Added VTK mesh export code and GUI * *Revision 1.2 2006/12/06 01:26:07 pyushkevich *Preparing for 1.4.1. Seems to be stable in Windows but some bugs might be still there * *Revision 1.1 2006/12/02 04:22:23 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.32 2006/02/02 01:23:10 pauly *BUG: Fixed SNAP bugs in the last checkin * *Revision 1.31 2006/02/01 20:21:26 pauly *ENH: An improvement to the main SNAP UI structure: one set of GL windows is used to support SNAP and IRIS modes * *Revision 1.30 2005/12/12 00:27:44 pauly *ENH: Preparing SNAP for 1.4 release. Snapshot functionality * *Revision 1.29 2005/11/10 23:02:14 pauly *ENH: Added support for VoxBo CUB files to ITK-SNAP, as well as some cosmetic touches * *Revision 1.28 2005/11/03 18:45:29 pauly *ENH: Enabled SNAP to read DICOM Series * *Revision 1.27 2005/10/29 14:00:15 pauly *ENH: SNAP enhacements like color maps and progress bar for 3D rendering * *Revision 1.26 2005/08/11 04:37:07 pauly *BUG: Fixed crash when using single-slice mode and entering auto-seg * *Revision 1.25 2005/04/21 14:46:30 pauly *ENH: Improved management and editing of color labels in SNAP * *Revision 1.24 2005/04/14 16:35:10 pauly *ENH: Added Image Info window to SNAP * *Revision 1.23 2005/03/08 03:12:51 pauly *BUG: Minor bugfixes in SNAP, mostly to the user interface * *Revision 1.22 2004/09/14 14:11:11 pauly *ENH: Added an activation manager to main UI class, improved snake code, various UI fixes and additions * *Revision 1.21 2004/09/08 12:09:46 pauly *ENH: Adapting SNAP to work with stop-n-go function in finite diff. framewk * *Revision 1.20 2004/08/26 18:29:19 pauly *ENH: New user interface for configuring the UI options * *Revision 1.19 2004/08/03 23:26:32 ibanez *ENH: Modification for building in multple platforms. By Julien Jomier. * *Revision 1.18 2004/07/30 15:56:34 jjomier *FIX: Warning, disabling 4786 on MSVC * *Revision 1.17 2004/07/29 14:00:36 pauly *ENH: A new interface for changing the appearance of SNAP * *Revision 1.16 2004/07/24 19:00:06 pauly *ENH: Thumbnail UI for slice zooming * *Revision 1.15 2004/07/21 18:17:45 pauly *ENH: Enhancements to the way that the slices are displayed * *Revision 1.14 2004/03/19 00:54:48 pauly *ENH: Added the ability to externally load the advection image * *Revision 1.13 2004/01/19 22:17:52 pauly *ENH: Final touches before the 1.0 release * *Revision 1.12 2003/12/07 19:48:41 pauly *ENH: Resampling, multiresolution * *Revision 1.11 2003/11/29 17:06:48 pauly *ENH: Minor Help issues * *Revision 1.10 2003/11/10 00:27:26 pauly *FIX: Bug with linear interpolation in PDE solver *ENH: Help viewer and tutorial * *Revision 1.9 2003/10/09 22:45:14 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.8 2003/10/07 00:37:27 jjomier *ENH: Added cygwin support * *Revision 1.7 2003/10/06 12:30:00 pauly *ENH: Added history lists, remembering of settings, new snake parameter preview * *Revision 1.6 2003/10/02 14:55:52 pauly *ENH: Development during the September code freeze * *Revision 1.2 2003/09/13 15:18:01 pauly *FIX: Got SNAP to work properly with different image orientations * *Revision 1.1 2003/09/11 13:51:01 pauly *FIX: Enabled loading of images with different orientations *ENH: Implemented image save and load operations * *Revision 1.5 2003/08/28 22:58:30 pauly *FIX: Erratic scrollbar behavior * *Revision 1.4 2003/08/28 14:37:09 pauly *FIX: Clean 'unused parameter' and 'static keyword' warnings in gcc. *FIX: Label editor repaired * *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:50 pauly *Initial checkin of the SNAP application into the InsightApplications tree. * *Revision 1.17 2003/07/12 01:34:18 pauly *More final changes before ITK checkin * *Revision 1.16 2003/07/11 23:28:10 pauly **** empty log message *** * *Revision 1.15 2003/07/10 14:30:26 pauly *Integrated ITK into SNAP level set segmentation * *Revision 1.14 2003/06/23 23:59:32 pauly *Command line argument parsing * *Revision 1.13 2003/06/14 22:42:06 pauly *Several changes. Started working on implementing the level set function *in ITK. * *Revision 1.12 2003/06/08 23:27:56 pauly *Changed variable names using combination of ctags, egrep, and perl. * *Revision 1.11 2003/06/08 16:11:42 pauly *User interface changes *Automatic mesh updating in SNAP mode * *Revision 1.10 2003/05/22 17:36:19 pauly *Edge preprocessing settings * *Revision 1.9 2003/05/17 21:39:30 pauly *Auto-update for in/out preprocessing * *Revision 1.8 2003/05/14 18:33:58 pauly *SNAP Component is working. Double thresholds have been enabled. Many other changes. * *Revision 1.7 2003/05/07 19:14:46 pauly *More progress on getting old segmentation working in the new SNAP. Almost there, region of interest and bubbles are working. * *Revision 1.6 2003/05/05 12:30:18 pauly **** empty log message *** * *Revision 1.5 2003/04/25 02:58:29 pauly *New window2d model with InteractionModes * *Revision 1.4 2003/04/23 06:05:18 pauly **** empty log message *** * *Revision 1.3 2003/04/18 17:32:18 pauly **** empty log message *** * *Revision 1.2 2003/04/18 00:25:37 pauly **** empty log message *** * *Revision 1.1 2003/04/16 05:04:17 pauly *Incorporated intensity modification into the snap pipeline *New IRISApplication *Random goodies * *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.33 2002/04/28 20:12:40 scheuerm *tiny documentation changes * *Revision 1.32 2002/04/28 17:29:43 scheuerm *Added some documentation * *Revision 1.31 2002/04/27 18:30:03 moon *Finished commenting * *Revision 1.30 2002/04/27 17:48:34 bobkov *Added comments * *Revision 1.29 2002/04/27 00:08:27 talbert *Final commenting run through . . . no functional changes. * *Revision 1.28 2002/04/26 17:37:13 moon *Fixed callback on save preproc dialog cancel button. *Fixed bubble browser output. Position was zero-based, which didn't match the 2D *window slice numbers (1 based), so I changed the bubble positions to be cursor *position +1. *Disallowed starting snake window if current label in not visible. *Put in Apply+ button in threshold dialog, which changes seg overlay to be an *overlay of the positive voxels in the preproc data (a zero-level visualization). *Added more m_OutMessage and m_OutMessage messages. * *Revision 1.27 2002/04/24 19:50:23 moon *Pulled LoadGreyFileCallback out of GUI into UserInterfaceLogic, made modifications due *to change in ROI semantics. Before, the ROI was from ul to lr-1, which is a bad *decision. I changed everything to work with a ROI that is inclusive, meaning *that all voxels from ul through lr inclusive are part of the ROI. This involved *a lot of small changes to a lot of files. * *Revision 1.26 2002/04/23 19:28:20 bobkov *declared TweakROI method in UserInterfaceLogic.h to be public *left ColorLabel.h and ColorLabel.cpp unchanged * *Revision 1.25 2002/04/19 23:03:59 moon *Changed more stuff to get the snake params state synched with the global state. *Changed the range of ground in snake params dialog. *Removed the use_del_g stuff, since it's really not necessary, I found out. * *Revision 1.24 2002/04/19 20:34:58 moon *Made preproc dialogs check global state and only preproc if parameters have changed. *So no if you hit apply, then ok, it doesn't re process on the ok. * *Revision 1.23 2002/04/18 21:14:03 moon *I had changed the Cancel buttons to be Close on the Filter dialogs, and I changed *the names of the callbacks in GUI, but not in UserInterfaceLogic. So I just hooked them *up so the dialogs get closed. * *Revision 1.22 2002/04/18 21:04:51 moon *Changed the IRIS window ROI stuff. Now the ROI is always valid if an image is *loaded, but there is a toggle to show it or not. This will work better with *Konstantin's addition of being able to drag the roi box. * *I also changed a bunch of areas where I was calling InitializeSlice for the 2D windows, *when this is not at all what I should have done. Now those spots call *MakeSegTextureCurrent, or MakeGreyTextureCurrent. This means that the view is not *reset every time the snake steps, the preproc/orig radio buttons are changed, etc. * *Revision 1.21 2002/04/16 18:54:33 moon *minor bug with not stopping snake when play is pushed, and then other *buttons are pushed. Also added a function that can be called when the user *clicks the "X" on a window, but it's not what we want, I don't think. The *problem is if the user clicks the "X" on the snake window when a "non modal" *dialog is up, all the windows close, but the program doesn't quit. I think *it's a bug in FLTK, but I can't figure out how to solve it. * *Revision 1.20 2002/04/16 13:07:56 moon *Added tooltips to some widgets, made minor changes to enabling/disabling of *widgets, clearing 3D window when initialization is restarted in snake window, *changed kappa in edge preproc dialog to be [0..1] range instead of [0..3] * *Revision 1.19 2002/04/14 22:02:54 scheuerm *Changed loading dialog for preprocessed image data. Code restructuring *along the way: Most important is addition of *SnakeVoxDataClass::ReadRawPreprocData() * *Revision 1.18 2002/04/11 23:07:43 bobkov *Commented the Bubble class *Commented m_BtnAddBubble, m_BtnRemoveBubble, *m_BrsActiveBubbles and m_InBubbleRadius callbacks *Commented GetBubbles and GetNumberOfBubbles methodS * *Revision 1.17 2002/04/10 20:19:40 moon *got play and stop vcr buttons to work. *put in lots of comments. * *Revision 1.16 2002/04/09 18:59:33 moon *Put in dialog to change snake parameters. Also implemented Rewind button, which *now restarts the snake. It seems for now that changing snake parameters restarts *the snake. I don't know if this is the way it has to be, or I just did something *wrong in snakewrapper. I'll have to check with Sean. * *Revision 1.15 2002/04/08 13:32:35 talbert *Added a preprocessed save dialog box as well as a save preprocessed menu *option in the snake window. Added the code necessary to implement the *GUI side of saving. * *Revision 1.14 2002/04/07 02:22:49 scheuerm *Improved handling of OK and Apply buttons in preprocessing dialogs. * *Revision 1.13 2002/04/04 15:30:09 moon *Put in code to get StepSize choice box filled with values and working. *AcceptSegment button callback puts snake seg data into full_data (IRIS) *Fixed a couple more UI cosmetic things. * *Revision 1.12 2002/04/03 22:12:07 moon *Added color chip, image probe, seg probe to snake window, although seg probe *maybe shouldn't be there. added update continuously checkbox to 3Dwindow. *changes accept/restart to be on top of each other, and one is shown at a time, *which I think is more intuitive. *changed snake iteration field to be text output. added callback for step size *choice. * *Revision 1.11 2002/03/27 17:59:40 moon *changed a couple things. nothing big. a callback in .fl was bool return type *which didn't compile in windows. this is the version I think will work for a *demo for Kye * *Revision 1.10 2002/03/26 18:16:32 scheuerm *Added loading and display of preprocessed data: *- added vtkImageDeepCopy function *- added flags indicating which dataset to display in GlobalState *- added flag indicating whether to load gray or preprocessed data * in the GUI class * *Revision 1.9 2002/03/25 02:15:57 scheuerm *Added loading of preprocessed data. It isn't being converted *to floats yet. It's not possible to actually display the data *right now. * *Revision 1.8 2002/03/24 19:27:46 talbert *Added callback the preprocess button to show dialog boxes for filtering. Added callbacks for buttons in filtering dialog boxes. Modified the AddBubbles callback so that the newest bubble is selected in the Bubble Browser. m_OutAboutCompiled and ran to verify that new bubbles are selected and that the dialogs appear over the *3d window. talbert s f * *Revision 1.7 2002/03/21 15:45:46 bobkov *implemented callbacks for buttons AddBubble and RemoveBubble, implemented callbacks for Radius slider and ActiveBubble browser, created methods getBubbles and getNumberOfBubbles e * *Revision 1.6 2002/03/19 19:35:32 moon *added snakewrapper to makefile so it gets compiled. started putting in callback, *etc. for snake vcr buttons. added snake object to IrisGlobals, instantiated in Main * *Revision 1.5 2002/03/19 17:47:10 moon *added some code to disable widgets, make the radio buttons work, etc. in the snake window. fixed the quit callback from the snake window to work (crashed before) *changed the [accept/restart]bubble_button widgets to be acceptinitialization_button and added callbacks (empty). * *Revision 1.4 2002/03/08 14:23:48 moon *added comments * *Revision 1.3 2002/03/08 14:06:29 moon *Added Header and Log tags to all files **/ itksnap/UserInterface/MeshIOWizard/0000755000076500000240000000000011560342171016610 5ustar paulystaffitksnap/UserInterface/MeshIOWizard/CVS/0000755000076500000240000000000011560342171017243 5ustar paulystaffitksnap/UserInterface/MeshIOWizard/CVS/Entries0000644000076500000240000000041511560342171020577 0ustar paulystaff/MeshExportSettings.h/1.3/Sat Nov 15 12:20:38 2008// /MeshIOWizardUI.fl/1.3/Wed Oct 13 15:03:00 2010// /MeshIOWizardUIBase.h/1.2/Sun Dec 30 04:05:27 2007// /MeshIOWizardUILogic.cxx/1.10/Mon Apr 18 17:35:30 2011// /MeshIOWizardUILogic.h/1.3/Mon Apr 18 17:35:30 2011// D itksnap/UserInterface/MeshIOWizard/CVS/Repository0000644000076500000240000000004311560342171021342 0ustar paulystaffitksnap/UserInterface/MeshIOWizard itksnap/UserInterface/MeshIOWizard/CVS/Root0000644000076500000240000000010011560342171020100 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/MeshIOWizard/MeshExportSettings.h0000644000076500000240000000461311107537226022610 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MeshExportSettings.h,v $ Language: C++ Date: $Date: 2008/11/15 12:20: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. =========================================================================*/ #ifndef __MeshExportSettings_h_ #define __MeshExportSettings_h_ #include "SNAPCommon.h" #include "GuidedMeshIO.h" #include /** * \class MeshExportSettings * The set of values that are collected during interaction with the * mesh export wizard. These settings can be passed on to other parts * of the program. */ class MeshExportSettings { public: typedef std::string StringType; typedef GuidedMeshIO::FileFormat FileFormat; irisGetMacro(MeshFileName, StringType); irisSetMacro(MeshFileName, StringType); irisGetMacro(MeshFormat, Registry); irisSetMacro(MeshFormat, Registry); irisGetMacro(FlagSingleLabel, bool); irisSetMacro(FlagSingleLabel, bool); irisGetMacro(FlagSingleScene, bool); irisSetMacro(FlagSingleScene, bool); irisGetMacro(ExportLabel, LabelType); irisSetMacro(ExportLabel, LabelType); virtual ~MeshExportSettings() {} private: StringType m_MeshFileName; Registry m_MeshFormat; bool m_FlagSingleLabel; bool m_FlagSingleScene; LabelType m_ExportLabel; }; #endif // __MeshExportSettings_h_ itksnap/UserInterface/MeshIOWizard/MeshIOWizardUI.fl0000644000076500000240000001247311455345044021712 0ustar paulystaff# data file for the Fltk User Interface Designer (fluid) version 1.0109 header_name {.h} code_name {.cxx} class MeshIOWizardUI {open : MeshIOWizardUIBase } { Function {MakeWindow()} {open return_type {virtual void} } { Fl_Window m_WinWizard { label {3D Mesh Export Wizard} open xywh {627 170 470 330} type Double code0 {\#include "MeshIOWizardUIBase.h"} visible } { Fl_Wizard m_GrpWizard {open xywh {0 0 475 330} box PLASTIC_DOWN_BOX } { Fl_Group m_PageMesh { label {Mesh Selection} open xywh {0 20 470 310} labelsize 12 } { Fl_Group {} {open selected xywh {25 65 415 195} labeltype ENGRAVED_LABEL align 21 } { Fl_Round_Button m_BtnMeshPageSingleExport { label {Export 3D mesh for a single selected label:} callback {this->OnMeshPageRadioChange();} xywh {50 85 310 20} type Radio down_box ROUND_DOWN_BOX value 1 labelsize 12 } Fl_Round_Button m_BtnMeshPageMultiExport { label {Export meshes for all the labels in the segmentation:} callback {this->OnMeshPageRadioChange();} xywh {50 165 310 20} type Radio down_box ROUND_DOWN_BOX labelsize 12 } Fl_Choice m_InMeshPageSelectedLabel { label {Label to export:} open xywh {160 115 185 20} down_box BORDER_BOX labelsize 12 textsize 12 } {} Fl_Group {} {open xywh {60 185 340 50} } { Fl_Round_Button m_BtnMeshPageIndexedExport { label {As individual mesh files (indexed by 001, 002, etc)} callback {this->OnMeshPageRadioChange();} xywh {70 190 310 20} type Radio down_box ROUND_DOWN_BOX labelsize 12 deactivate } Fl_Round_Button m_BtnMeshPageSceneExport { label {As a single 3D scene (not supported by all file formats)} callback {this->OnMeshPageRadioChange();} xywh {70 210 310 20} type Radio down_box ROUND_DOWN_BOX value 1 labelsize 12 deactivate } } } Fl_Button m_BtnMeshPageNext { label {&Next >} callback {this->OnMeshPageNext();} xywh {290 295 80 25} box PLASTIC_UP_BOX shortcut 0x8006e color 180 labelfont 1 labelsize 12 } Fl_Button {} { label {< Back} xywh {200 295 80 25} box PLASTIC_UP_BOX color 180 labelsize 12 deactivate } Fl_Button {} { label Cancel callback {this->OnCancel();} xywh {380 295 80 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } Fl_Group {} { label {Select which segmentation labels to export:} xywh {10 30 370 40} labeltype EMBOSSED_LABEL align 20 } {} } Fl_Group m_PageFile { label {File Selection} open xywh {0 20 470 310} labelsize 12 hide } { Fl_Group {} { label {Choose a filename to save the 3D mesh:} xywh {10 30 370 40} labeltype EMBOSSED_LABEL align 20 } {} Fl_Input m_InFilePageBrowser { label {Filename:} callback {this->OnFilePageFileInputChange();} tooltip {Enter the filename of the image you want to save, or select one using the 'Browse' and 'History' buttons} xywh {30 110 405 25} labelsize 12 align 5 when 1 textsize 12 } Fl_Button {} { label {&Browse...} callback {this->OnFilePageBrowse();} xywh {265 140 80 25} box PLASTIC_UP_BOX shortcut 0x80062 labelsize 12 } Fl_Menu_Button m_InFilePageHistory { label History callback {this->OnFilePageFileHistoryChange();} open xywh {355 140 80 25} box PLASTIC_UP_BOX selection_color 181 labelsize 12 align 20 textsize 12 } {} Fl_Choice m_InFilePageFormat { label {Mesh file format:} callback {this->OnFilePageFileFormatChange();} open tooltip {Use this dropdown box to select an image format if one is not selected for you automatically. Normally, SNAP will figure out the file format once you select an image file using the 'Browse' button.} xywh {30 200 210 25} down_box BORDER_BOX labelsize 12 align 5 textsize 12 } { MenuItem {} { label {Select a format...} xywh {30 30 100 20} labelsize 12 hide } } Fl_Button m_BtnFilePageNext { label {&Save} callback {this->OnFilePageNext();} xywh {290 295 80 25} box PLASTIC_UP_BOX shortcut 0x8006e color 180 labelfont 1 labelsize 12 deactivate } Fl_Button m_BtnFilePageBack { label {< Back} callback {this->OnFilePageBack()} xywh {200 295 80 25} box PLASTIC_UP_BOX color 180 labelsize 12 } Fl_Button {} { label Cancel callback {this->OnCancel();} xywh {380 295 80 25} box PLASTIC_UP_BOX shortcut 0xff1b color 180 labelsize 12 } } Fl_Group m_PageOptions { label Options open xywh {0 20 470 310} labelsize 12 hide } {} } } } } itksnap/UserInterface/MeshIOWizard/MeshIOWizardUIBase.h0000644000076500000240000000404110735614407022325 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MeshIOWizardUIBase.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:27 $ 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 __MeshIOWizardUIBase_h_ #define __MeshIOWizardUIBase_h_ /** * \class MeshIOWizardUIBase * The base class for the Mesh Export Wizard UI. */ class MeshIOWizardUIBase { public: // Dummy constructor and destructor MeshIOWizardUIBase() {}; virtual ~MeshIOWizardUIBase() {}; virtual void OnCancel() = 0; virtual void OnFilePageBrowse() = 0; virtual void OnFilePageNext() = 0; virtual void OnFilePageFileInputChange() = 0; virtual void OnFilePageFileHistoryChange() = 0; virtual void OnFilePageFileFormatChange() = 0; virtual void OnFilePageBack() = 0; virtual void OnMeshPageNext() = 0; virtual void OnMeshPageRadioChange() = 0; }; #endif // __MeshIOWizardUIBase_h_ itksnap/UserInterface/MeshIOWizard/MeshIOWizardUILogic.cxx0000644000076500000240000002533211553073142023063 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MeshIOWizardUILogic.cxx,v $ Language: C++ Date: $Date: 2011/04/18 17:35:30 $ Version: $Revision: 1.10 $ 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 "MeshIOWizardUILogic.h" #include "IRISApplication.h" #include "GuidedMeshIO.h" #include "FL/Fl_Native_File_Chooser.H" #include "FL/filename.H" #include "FL/fl_ask.H" using namespace std; MeshIOWizardUILogic ::MeshIOWizardUILogic() { m_MeshSelected = false; m_Driver = NULL; // Initialize the file format database m_FileFormatPattern[GuidedMeshIO::FORMAT_VTK] = "vtk"; m_FileFormatPattern[GuidedMeshIO::FORMAT_STL] = "stl"; m_FileFormatPattern[GuidedMeshIO::FORMAT_BYU] = "byu,y"; m_FileFormatDescription[GuidedMeshIO::FORMAT_VTK] = "VTK PolyData File"; m_FileFormatDescription[GuidedMeshIO::FORMAT_STL] = "STL Mesh File"; m_FileFormatDescription[GuidedMeshIO::FORMAT_BYU] = "BYU Mesh File"; } string MeshIOWizardUILogic ::GetFilePattern() { // String containing the whole patterns StringType pattern = ""; bool patternNeedsNewline = false; // String containing the "All Image Files" pattern StringType allImageFiles = "All Mesh Files\t*.{"; bool allImageFilesNeedsComma = false; // Go through all supported formats for(size_t i = 0; i < GuidedMeshIO::FORMAT_COUNT; i++) { // Add comma to allImageFiles if(allImageFilesNeedsComma) allImageFiles += ","; else allImageFilesNeedsComma = true; // Add extension to all image files allImageFiles += m_FileFormatPattern[i]; // Add a tab to the pattern if(patternNeedsNewline) pattern += "\n"; else patternNeedsNewline = true; // Construct the pattern pattern += m_FileFormatDescription[i]; pattern += " Files\t*.{"; pattern += m_FileFormatPattern[i]; pattern += "}"; } // Finish the all image pattern allImageFiles += "}\n"; // Compete the pattern pattern = allImageFiles + pattern; return pattern; } void MeshIOWizardUILogic:: OnCancel() { m_MeshSelected = false; m_WinWizard->hide(); } void MeshIOWizardUILogic ::OnFilePageBrowse() { // Get the pattern for selecting the file string pattern = GetFilePattern(); // Get the current pathname const char *path = m_InFilePageBrowser->value(); path = strlen(path) ? path : NULL; // Configure a file dialog const char *fName = NULL; Fl_Native_File_Chooser chooser; chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); chooser.title("Select a mesh file"); chooser.options(Fl_Native_File_Chooser::NEW_FOLDER); chooser.preset_file(path); chooser.filter(pattern.c_str()); if(chooser.show() == 0) fName = chooser.filename(); // Bring up th choice dialog if (fName && strlen(fName) > 0) { // Set the new filename m_InFilePageBrowser->value(fName); // Reset the format drop-down box to a null value m_InFilePageFormat->value(0); // Call the filename-changed callback OnFilePageFileInputChange(); } } void MeshIOWizardUILogic ::OnFilePageFileInputChange() { // Clear the registry m_Registry.Clear(); // Deactivate the next page m_BtnFilePageNext->deactivate(); // Check the length of the input const char *text = m_InFilePageBrowser->value(); if (text != NULL && strlen(text) > 0) { // Try to load the registry associated with this filename m_Driver->GetSystemInterface()->FindRegistryAssociatedWithFile( m_InFilePageBrowser->value(), m_Registry); // If the registry contains a file format, override with that GuidedMeshIO::FileFormat fmt = m_GuidedIO.GetFileFormat(m_Registry, GuidedMeshIO::FORMAT_COUNT); // Try to select a file format accoring to the file name if(fmt == GuidedMeshIO::FORMAT_COUNT) fmt = DetermineFileFormatFromFileName(text); // If the filename does not match any format, we do not change the // format choice box in case that the user has already set it manually if(fmt < GuidedMeshIO::FORMAT_COUNT) { m_InFilePageFormat->value((int)fmt+1); m_BtnFilePageNext->activate(); } else { m_InFilePageFormat->value(0); } } } GuidedMeshIO::FileFormat MeshIOWizardUILogic ::DetermineFileFormatFromFileName(const char *testFile) { // Iterate over the known file types for(size_t i = 0;i < GuidedMeshIO::FORMAT_COUNT;i++) { // Create a matching pattern StringType pattern = "*.{" + m_FileFormatPattern[i] + "}"; // Check if the filename matches the pattern if(fl_filename_match(testFile, pattern.c_str())) return (GuidedMeshIO::FileFormat) i; } // Failed: return illegal pattern return GuidedMeshIO::FORMAT_COUNT; } void MeshIOWizardUILogic ::SetHistory(const HistoryType &history) { // Store the history m_History = history; // Clear the history drop box m_InFilePageHistory->clear(); // Add the history if(history.size() > 0) { // Add each item to the history menu (history is traversed // backwards) for(HistoryType::reverse_iterator it=m_History.rbegin(); it!=m_History.rend();it++) { // FLTK's add() treats slashes as submenu separators, hence this code m_InFilePageHistory->replace( m_InFilePageHistory->add("dummy"),it->c_str()); } // Activate the history menu m_InFilePageHistory->activate(); } else { // Deactivate history m_InFilePageHistory->deactivate(); } } void MeshIOWizardUILogic ::OnFilePageNext() { // There must be a file format assert(m_InFilePageFormat->value() > 0); // There must be a selected label assert((size_t) m_InMeshPageSelectedLabel->value() < m_ColorLabelMenuIndex.size()); // The mesh has been selected m_MeshSelected = true; // Set the mesh filename m_ExportSettings.SetMeshFileName(m_InFilePageBrowser->value()); // Set the mesh file type m_GuidedIO.SetFileFormat(m_Registry, static_cast(m_InFilePageFormat->value() - 1)); m_ExportSettings.SetMeshFormat(m_Registry); // Set the export properties if(m_BtnMeshPageSingleExport->value()) { m_ExportSettings.SetFlagSingleLabel(true); m_ExportSettings.SetFlagSingleScene(false); m_ExportSettings.SetExportLabel( m_ColorLabelMenuIndex[m_InMeshPageSelectedLabel->value()]); } else if(m_BtnMeshPageMultiExport->value()) { m_ExportSettings.SetFlagSingleLabel(false); m_ExportSettings.SetExportLabel(0); if(m_BtnMeshPageSceneExport->value()) { m_ExportSettings.SetFlagSingleScene(true); } else { m_ExportSettings.SetFlagSingleScene(false); } } // Hide the wizard m_WinWizard->hide(); } void MeshIOWizardUILogic ::OnFilePageFileHistoryChange() { // Copy the history value to the filename m_InFilePageBrowser->value( m_InFilePageHistory->mvalue()->label()); // Update everything OnFilePageFileInputChange(); } void MeshIOWizardUILogic ::OnFilePageFileFormatChange() { // Activate the next button if there is a format selected if(m_InFilePageFormat->value() > 0) m_BtnFilePageNext->activate(); else m_BtnFilePageNext->deactivate(); } void MeshIOWizardUILogic ::OnFilePageBack() { m_GrpWizard->value(m_PageMesh); } void MeshIOWizardUILogic ::OnMeshPageNext() { m_GrpWizard->value(m_PageFile); } void MeshIOWizardUILogic ::OnMeshPageRadioChange() { // Set the other radio buttons if(m_BtnMeshPageSingleExport->value()) { m_InMeshPageSelectedLabel->activate(); m_BtnMeshPageIndexedExport->deactivate(); m_BtnMeshPageSceneExport->deactivate(); } else { m_InMeshPageSelectedLabel->deactivate(); m_BtnMeshPageIndexedExport->activate(); m_BtnMeshPageSceneExport->activate(); } } // Custom initialization code void MeshIOWizardUILogic ::MakeWindow() { // Parent's method MeshIOWizardUI::MakeWindow(); // Initialize the file save dialog box based on the allowed file types for(size_t i = 0; i < GuidedMeshIO::FORMAT_COUNT; i++) { // Create an appropriate description string text = m_FileFormatDescription[i]; // Add a menu option to the save menu, disabling it if it's unsupported m_InFilePageFormat->add(text.c_str(), 0, NULL, NULL, 0); } // Set the format description to 0 m_InFilePageFormat->value(0); } // Display the wizard and get the save settings bool MeshIOWizardUILogic ::DisplayWizard(IRISApplication *driver, bool snapmode) { // Set the driver m_Driver = driver; // The loaded flag is false m_MeshSelected = false; // Switch IRIS mode / SNAP mode if(snapmode) { // Just go to the file page m_GrpWizard->value(m_PageFile); } else { // Point the wizard to the first page m_GrpWizard->value(m_PageMesh); } // Get the list of all currenly available labels ColorLabelTable *clt = m_Driver->GetColorLabelTable(); // Initialize the mapping from menu index to color label m_ColorLabelMenuIndex.clear(); // Populate the color label list m_InMeshPageSelectedLabel->clear(); size_t k = 0; for(size_t i = 1; i < MAX_COLOR_LABELS; i++) { if(clt->IsColorLabelValid(i)) { ColorLabel label = clt->GetColorLabel(i); m_InMeshPageSelectedLabel->add(label.GetLabel()); if(i == m_Driver->GetGlobalState()->GetDrawingColorLabel()) m_InMeshPageSelectedLabel->value(k); m_ColorLabelMenuIndex.push_back(i); k++; } } // Show the input window m_WinWizard->show(); // Loop until the window has been closed while (m_WinWizard->visible()) Fl::wait(); // Remove the driver m_Driver = NULL; // Whether or not the load has been succesfull return m_MeshSelected; } itksnap/UserInterface/MeshIOWizard/MeshIOWizardUILogic.h0000644000076500000240000000754311553073142022514 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: MeshIOWizardUILogic.h,v $ Language: C++ Date: $Date: 2011/04/18 17:35:30 $ 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 __MeshIOWizardUILogic_h_ #define __MeshIOWizardUILogic_h_ #include "FLTKWidgetActivationManager.h" #include "MeshIOWizardUI.h" #include "MeshExportSettings.h" #include "GuidedMeshIO.h" #include #include class IRISApplication; /** * \class MeshIOWizardUILogic * The UI logic class for the Mesh Export Wizard UI. */ class MeshIOWizardUILogic : public MeshIOWizardUI { public: // History typedef typedef std::string StringType; typedef std::vector HistoryType; // Dummy constructor and destructor MeshIOWizardUILogic(); virtual ~MeshIOWizardUILogic() {}; // Callback methods extended from the UIBase class void OnCancel(); void OnFilePageBrowse(); void OnFilePageNext(); void OnFilePageFileInputChange(); void OnFilePageFileHistoryChange(); void OnFilePageFileFormatChange(); void OnFilePageBack(); void OnMeshPageNext(); void OnMeshPageRadioChange(); // Custom initialization code void MakeWindow(); // Display the wizard and get the save settings bool DisplayWizard(IRISApplication *driver, bool snapmode); /** Set the history list of recently opened files */ void SetHistory(const HistoryType &history); // Set the history values for the wizard irisGetMacro(History, HistoryType); // Get the export settings irisGetMacro(ExportSettings, MeshExportSettings); private: // State flag system enum UIStateFlags { UIF_NULL }; // Activation manager FLTKWidgetActivationManager m_Activation; // Parent-level IRIS application IRISApplication *m_Driver; // History of loaded filenames HistoryType m_History; // The settings collected by the wizard MeshExportSettings m_ExportSettings; // Whether a mesh has been loaded in the wizard bool m_MeshSelected; /** Extensions for different file formats */ StringType m_FileFormatPattern[GuidedMeshIO::FORMAT_COUNT]; /** Brief descriptions of different file formats */ StringType m_FileFormatDescription[GuidedMeshIO::FORMAT_COUNT]; // Generate an FLTK search pattern based on selection StringType GetFilePattern(); // Guess file format from a filename GuidedMeshIO::FileFormat DetermineFileFormatFromFileName(const char *testFile); // The registry associated with the mesh being saves Registry m_Registry; // A guided mesh IO object used to save meshes GuidedMeshIO m_GuidedIO; // An array used to link labels in the drop down to label indices std::vector m_ColorLabelMenuIndex; }; #endif // __MeshIOWizardUILogic_h_ itksnap/UserInterface/SliceWindow/0000755000076500000240000000000011560342171016532 5ustar paulystaffitksnap/UserInterface/SliceWindow/AnnotationInteractionMode.cxx0000644000076500000240000001673611144664622024420 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: AnnotationInteractionMode.cxx,v $ Language: C++ Date: $Date: 2009/02/12 00:13:06 $ Version: $Revision: 1.13 $ Copyright (c) 2007 Paul A. Yushkevich 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 "AnnotationInteractionMode.h" #include "GlobalState.h" #include "PolygonDrawing.h" #include "UserInterfaceBase.h" #include "IRISApplication.h" #include #include #ifndef PI #define PI 3.14159265358979323846 #endif AnnotationInteractionMode ::AnnotationInteractionMode(GenericSliceWindow *parent) : GenericSliceWindow::EventHandler(parent) { m_FlagDrawingLine = false; } AnnotationInteractionMode ::~AnnotationInteractionMode() { } int AnnotationInteractionMode ::OnKeyDown(const FLTKEvent &event) { // on mac there are two delete keys // one maps to backspace on other pc if(Fl::event_key() == FL_Delete || Fl::event_key() == FL_BackSpace) { // delete all or delete just the last line if(Fl::event_state() == FL_SHIFT) m_Lines.clear(); else if(!m_Lines.empty()) m_Lines.pop_back(); // Redraw m_Parent->GetCanvas()->redraw(); // only return 1 when the keystroke is handled return 1; } // leave it for additional handling return 0; } int AnnotationInteractionMode ::OnMousePress(const FLTKEvent &event) { // when active drawing if(m_FlagDrawingLine) { // Record the location Vector3f xEvent = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); // Handle different mouse buttons if(Fl::event_button() == FL_LEFT_MOUSE) // Move the second point around the first point which is fixed m_CurrentLine.second = xEvent; else if (Fl::event_button() == FL_MIDDLE_MOUSE) { // Translation of the segment Vector3f delta = xEvent - m_CurrentLine.second; m_CurrentLine.second = xEvent; m_CurrentLine.first += delta; } else if(Fl::event_button() == FL_RIGHT_MOUSE) { // Commit the segment m_Lines.push_back(m_CurrentLine); m_FlagDrawingLine = false; } // Redraw m_Parent->GetCanvas()->redraw(); // Even though no action may have been performed, we don't want other handlers // to get the left and right mouse button events return 1; } else return 0; } int AnnotationInteractionMode ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent) { // Make sure it's left mouse button being pressed if(Fl::event_button() == FL_RIGHT_MOUSE) return 1; // Record the location Vector3f xEvent = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); // Record the location of the event if(m_FlagDrawingLine) { if(Fl::event_button() == FL_LEFT_MOUSE) // Move the second point around the first point which is fixed m_CurrentLine.second = xEvent; else if(Fl::event_button() == FL_MIDDLE_MOUSE) { // Translation of the segment Vector3f delta = xEvent - m_CurrentLine.second; m_CurrentLine.second = xEvent; m_CurrentLine.first += delta; } } else if(Fl::event_button() == FL_LEFT_MOUSE) { m_CurrentLine.first = xEvent; m_CurrentLine.second = xEvent; m_FlagDrawingLine = true; } // redraw m_Parent->GetCanvas()->redraw(); return 1; } void AnnotationInteractionMode ::OnDraw() { // Get the current annotation settings AnnotationSettings as = m_ParentUI->GetDriver()->GetGlobalState()->GetAnnotationSettings(); const bool shownOnAllSlices = as.shownOnAllSlices; // Push the line state glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); // set line and point drawing parameters glPointSize(3); glLineWidth(1.0); glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Draw current line if(m_FlagDrawingLine) { glColor3d(1.,1.,0.); glBegin(GL_POINTS); glVertex2d(m_CurrentLine.first[0], m_CurrentLine.first[1]); glVertex2d(0.5 * (m_CurrentLine.first[0] + m_CurrentLine.second[0]), 0.5 * (m_CurrentLine.first[1] + m_CurrentLine.second[1])); glVertex2d(m_CurrentLine.second[0], m_CurrentLine.second[1]); glEnd(); glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); glEnable(GL_LINE_STIPPLE); glLineStipple(1, 0x9999); glBegin(GL_LINES); glVertex2d(m_CurrentLine.first[0], m_CurrentLine.first[1]); glVertex2d(m_CurrentLine.second[0], m_CurrentLine.second[1]); glEnd(); glPopAttrib(); // Compute the length of the drawing line Vector2f pt1InAna = m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.first); Vector2f pt2InAna = m_Parent->MapSliceToPhysicalWindow(m_CurrentLine.second); double length = (pt1InAna[0] - pt2InAna[0]) * (pt1InAna[0] - pt2InAna[0]) + (pt1InAna[1] - pt2InAna[1]) * (pt1InAna[1] - pt2InAna[1]); length = sqrt(length); std::ostringstream oss_length; oss_length << std::setprecision(4) << length << " " << "mm"; // Compute the offset of 5 screen pixels Vector3f v_offset = m_Parent->MapWindowToSlice(Vector2f(5.f,5.f)) - m_Parent->MapWindowToSlice(Vector2f(0.f,0.f)); Vector3f v_dims = m_Parent->MapWindowToSlice(Vector2f(48.f,12.f)) - m_Parent->MapWindowToSlice(Vector2f(0.f,0.f)); // Show the length of the drawing line gl_draw(oss_length.str().c_str(), (float) (m_CurrentLine.second[0] + v_offset(0)), (float) (m_CurrentLine.second[1] + v_offset(1))); // Compute and show the intersection angles of the drawing line with the other (visible) lines for(LineIntervalList::iterator it = m_Lines.begin(); it!=m_Lines.end(); it++) { if(shownOnAllSlices || it->first[2] == m_Parent->m_DisplayAxisPosition) { Vector2f vit = m_Parent->MapSliceToPhysicalWindow(it->first) - m_Parent->MapSliceToPhysicalWindow(it->second); vit /= sqrt(vit[0]*vit[0] + vit[1]*vit[1]); Vector2f vc = pt1InAna - pt2InAna; vc /= sqrt(vc[0]*vc[0] + vc[1]*vc[1]); // Compute the dot product and no need for the third components that are zeros double angle = 180.0 * acos(fabs(vc[0]*vit[0]+vc[1]*vit[1])) / PI; std::ostringstream oss_angle; oss_angle << std::setprecision(3) << angle << " " << "deg"; // Show the length of the drawing line gl_draw(oss_angle.str().c_str(), (float) (0.5*(it->first[0] + it->second[0]) + v_offset(0)), (float) (0.5*(it->first[1] + it->second[1]) + v_offset(1))); } } } // Draw each of the lines glColor3d(1.,0.,0.); glBegin(GL_POINTS); for(LineIntervalList::iterator it = m_Lines.begin(); it!=m_Lines.end(); it++) { if(shownOnAllSlices || it->first[2] == m_Parent->m_DisplayAxisPosition) glVertex2d(0.5*(it->first[0] + it->second[0]), 0.5*(it->first[1] + it->second[1])); } glEnd(); glBegin(GL_LINES); for(LineIntervalList::iterator it = m_Lines.begin(); it!=m_Lines.end(); it++) { if(shownOnAllSlices || it->first[2] == m_Parent->m_DisplayAxisPosition) { glVertex2d(it->first[0], it->first[1]); glVertex2d(it->second[0], it->second[1]); } } glEnd(); glPopAttrib(); } itksnap/UserInterface/SliceWindow/AnnotationInteractionMode.h0000644000076500000240000000563211144330075024026 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: AnnotationInteractionMode.h,v $ Language: C++ Date: $Date: 2009/02/10 16:50:05 $ Version: $Revision: 1.6 $ 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 __AnnotationInteractionMode_h_ #define __AnnotationInteractionMode_h_ #include "GenericSliceWindow.h" #include "GlobalState.h" #include #include /** * \class AnnotationInteractionMode * \brief UI interaction mode that takes care of polygon drawing and editing. * * \see GenericSliceWindow */ class AnnotationInteractionMode : public GenericSliceWindow::EventHandler { public: AnnotationInteractionMode(GenericSliceWindow *parent); virtual ~AnnotationInteractionMode(); int OnMousePress(const FLTKEvent &event); int OnKeyDown(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent) {return 0;}; int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent); int OnShortcut(const FLTKEvent &event) {return 0;}; void OnDraw(); private: // Annotations typedef std::pair LineIntervalType; typedef std::list LineIntervalList; LineIntervalList m_Lines; // What happened on last click bool m_FlagDrawingLine; LineIntervalType m_CurrentLine; // The window handler needs to access our privates friend class IRISSliceWindow; }; #endif // __AnnotationInteractionMode_h_ itksnap/UserInterface/SliceWindow/BubblesInteractionMode.cxx0000644000076500000240000001451710753707352023662 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: BubblesInteractionMode.cxx,v $ Language: C++ Date: $Date: 2008/02/10 23:55:22 $ 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. =========================================================================*/ #include "BubblesInteractionMode.h" #include "IRISApplication.h" #include "IRISSliceWindow.h" #include "UserInterfaceBase.h" #include "IRISImageData.h" #include #include #include const GLubyte stipple[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55 }; BubblesInteractionMode ::BubblesInteractionMode(GenericSliceWindow *parent) :GenericSliceWindow::EventHandler(parent) { } void BubblesInteractionMode ::OnDraw() { // Draw the bubbles if necessary if (m_GlobalState->GetSnakeActive() == false) { // Get the list of bubbles std::vector bubbles = m_GlobalState->GetBubbleArray(); // draw bubbles int numBubbles = bubbles.size(); int activeBubble = m_GlobalState->GetActiveBubble(); if (numBubbles > 0) { // Get the active color label int currentcolor = m_GlobalState->GetDrawingColorLabel(); ColorLabel cl = m_Driver->GetColorLabelTable()->GetColorLabel(currentcolor); if (cl.IsValid()) { // Get the current alpha blending factor for displaying overlays unsigned char alpha = m_GlobalState->GetSegmentationAlpha(); // Get the color of the active color label unsigned char rgb[3]; cl.GetRGBVector(rgb); // Get the current crosshairs position Vector3f cursorImage = to_float(m_Driver->GetCursorPosition()) + Vector3f(0.5f); // Get the image space dimension that corresponds to this window int iid = m_Parent->m_ImageAxes[2]; // Get the other essentials from the parent Vector3f scaling = m_Parent->m_SliceSpacing; // Turn on alpha blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Create a filled circle object GLUquadricObj *object = gluNewQuadric(); gluQuadricDrawStyle(object,GLU_FILL); // Draw each bubble for (int i = 0; i < numBubbles; i++) { // Get the center and radius of the i-th bubble Vector3f ctrImage = to_float(bubbles[i].center) + Vector3f(0.5f); double radius = bubbles[i].radius; // Remap the center into slice coordinates Vector3f ctrSlice = m_Parent->MapImageToSlice(to_float(ctrImage)); // Compute the offset from the center along the slice z-direction // in physical coordinates double dcenter = scaling(2) * (cursorImage(iid) - ctrImage(iid)); // Check if the bubble is intersected by the current slice plane if (dcenter >= radius || -dcenter >= radius) continue; // Compute the radius of the bubble in the cut plane double diskradius = sqrt(fabs(radius*radius - dcenter*dcenter)); // Draw the bubble glColor4ub(rgb[0],rgb[1],rgb[2],alpha); glPushMatrix(); glPushAttrib(GL_POLYGON_BIT | GL_COLOR_BUFFER_BIT); if(activeBubble == i) { glEnable(GL_POLYGON_STIPPLE); glPolygonStipple(stipple); } glTranslatef(ctrSlice[0], ctrSlice[1], 0.0f); glScalef(1.0f / scaling(0),1.0f / scaling(1),1.0f); gluDisk(object,0,diskradius,100,1); glPopAttrib(); // If the bubble is active, draw an outline around the bubble if(activeBubble == i) { glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glLineWidth(1.5); glColor4ub( 255 - (255 - rgb[0]) / 2, 255 - (255 - rgb[1]) / 2, 255 - (255 - rgb[2]) / 2, 255); glBegin(GL_LINE_LOOP); for(unsigned int d = 0; d < 360; d+=2) { double rad = d * vnl_math::pi / 180.0; glVertex2f(diskradius * cos(rad), diskradius * sin(rad)); } glEnd(); glPopAttrib(); } glPopMatrix(); } gluDeleteQuadric(object); glDisable(GL_BLEND); } } } } itksnap/UserInterface/SliceWindow/BubblesInteractionMode.h0000644000076500000240000000406410735614407023301 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: BubblesInteractionMode.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:27 $ 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 __BubblesInteractionMode_h_ #define __BubblesInteractionMode_h_ #include "GenericSliceWindow.h" /** * \class BubblesInteractionMode * \brief UI interaction mode that takes care of bubble placement * in segmentation. * * The interaction mode for display (and one day, manipulation) of * intialization bubbles for the segmentation. Presently, this class * only has a draw method, but if we ever want to me able to click and * drag bubbles, this is the place to do so * * \see GenericSliceWindow */ class BubblesInteractionMode : public GenericSliceWindow::EventHandler { public: BubblesInteractionMode(GenericSliceWindow *parent); void OnDraw(); }; #endif // __BubblesInteractionMode_h_ itksnap/UserInterface/SliceWindow/CrosshairsInteractionMode.cxx0000644000076500000240000003354211457377610024425 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: CrosshairsInteractionMode.cxx,v $ Language: C++ Date: $Date: 2010/10/19 20:28:56 $ 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. =========================================================================*/ #include "CrosshairsInteractionMode.h" #include "SNAPOpenGL.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "SNAPAppearanceSettings.h" #include "UserInterfaceBase.h" #include "SliceWindowCoordinator.h" CrosshairsInteractionMode ::CrosshairsInteractionMode( GenericSliceWindow *parent, Button cursor_button, Button zoom_button, Button pan_button) : GenericSliceWindow::EventHandler(parent), m_BtnCursor(cursor_button), m_BtnZoom(zoom_button), m_BtnPan(pan_button) { m_NeedToRepaintControls = false; m_LastViewposUpdateTime = 0; m_NeedUIUpdateOnRepaint = false; } int CrosshairsInteractionMode ::OnMousePress(const FLTKEvent &event) { if(m_BtnCursor == ANY || event.SoftButton == m_BtnCursor) { UpdateCrosshairs(event); m_Parent->m_GlobalState->SetUpdateSliceFlag(0); m_RepeatEvent = event; } else if(m_BtnZoom == ANY || event.SoftButton == m_BtnZoom) { m_StartViewZoom = m_Parent->GetViewZoom(); } else if(m_BtnPan == ANY || event.SoftButton == m_BtnPan) { m_StartViewPosition = m_Parent->m_ViewPosition; } return 1; } int CrosshairsInteractionMode ::OnMouseRelease(const FLTKEvent &event,const FLTKEvent &dragEvent) { if(m_BtnCursor == ANY || dragEvent.SoftButton == m_BtnCursor) { UpdateCrosshairs(event); m_Parent->m_GlobalState->SetUpdateSliceFlag(1); m_RepeatEvent = event; return 1; } else if(m_BtnZoom == ANY || dragEvent.SoftButton == m_BtnZoom) return 1; else if(m_BtnPan == ANY || dragEvent.SoftButton == m_BtnPan) return 1; return 0; } int CrosshairsInteractionMode ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &dragEvent) { if(m_BtnCursor == ANY || dragEvent.SoftButton == m_BtnCursor) { UpdateCrosshairs(event); m_Parent->m_GlobalState->SetUpdateSliceFlag(1); m_RepeatEvent = event; return 1; } else if(m_BtnZoom == ANY || dragEvent.SoftButton == m_BtnZoom) { // Under the right button, the tool causes us to zoom based on the vertical // motion float zoom = m_StartViewZoom * pow(1.02f,(float)(event.XSpace(1) - dragEvent.XSpace(1))); // Clamp the zoom factor to reasonable limits zoom = m_ParentUI->GetSliceCoordinator()->ClampZoom(m_Parent->m_Id,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); m_ParentUI->GetSliceCoordinator()->OnZoomUpdateInWindow(m_Parent->m_Id,zoom); // Schedule an update of the zoom percentage display in the parent m_NeedUIUpdateOnRepaint = true; return 1; } else if (m_BtnPan == ANY || dragEvent.SoftButton == m_BtnPan) { // Compute the start and end point in slice coordinates Vector3f xStart = m_Parent->MapWindowToSlice(dragEvent.XSpace.extract(2)); Vector3f xEnd = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); Vector2f xOffset(xEnd[0] - xStart[0],xEnd[1] - xStart[1]); // Remove the scaling by spacing xOffset(0) *= m_Parent->m_SliceSpacing(0); xOffset(1) *= m_Parent->m_SliceSpacing(1); // Under the left button, the tool changes the view_pos by the // distance traversed m_Parent->SetViewPosition(m_StartViewPosition - xOffset); m_ParentUI->OnViewPositionsUpdate(); return 1; } return 0; } int CrosshairsInteractionMode ::OnMouseWheel(const FLTKEvent &irisNotUsed(event)) { // Must have the cursor inside the window to process key events if(!m_Parent->GetCanvas()->GetFocus()) return 0; // Get the amount of the scroll float scroll = (float) Fl::event_dy(); // Get the cross-hairs position in image space Vector3ui xCrossImage = m_Driver->GetCursorPosition(); // Map it into slice space Vector3f xCrossSlice = m_Parent->MapImageToSlice(to_float(xCrossImage) + Vector3f(0.5f)); // Advance by the scroll amount xCrossSlice[2] += scroll; // Map back into display space xCrossImage = to_unsigned_int(m_Parent->MapSliceToImage(xCrossSlice)); // Clamp by the display size Vector3ui xSize = m_Driver->GetCurrentImageData()->GetVolumeExtents(); Vector3ui xCrossClamped = xCrossImage.clamp( Vector3ui(0,0,0),xSize - Vector3ui(1,1,1)); // Update the crosshairs position in the global state m_Driver->SetCursorPosition(xCrossClamped); // Cause a repaint m_NeedToRepaintControls = true; // Update the crosshairs position in the current image data m_ParentUI->OnCrosshairPositionUpdate(true); m_ParentUI->RedrawWindows(); return 1; } void CrosshairsInteractionMode ::UpdateCrosshairs(const Vector3f &xClick) { // 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_Driver->GetCurrentImageData()->GetVolumeExtents()); Vector3ui xCrossClamped = to_unsigned_int( xCrossInteger.clamp(Vector3i(0),xSize - Vector3i(1))); // Update the crosshairs position in the global state m_Driver->SetCursorPosition(xCrossClamped); // Cause a repaint m_NeedToRepaintControls = true; // Update the crosshairs position in the current image data m_ParentUI->OnCrosshairPositionUpdate(true); m_ParentUI->RedrawWindows(); } void CrosshairsInteractionMode ::TimeoutCallback(void *vp) { CrosshairsInteractionMode *cim = (CrosshairsInteractionMode *)vp; FLTKEvent ev; if(cim->GetCanvas()->IsDragging()) { FLTKEvent ev = cim->m_RepeatEvent; ev.TimeStamp = FLTKEventTimeStamp(); cim->UpdateCrosshairs(ev); } } void CrosshairsInteractionMode ::UpdateCrosshairs(const FLTKEvent &event) { // Compute the position in slice coordinates Vector3f xClick = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); // Check if the cursor is in one of the hot zones Vector3f z0 = m_Parent->MapWindowToSlice( Vector2f(0.1 * m_Parent->GetCanvas()->w(),0.1 * m_Parent->GetCanvas()->h())); Vector3f z1 = m_Parent->MapWindowToSlice( Vector2f(0.9 * m_Parent->GetCanvas()->w(),0.9 * m_Parent->GetCanvas()->h())); Vector3f y0 = m_Parent->MapWindowToSlice( Vector2f(0.0 * m_Parent->GetCanvas()->w(),0.0 * m_Parent->GetCanvas()->h())); Vector3f y1 = m_Parent->MapWindowToSlice( Vector2f(1.0 * (m_Parent->GetCanvas()->w()-1),1.0 * (m_Parent->GetCanvas()->h()-1))); bool hotzone = false; Vector2f newViewPos = m_Parent->m_ViewPosition; for(size_t i = 0; i < 2; i++) { // There are three discrete speeds int pixel_speeds[] = {5, 10, 20}; int min_voxel_speeds[] = {1, 2, 4}; double zmin = std::min(z0[i], z1[i]), zmax = std::max(z0[i], z1[i]); double ymin = std::min(y0[i], y1[i]), ymax = std::max(y0[i], y1[i]); if(xClick[i] < zmin) { // The speed is based on how close to the border we are double relspeed = (zmin - xClick[i]) / (zmin - ymin); int speedidx = std::min((int) (relspeed * 2.0), 2); int pixspeed = pixel_speeds[speedidx]; // We want to move 5 screen pixels every 0.1 sec. How many voxels is that int nvox = std::max(min_voxel_speeds[speedidx], (int) (0.5 + pixspeed / (m_Parent->m_ViewZoom * m_Parent->m_SliceSpacing[i]))); // Move the cursor left by nvox voxel newViewPos[i] -= nvox * m_Parent->m_SliceSpacing[i]; hotzone = true; } else if(xClick[i] > zmax) { // The speed is based on how close to the border we are double relspeed = (xClick[i] - zmax) / (ymax - zmax); int speedidx = std::min((int) (relspeed * 2.0), 2); int pixspeed = pixel_speeds[speedidx]; // We want to move 5 screen pixels every 0.1 sec. How many voxels is that int nvox = std::max(min_voxel_speeds[speedidx], (int) (0.5 + pixspeed / (m_Parent->m_ViewZoom * m_Parent->m_SliceSpacing[i]))); // Move the cursor left by 1 voxel newViewPos[i] += nvox * m_Parent->m_SliceSpacing[i]; hotzone = true; } } // Hotzone is disabled when whole slice is visible if(m_Parent->m_ViewZoom <= m_Parent->m_OptimalZoom) hotzone = false; // Check apperarance settings if(!m_ParentUI->GetAppearanceSettings()->GetFlagAutoPan()) hotzone = false; // Now we are ready to do something about the hotzome if(hotzone) { // If we were in the hotzone less that 100 ms ago, forget it static FLTKEventTimeStamp last_hotzone_action; FLTKEventTimeStamp tsnow; if(tsnow.ElapsedUSecFrom(last_hotzone_action) > 100000) { m_Parent->SetViewPosition(newViewPos); m_ParentUI->OnViewPositionsUpdate(true); last_hotzone_action = tsnow; } // Even if we were too soon for a hotzone, schedule the check in a few ms Fl::add_timeout(0.02, CrosshairsInteractionMode::TimeoutCallback, this); } xClick = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); UpdateCrosshairs(xClick); } int CrosshairsInteractionMode ::OnKeyDown(const FLTKEvent &event) { // Must have the cursor inside the window to process key events if(!m_Parent->GetCanvas()->GetFocus()) return 0; // Vector encoding the movement in pixels Vector3f xMotion(0.0f); // Check the shift state float xStep = (event.State & FL_SHIFT) ? 5.0f : 1.0f; // Handle up, down, left, right, pg-up and pg-down events switch(Fl::event_key()) { case FL_Right: xMotion[0] += xStep; break; case FL_Left: xMotion[0] -= xStep; break; case FL_Down: xMotion[1] -= xStep; break; case FL_Up: xMotion[1] += xStep; break; case FL_Page_Up: xMotion[2] -= xStep; break; case FL_Page_Down: xMotion[2] += xStep; break; default: return 0; }; // Get the crosshair coordinates, in slice space Vector3f xCross = m_Parent->MapImageToSlice( to_float(m_Driver->GetCursorPosition())); // Add the motion vector UpdateCrosshairs(xCross + xMotion); // Handled! return 1; } void CrosshairsInteractionMode:: OnDraw() { // Do the painting only if necessary if(m_NeedToRepaintControls) { // Update the image probe and scrollbar controls m_ParentUI->OnCrosshairPositionUpdate(false); // No need to call this until another update m_NeedToRepaintControls = false; } if(m_NeedUIUpdateOnRepaint) { m_ParentUI->OnZoomUpdate(); m_NeedUIUpdateOnRepaint = false; } // Get the line color, thickness and dash spacing for the crosshairs SNAPAppearanceSettings::Element elt = m_Parent->m_ThumbnailIsDrawing ? m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::CROSSHAIRS_THUMB) : m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::CROSSHAIRS); // Exit if the crosshars are not drawn if(!elt.Visible) return; // Get the current cursor 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 = m_Parent->MapImageToSlice(xCursorImage); // Upper and lober bounds to which the crosshairs are drawn Vector2i lower(0); Vector2i upper = m_Parent->m_SliceSize.extract(2); // Set line properties glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); // Apply the line properties; thick line is only applied in zoom thumbnail (?) SNAPAppearanceSettings::ApplyUIElementLineSettings(elt); // Apply the color glColor3dv(elt.NormalColor.data_block()); // Refit matrix so that the lines are centered on the current pixel glPushMatrix(); glTranslated( xCursorSlice(0), xCursorSlice(1), 0.0 ); // Paint the cross-hairs glBegin(GL_LINES); glVertex2f(0, 0); glVertex2f(lower(0) - xCursorSlice(0), 0); glVertex2f(0, 0); glVertex2f(upper(0) - xCursorSlice(0), 0); glVertex2f(0, 0); glVertex2f(0, lower(1) - xCursorSlice(1)); glVertex2f(0, 0); glVertex2f(0, upper(1) - xCursorSlice(1)); glEnd(); glPopMatrix(); glPopAttrib(); } itksnap/UserInterface/SliceWindow/CrosshairsInteractionMode.h0000644000076500000240000000652011455103175024035 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: CrosshairsInteractionMode.h,v $ Language: C++ Date: $Date: 2010/10/12 16:02:05 $ Version: $Revision: 1.6 $ 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 __CrosshairsInteractionMode_h_ #define __CrosshairsInteractionMode_h_ #include "GenericSliceWindow.h" #include "GlobalState.h" /** * \class CrosshairsInteractionMode * \brief UI interaction mode that takes care of crosshair positioning. * * \see GenericSliceWindow */ class CrosshairsInteractionMode : public GenericSliceWindow::EventHandler { public: /* Enum used to specify what mouse buttons different navigation functionality responds to */ enum Button { LEFT=FL_LEFT_MOUSE, RIGHT=FL_RIGHT_MOUSE, MIDDLE=FL_MIDDLE_MOUSE, NONE=-1, ANY=-2 }; CrosshairsInteractionMode( GenericSliceWindow *parent, Button cursor_button = LEFT, Button zoom_button = RIGHT, Button pan_button = MIDDLE); void SetInteractionStyle(Button cursor_button, Button zoom_button, Button pan_button) { m_BtnCursor = cursor_button; m_BtnZoom = zoom_button; m_BtnPan = pan_button; } void OnDraw(); int OnMousePress(const FLTKEvent &event); int OnMouseWheel(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)); int OnKeyDown(const FLTKEvent &event); private: void UpdateCrosshairs(const FLTKEvent &event); void UpdateCrosshairs(const Vector3f &xCross); // Whether or not we need to repaint the controls that depend on // the current slice position bool m_NeedToRepaintControls; FLTKEvent m_RepeatEvent; long int m_LastViewposUpdateTime; // Buttons assigned to each of the functionalities Button m_BtnCursor, m_BtnZoom, m_BtnPan; // Copied from zoom-pan. All your base are belong to us. Vector2f m_StartViewPosition; float m_StartViewZoom; bool m_NeedUIUpdateOnRepaint; static void TimeoutCallback(void *); }; #endif // __CrosshairsInteractionMode_h_ itksnap/UserInterface/SliceWindow/CVS/0000755000076500000240000000000011560342171017165 5ustar paulystaffitksnap/UserInterface/SliceWindow/CVS/Entries0000644000076500000240000000356711560342171020534 0ustar paulystaff/AnnotationInteractionMode.cxx/1.13/Thu Feb 12 00:13:06 2009// /AnnotationInteractionMode.h/1.6/Tue Feb 10 16:50:05 2009// /BubblesInteractionMode.cxx/1.3/Sun Feb 10 23:55:22 2008// /BubblesInteractionMode.h/1.2/Sun Dec 30 04:05:27 2007// /CrosshairsInteractionMode.cxx/1.13/Tue Oct 19 20:28:56 2010// /CrosshairsInteractionMode.h/1.6/Tue Oct 12 16:02:05 2010// /GLToPNG.cxx/1.4/Sat May 1 21:29:32 2010// /GLToPNG.h/1.2/Sun Dec 30 04:05:28 2007// /GenericSliceWindow.cxx/1.36/Wed Oct 13 16:59:25 2010// /GenericSliceWindow.h/1.28/Tue Oct 12 17:57:11 2010// /IRISSliceWindow.cxx/1.11/Mon Jun 28 18:45:08 2010// /IRISSliceWindow.h/1.7/Mon Oct 26 16:00:56 2009// /OpenGLSliceTexture.cxx/1.2/Tue Aug 25 19:44:25 2009// /OpenGLSliceTexture.h/1.9/Tue Aug 25 19:44:25 2009// /PaintbrushInteractionMode.cxx/1.14/Sat Jan 31 09:02:50 2009// /PaintbrushInteractionMode.h/1.6/Tue Dec 2 21:43:24 2008// /PolygonDrawing.cxx/1.15/Wed Oct 13 17:01:08 2010// /PolygonDrawing.h/1.7/Thu May 27 11:16:22 2010// /PolygonInteractionMode.cxx/1.8/Thu May 27 07:29:36 2010// /PolygonInteractionMode.h/1.3/Sun Dec 30 04:43:03 2007// /PolygonScanConvert.cxx/1.8/Fri Jan 23 20:09:38 2009// /PolygonScanConvert.h/1.3/Fri Jan 23 20:09:38 2009// /PopupButtonInteractionMode.cxx/1.2/Tue Oct 12 16:02:05 2010// /PopupButtonInteractionMode.h/1.1/Thu May 27 07:29:36 2010// /RegionInteractionMode.cxx/1.3/Sun Feb 10 23:55:22 2008// /RegionInteractionMode.h/1.2/Sun Dec 30 04:05:28 2007// /SNAPSliceWindow.cxx/1.7/Tue Aug 25 21:38:16 2009// /SNAPSliceWindow.h/1.5/Tue Aug 25 19:46:18 2009// /SliceWindowCoordinator.cxx/1.5/Tue Oct 12 17:57:11 2010// /SliceWindowCoordinator.h/1.5/Mon May 4 20:15:57 2009// /ThumbnailInteractionMode.cxx/1.3/Tue Jun 16 19:05:37 2009// /ThumbnailInteractionMode.h/1.2/Sun Dec 30 04:05:29 2007// /ZoomPanInteractionMode.cxx/1.3/Mon May 4 20:15:57 2009// /ZoomPanInteractionMode.h/1.2/Sun Dec 30 04:05:29 2007// D itksnap/UserInterface/SliceWindow/CVS/Repository0000644000076500000240000000004211560342171021263 0ustar paulystaffitksnap/UserInterface/SliceWindow itksnap/UserInterface/SliceWindow/CVS/Root0000644000076500000240000000010011560342171020022 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/SliceWindow/GenericSliceWindow.cxx0000644000076500000240000006411211455362555023021 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GenericSliceWindow.cxx,v $ Language: C++ Date: $Date: 2010/10/13 16:59:25 $ Version: $Revision: 1.36 $ 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 "GenericSliceWindow.h" #include "CrosshairsInteractionMode.h" #include "GlobalState.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "OpenGLSliceTexture.h" #include "SliceWindowCoordinator.h" #include "SNAPAppearanceSettings.h" #include "UserInterfaceBase.h" #include "ThumbnailInteractionMode.h" #include "PopupButtonInteractionMode.h" #include #include #include #include #include #include "itkConstantPadImageFilter.h" using namespace std; GenericSliceWindow ::GenericSliceWindow(int index, UserInterfaceBase *ui, FLTKCanvas *canvas) : RecursiveInteractionMode(canvas) { // Copy parent pointers m_ParentUI = ui; m_Driver = m_ParentUI->GetDriver(); m_GlobalState = m_Driver->GetGlobalState(); // Set the window ID m_Id = index; // Initialize the interaction modes m_NavigationMode = new CrosshairsInteractionMode(this); m_ThumbnailMode = new ThumbnailInteractionMode(this); m_PopupButtonMode = new PopupButtonInteractionMode(this); // The slice is not yet initialized m_IsSliceInitialized = false; // Initialize the Main image slice texture m_MainTexture = new OpenGLSliceTexture(4, GL_RGBA); // Initialize the Segmentation slice texture m_LabelRGBTexture = new OpenGLSliceTexture(4, GL_RGBA); // Initalize the margin m_Margin = 2; // Initialize the zoom management m_ManagedZoom = false; // No thumbnail m_ThumbnailIsDrawing = false; // Allow focus grabbing m_Canvas->SetGrabFocusOnEntry(true); // Register the sub-interaction modes m_NavigationMode->Register(); m_ThumbnailMode->Register(); m_PopupButtonMode->Register(); // We have been registered m_IsRegistered = true; } GenericSliceWindow ::~GenericSliceWindow() { // Delete the interaction modes delete m_NavigationMode; delete m_ThumbnailMode; delete m_PopupButtonMode; // Delete textures delete m_MainTexture; delete m_LabelRGBTexture; } void GenericSliceWindow ::InitializeSlice(GenericImageData *imageData) { // Register should have been called already assert(m_IsRegistered); // Store the image data pointer m_ImageData = imageData; // The main image should be loaded if (imageData->IsMainLoaded()) { // Initialize the Main image slice texture m_MainTexture->SetImage( m_ImageData->GetMain()->GetDisplaySlice(m_Id).GetPointer()); } else { // If not m_IsSliceInitialized = false; ClearInteractionStack(); return; } // Initialize the segmentation slice texture if (imageData->IsSegmentationLoaded()) { m_LabelRGBTexture->SetImage( m_ImageData->GetSegmentation()->GetDisplaySlice(m_Id).GetPointer()); } // Initialize overlay slice InitializeOverlaySlice(imageData); // 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? } // No information about the current slice available yet m_ImageSliceIndex = -1; m_DisplayAxisPosition = 0.0f; // We have been initialized m_IsSliceInitialized = true; // If the is no current interaction mode, enter the crosshairs mode if(GetInteractionModeCount() == 0) PushInteractionMode(m_NavigationMode); // setup default view - fit to window ResetViewToFit(); } void GenericSliceWindow ::InitializeOverlaySlice(GenericImageData *imageData) { // Register should have been called already assert(m_IsRegistered); // The main image should be loaded if (!imageData->IsMainLoaded()) { // If not m_IsSliceInitialized = false; ClearInteractionStack(); return; } // Store the image data pointer m_ImageData = imageData; // Clear the overlay texture list while (m_OverlayTextureList.size() > 0) { delete m_OverlayTextureList.front(); m_OverlayTextureList.pop_front(); } // Initialize the overlay slice texture if (imageData->IsOverlayLoaded()) { std::list::iterator it = m_ImageData->GetOverlays()->begin(); while (it != m_ImageData->GetOverlays()->end()) { OpenGLSliceTexture *texture = new OpenGLSliceTexture(4, GL_RGBA); texture->SetImage((*it)->GetDisplaySlice(m_Id).GetPointer()); m_OverlayTextureList.push_back(texture); it++; } } } void GenericSliceWindow ::ComputeOptimalZoom() { // Should be fully initialized assert(m_IsRegistered && m_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_ViewPosition = worldSize * 0.5f; // Reduce the width and height of the slice by the margin Vector2i szCanvas = Vector2i(m_Canvas->w(),m_Canvas->h()) - Vector2i(2 * m_Margin); // 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(); } Vector2i GenericSliceWindow ::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 GenericSliceWindow ::ResetViewToFit() { // Should be fully initialized assert(m_IsRegistered && m_IsSliceInitialized); // Compute slice size in spatial coordinates ComputeOptimalZoom(); // The zoom factor is the bigger of these ratios, the number of pixels // on the screen per millimeter in world space m_ViewZoom = m_OptimalZoom; // Cause a redraw of the window m_Canvas->redraw(); } Vector3f GenericSliceWindow ::MapSliceToImage(const Vector3f &xSlice) { assert(m_IsSliceInitialized); // Get corresponding position in display space return m_DisplayToImageTransform.TransformPoint(xSlice); } /** * Map a point in image coordinates to slice coordinates */ Vector3f GenericSliceWindow ::MapImageToSlice(const Vector3f &xImage) { assert(m_IsSliceInitialized); // Get corresponding position in display space return m_ImageToDisplayTransform.TransformPoint(xImage); } Vector2f GenericSliceWindow ::MapSliceToWindow(const Vector3f &xSlice) { assert(m_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 Vector2f uvWindow = m_ViewZoom * (uvScaled - m_ViewPosition) + Vector2f(0.5f * m_Canvas->w(),0.5f * m_Canvas->h()); // That's it, the projection matrix is set up in the scaled-slice coordinates return uvWindow; } Vector3f GenericSliceWindow ::MapWindowToSlice(const Vector2f &uvWindow) { assert(m_IsSliceInitialized && m_ViewZoom > 0); // Compute the scaled slice coordinates Vector2f winCenter(0.5f * m_Canvas->w(),0.5f * m_Canvas->h()); 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), m_DisplayAxisPosition); // Return this vector return uvSlice; } Vector2f GenericSliceWindow ::MapSliceToPhysicalWindow(const Vector3f &xSlice) { assert(m_IsSliceInitialized); // Compute the physical window coordinates Vector2f uvPhysical; uvPhysical[0] = xSlice[0] * m_SliceSpacing[0]; uvPhysical[1] = xSlice[1] * m_SliceSpacing[1]; return uvPhysical; } void GenericSliceWindow ::OnDraw() { // Set up the projection if necessary if(!m_Canvas->valid()) { // The window will have coordinates (0,0) to (w,h), i.e. the same as the window // coordinates. glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,m_Canvas->w(),0.0,m_Canvas->h()); glViewport(0,0,m_Canvas->w(),m_Canvas->h()); // Establish the model view matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Compute the optimal zoom if(m_IsRegistered && m_IsSliceInitialized) { // If the zoom is set to fit, maintain the fit, otherwise, maintain the // optimal zoom level if(!m_ManagedZoom && m_ViewZoom == m_OptimalZoom) { ComputeOptimalZoom(); m_ViewZoom = m_OptimalZoom; } else { ComputeOptimalZoom(); } } } // Get the properties for the background color Vector3d clrBack = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::BACKGROUND_2D).NormalColor; // Clear the display, using a blue shade when under focus glClearColor(clrBack[0],clrBack[1],clrBack[2],1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Slice should be initialized before display if (!m_IsSliceInitialized) return; // Compute the position of the cross-hairs in display space Vector3ui cursorImageSpace = m_Driver->GetCursorPosition(); Vector3f cursorDisplaySpace = m_ImageToDisplayTransform.TransformPoint( to_float(cursorImageSpace) + Vector3f(0.5f)); // Get the current slice number m_ImageSliceIndex = cursorImageSpace[m_ImageAxes[2]]; m_DisplayAxisPosition = cursorDisplaySpace[2]; // Set up lighting attributes glPushAttrib(GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT | GL_PIXEL_MODE_BIT | GL_TEXTURE_BIT ); glDisable(GL_LIGHTING); // glDisable(GL_DEPTH); // Prepare for overlay drawing. The model view is set up to correspond // to pixel coordinates of the slice glPushMatrix(); glTranslated(0.5 * m_Canvas->w(),0.5 * m_Canvas->h(),0.0); glScalef(m_ViewZoom,m_ViewZoom,1.0); glTranslated(-m_ViewPosition(0),-m_ViewPosition(1),0.0); glScalef(m_SliceSpacing[0],m_SliceSpacing[1],1.0); // Make the grey and segmentation image textures up-to-date DrawMainTexture(); DrawOverlayTexture(); DrawSegmentationTexture(); // Draw the overlays if(m_ParentUI->GetAppearanceSettings()->GetOverallVisibility()) { DrawOverlays(); // Draw the zoom locator if(IsThumbnailOn()) DrawThumbnail(); } // Clean up the GL state glPopMatrix(); glPopAttrib(); // Draw the little popup button // m_PopupButtonMode->OnDraw(); // Display! glFlush(); } void GenericSliceWindow ::DrawMainTexture() { // We should have a slice to return assert(m_ImageSliceIndex >= 0); if (m_ImageData->IsMainLoaded()) { // Get the color to use for background Vector3d clrBackground = m_ThumbnailIsDrawing ? m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::ZOOM_THUMBNAIL).NormalColor : Vector3d(1.0); // Set the interpolation mode to current default m_MainTexture->SetInterpolation( m_ParentUI->GetAppearanceSettings()->GetGreyInterpolationMode() == SNAPAppearanceSettings::LINEAR ? GL_LINEAR : GL_NEAREST); // Paint the grey texture with color as background m_MainTexture->Draw(clrBackground); } } void GenericSliceWindow ::DrawOverlayTexture() { // We should have a slice to return assert(m_ImageSliceIndex >= 0); // Get the color to use for background Vector3d clrBackground = m_ThumbnailIsDrawing ? m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::ZOOM_THUMBNAIL).NormalColor : Vector3d(1.0); OverlayTextureIterator textureIt = m_OverlayTextureList.begin(); std::list::iterator wrapperIt = m_ImageData->GetOverlays()->begin(); while (textureIt != m_OverlayTextureList.end()) { // Set the interpolation mode to current default OpenGLSliceTexture *texture = *textureIt; ImageWrapperBase *wrapper = *wrapperIt; texture->SetInterpolation( m_ParentUI->GetAppearanceSettings()->GetGreyInterpolationMode() == SNAPAppearanceSettings::LINEAR ? GL_LINEAR : GL_NEAREST); texture->DrawTransparent(wrapper->GetAlpha()); textureIt++; wrapperIt++; } } void GenericSliceWindow ::DrawSegmentationTexture() { // We should have a slice to return assert(m_ImageSliceIndex >= 0); if (m_ImageData->IsSegmentationLoaded()) { m_LabelRGBTexture->DrawTransparent(m_GlobalState->GetSegmentationAlpha()); } } void GenericSliceWindow ::DrawThumbnail() { // Get the thumbnail appearance properties const SNAPAppearanceSettings::Element &elt = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::ZOOM_THUMBNAIL); // If thumbnail is not to be drawn, exit if(!elt.Visible) return; // Indicate the fact that we are currently drawing in thumbnail mode m_ThumbnailIsDrawing = true; // The dimensions of the canvas on which we are working, in pixels Vector2i xCanvas( m_Canvas->w(), m_Canvas->h() ); // The thumbnail will occupy a specified fraction of the target canvas float xFraction = 0.01f * m_ParentUI->GetAppearanceSettings()->GetZoomThumbnailSizeInPercent(); // But it must not exceed a predefined size in pixels in either dimension float xThumbMax = m_ParentUI->GetAppearanceSettings()->GetZoomThumbnailMaximumSize(); // Recompute the fraction based on maximum size restriction float xNewFraction = xFraction; if( xCanvas[0] * xNewFraction > xThumbMax ) xNewFraction = xThumbMax * 1.0f / xCanvas[0]; if( xCanvas[1] * xNewFraction > xThumbMax ) xNewFraction = xThumbMax * 1.0f / xCanvas[1]; // Draw the little version of the image in the corner of the window double w = m_SliceSize[0]; double h = m_SliceSize[1]; // Set the position and size of the thumbnail, in pixels m_ThumbnailZoom = xNewFraction * m_OptimalZoom; m_ThumbnailPosition.fill(5); m_ThumbnailSize[0] = (int)(m_SliceSize[0] * m_SliceSpacing[0] * m_ThumbnailZoom); m_ThumbnailSize[1] = (int)(m_SliceSize[1] * m_SliceSpacing[1] * m_ThumbnailZoom); glPushMatrix(); glLoadIdentity(); glTranslated((double) m_ThumbnailPosition[0], (double) m_ThumbnailPosition[1], 0.0); glScaled(m_ThumbnailZoom, m_ThumbnailZoom, 1.0); glPushMatrix(); glScalef(m_SliceSpacing[0],m_SliceSpacing[1],1.0); // glTranslated(w * 0.1111, h * 0.1111, 0.0); // Draw the Main image (the background will be picked automatically) DrawMainTexture(); // Draw the crosshairs and stuff DrawOverlays(); // Apply the line settings SNAPAppearanceSettings::ApplyUIElementLineSettings(elt); // Draw the line around the image glColor3dv(elt.NormalColor.data_block()); glBegin(GL_LINE_LOOP); glVertex2d(0,0); glVertex2d(0,h); glVertex2d(w,h); glVertex2d(w,0); glEnd(); // Draw a box representing the current zoom level glPopMatrix(); glTranslated(m_ViewPosition[0],m_ViewPosition[1],0.0); w = m_Canvas->w() * 0.5 / m_ViewZoom; h = m_Canvas->h() * 0.5 / m_ViewZoom; glColor3dv(elt.ActiveColor.data_block()); glBegin(GL_LINE_LOOP); glVertex2d(-w,-h); glVertex2d(-w, h); glVertex2d( w, h); glVertex2d( w,-h); glEnd(); glPopMatrix(); // Indicate the fact that we are not drawing in thumbnail mode m_ThumbnailIsDrawing = false; } void GenericSliceWindow ::DrawOverlays() { if(!m_ThumbnailIsDrawing) { // Display the letters (RAI) DrawOrientationLabels(); // Display the rulers DrawRulers(); // Draw the zoom mode (does't really draw, repaints a UI widget) m_NavigationMode->OnDraw(); } m_NavigationMode->OnDraw(); } void GenericSliceWindow ::DrawOrientationLabels() { // The letter labels static const char *letters[3][2] = {{"R","L"},{"A","P"},{"I","S"}}; const char *labels[2][2]; // Get the properties for the labels const SNAPAppearanceSettings::Element &elt = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::MARKERS); // Leave if the labels are disabled if(!elt.Visible) return; // Repeat for X and Y directions for(unsigned int i=0;i<2;i++) { // Which axis are we on in anatomy space? unsigned int anatomyAxis = m_DisplayToAnatomyTransform.GetCoordinateIndexZeroBased(i); // Which direction is the axis facing (returns -1 or 1) unsigned int anatomyAxisDirection = m_DisplayToAnatomyTransform.GetCoordinateOrientation(i); // Map the direction onto 0 or 1 unsigned int letterIndex = (1 + anatomyAxisDirection) >> 1; // Compute the two labels for this axis labels[i][0] = letters[anatomyAxis][1-letterIndex]; labels[i][1] = letters[anatomyAxis][letterIndex]; } glPushAttrib(GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glLoadIdentity(); if(elt.AlphaBlending) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } glColor4d( elt.NormalColor[0], elt.NormalColor[1], elt.NormalColor[2], 1.0 ); gl_font(FL_COURIER_BOLD, elt.FontSize); int offset = 4 + elt.FontSize * 2; int margin = elt.FontSize / 3; int w = m_Canvas->w(), h = m_Canvas->h(); gl_draw(labels[0][0],margin,0,offset,h,FL_ALIGN_LEFT); gl_draw(labels[0][1],w - (offset+margin),0,offset,h,FL_ALIGN_RIGHT); gl_draw(labels[1][0],0,0,w,offset,FL_ALIGN_BOTTOM); gl_draw(labels[1][1],0,h - (offset+1),w,offset,FL_ALIGN_TOP); glPopMatrix(); glPopAttrib(); } void GenericSliceWindow ::DrawRulers() { // Get the properties for the labels const SNAPAppearanceSettings::Element &elt = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::RULER); // Leave if the labels are disabled if(!elt.Visible) return; glPushAttrib(GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glLoadIdentity(); SNAPAppearanceSettings::ApplyUIElementLineSettings(elt); glColor4d( elt.NormalColor[0], elt.NormalColor[1], elt.NormalColor[2], 1.0 ); gl_font(FL_HELVETICA, elt.FontSize); // Pick the scale of the ruler int w = m_Canvas->w(), h = m_Canvas->h(); // The ruler bar should be as large as possible but less than one half // of the screen width (not to go over the markers) double maxw = 0.5 * w - 20.0; maxw = maxw < 5 ? 5 : maxw; double scale = 1.0; while(m_ViewZoom * scale > maxw) scale /= 10.0; while(m_ViewZoom * scale < 0.1 * maxw) scale *= 10.0; // Draw a zoom bar double bw = scale * m_ViewZoom; glBegin(GL_LINES); glVertex2d(5,h - 5); glVertex2d(5,h - 20); glVertex2d(5,h - 10); glVertex2d(5 + bw,h - 10); glVertex2d(5 + bw,h - 5); glVertex2d(5 + bw,h - 20); glEnd(); // Based on the log of the scale, determine the unit string unit = "mm"; if(scale >= 10 && scale < 1000) { unit = "cm"; scale /= 10; } else if(scale >= 1000) { unit = "m"; scale /= 1000; } else if(scale < 1 && scale > 0.001) { unit = "\xb5m"; scale *= 1000; } else if(scale < 0.001) { unit = "nm"; scale *= 1000000; } ostringstream oss; oss << scale << " " << unit; // See if we can squeeze the label under the ruler if(bw > elt.FontSize * 4) gl_draw(oss.str().c_str(), 10, h - 30, (int) bw, 20, FL_ALIGN_TOP); else gl_draw(oss.str().c_str(), (int) bw+10, h - 20, (int) bw + elt.FontSize * 4+10, 15, FL_ALIGN_LEFT); glPopMatrix(); glPopAttrib(); } void GenericSliceWindow ::EnterInteractionMode(InteractionMode *mode) { // Empty the stack ClearInteractionStack(); // Push the crosshairs mode - last to get events if(mode != m_NavigationMode) { // Navigation mode serves as a fallback, allowing crosshair editing with middle button m_NavigationMode->SetInteractionStyle( CrosshairsInteractionMode::ANY, CrosshairsInteractionMode::NONE, CrosshairsInteractionMode::NONE); PushInteractionMode(m_NavigationMode); } // Push the input mode PushInteractionMode(mode); // Push the thumbnail mode PushInteractionMode(m_ThumbnailMode); // Push the popup mode // PushInteractionMode(m_PopupButtonMode); } void GenericSliceWindow ::EnterCrosshairsMode() { m_NavigationMode->SetInteractionStyle( CrosshairsInteractionMode::LEFT, CrosshairsInteractionMode::RIGHT, CrosshairsInteractionMode::MIDDLE); EnterInteractionMode(m_NavigationMode); } void GenericSliceWindow ::EnterZoomPanMode() { m_NavigationMode->SetInteractionStyle( CrosshairsInteractionMode::MIDDLE, CrosshairsInteractionMode::RIGHT, CrosshairsInteractionMode::LEFT); EnterInteractionMode(m_NavigationMode); } void GenericSliceWindow ::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; m_Canvas->redraw(); } void GenericSliceWindow ::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 GenericSliceWindow ::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 GenericSliceWindow ::SetViewZoom(float newZoom) { // Update the zoom m_ViewZoom = newZoom; // Repaint the window m_Canvas->redraw(); } GenericSliceWindow * GenericSliceWindow ::GetNextSliceWindow() { SliceWindowCoordinator *swc = m_ParentUI->GetSliceCoordinator(); return swc->GetWindow( (m_Id+1) % 3); } bool GenericSliceWindow ::IsThumbnailOn() { return m_ParentUI->GetAppearanceSettings()->GetFlagDisplayZoomThumbnail() && m_ViewZoom > m_OptimalZoom; } 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; } itksnap/UserInterface/SliceWindow/GenericSliceWindow.h0000644000076500000240000002677011455120567022451 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GenericSliceWindow.h,v $ Language: C++ Date: $Date: 2010/10/12 17:57:11 $ Version: $Revision: 1.28 $ 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 __GenericSliceWindow_h_ #define __GenericSliceWindow_h_ #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #pragma warning ( disable : 4503 ) #endif #include "FLTKCanvas.h" #include "SNAPCommonUI.h" #include "GreyImageWrapper.h" #include "RGBImageWrapper.h" #include "LabelImageWrapper.h" #include "RecursiveInteractionMode.h" // Forward references to parent classes class IRISApplication; class GenericImageData; class GlobalState; class UserInterfaceBase; // Forward references to interaction modes that work with this window class CrosshairsInteractionMode; class ThumbnailInteractionMode; class PopupButtonInteractionMode; // Forward reference to Gl texture object class OpenGLSliceTexture; /** * \class GenericSliceWindow * \brief A window used to display a 2D slice either in SNAP or in IRIS mode. * * A generic slice window, that is neither fitted to IRIS nor to SNAP. The * IRIS and SNAP windows are children of this slice window class. This class * provides support for display space to image space transforms, for texture * display and management, and for interaction mode plugins. * * The generic window supports two types of interaction modes: crosshairs mode * and zoom/pan mode. */ class GenericSliceWindow : public RecursiveInteractionMode { public: /** * Register the window with its parent UI. This method assigns an Id to * the window, which is equal to the coordinate direction in display space * along which the window displays slices. */ GenericSliceWindow(int id, UserInterfaceBase *parentUI, FLTKCanvas *inWindow); virtual ~GenericSliceWindow(); /** Enter the cross-hairs mode of operation */ virtual void EnterCrosshairsMode(); /** Enter the zoom/pan mode of operation */ virtual void EnterZoomPanMode(); /** * Initialize the window's attributes (size, view position, etc.) * This method should called when the image dimensions and transforms * get updated. */ virtual void InitializeSlice(GenericImageData *imageData); virtual void InitializeOverlaySlice(GenericImageData *imageData); /** * Reset the view parameters of the window (zoom, view position) to * defaults */ virtual void ResetViewToFit(); /** The FLTK draw method (paints the window) */ void OnDraw(); /** Respond to drag and drop events */ int OnDragAndDrop(const FLTKEvent &event); /** * 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 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 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); /** 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 (); /** Set the zoom factor (number of pixels on the screen per millimeter in * image space */ void SetViewZoom(float newZoom); /** 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); /** Get the zoom factor (number of pixels on the screen per millimeter in * image space */ irisGetMacro(ViewZoom,float); /** Compute the optimal zoom (best fit) */ irisGetMacro(OptimalZoom,float); /** Set the zoom management flag */ irisSetMacro(ManagedZoom,bool); /** Set the view position **/ irisSetMacro(ViewPosition, 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); irisGetMacro(Id, int); irisGetMacro(ParentUI, UserInterfaceBase *); /** Compute the canvas size needed to display slice at current zoom factor */ Vector2i GetOptimalCanvasSize(); /** * A parent class from which all the Fl event handlers associated * with this class should be derived */ class EventHandler : public InteractionMode { public: EventHandler(GenericSliceWindow *parent); void Register(); protected: GenericSliceWindow *m_Parent; GlobalState *m_GlobalState; UserInterfaceBase *m_ParentUI; IRISApplication *m_Driver; }; // Allow friendly access by interactors friend class EventHandler; friend class BubblesInteractionMode; friend class CrosshairsInteractionMode; friend class RegionInteractionMode; friend class PolygonInteractionMode; friend class ThumbnailInteractionMode; friend class PaintbrushInteractionMode; friend class AnnotationInteractionMode; protected: /** Pointer to the application driver for this UI object */ IRISApplication *m_Driver; /** Pointer to the global state object (shorthand) */ GlobalState *m_GlobalState; /** Pointer to GUI that contains this Window3D object */ UserInterfaceBase *m_ParentUI; /** The image data object that is displayed in this window */ GenericImageData *m_ImageData; /** Interaction mode used to position the crosshairs (navigation mode) */ CrosshairsInteractionMode *m_NavigationMode; /** Interaction mode used to control the zoom thumbnail. This mode is always * on and at the top of the interaction stack */ ThumbnailInteractionMode *m_ThumbnailMode; /** Interaction mode used to bring up a popup menu */ PopupButtonInteractionMode *m_PopupButtonMode; /** Whether or not we have been registered with the parent UI */ bool m_IsRegistered; /** * Whether or not the sizes have been initialized * (synched with the image data) */ bool m_IsSliceInitialized; // Window id, equal to the direction in display space along which the window // shows slices int m_Id; // Current slice number in image coordinates int m_ImageSliceIndex; // The position of the slice on its z-axis, in the display coordinate space float m_DisplayAxisPosition; // 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 thirs component is the pixel width // in the slice direction) Vector3f m_SliceSpacing; // Position of visible window in slice space coordinates Vector2f m_ViewPosition; // 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 lest in // default zoom unsigned int m_Margin; // Main image texture object OpenGLSliceTexture *m_MainTexture; // Overlay texture objects typedef std::list OverlayTextureList; typedef OverlayTextureList::iterator OverlayTextureIterator; typedef OverlayTextureList::const_iterator OverlayTextureConstIterator; OverlayTextureList m_OverlayTextureList; // Label texture object OpenGLSliceTexture *m_LabelRGBTexture; // Check whether the thumbnail should be draw or not bool IsThumbnailOn(); // The position and size of the zoom thumbnail Vector2i m_ThumbnailPosition, m_ThumbnailSize; // Whether or not the thumbnail is being drawn at the moment bool m_ThumbnailIsDrawing; // The zoom level in the thumbnail double m_ThumbnailZoom; // Computes the zoom that gives the best fit for the window void ComputeOptimalZoom(); // This method is called in draw() to paint the Main image slice virtual void DrawMainTexture(); // This method is called in draw() to paint the overlay slice virtual void DrawOverlayTexture(); // This method is called in draw() to paint the segmentation slice virtual void DrawSegmentationTexture(); // This method is called after the grey and segmentation images have // been drawn. It calls the draw method of each of the interaction modes virtual void DrawOverlays(); /** This method draws the RAI labels at the four sides of the slice */ void DrawOrientationLabels(); /** Draw a ruler for seeing distances on the screen */ void DrawRulers(); /** Draw a window that shows where in the image the zoom region is located */ void DrawThumbnail(); /** Access the next window in the slice pipeline */ GenericSliceWindow *GetNextSliceWindow(); /** Activate a given interaction mode */ virtual void EnterInteractionMode(InteractionMode *mode); }; #endif // __GenericSliceWindow_h_ itksnap/UserInterface/SliceWindow/GLToPNG.cxx0000644000076500000240000000553111367116474020446 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GLToPNG.cxx,v $ Language: C++ Date: $Date: 2010/05/01 21:29:32 $ 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 "GLToPNG.h" #include using namespace std; vtkImageData* GLToVTKImageData(unsigned int format, int x, int y, int w, int h) { // Cast the format to GL enum GLenum glformat = (GLenum) format; // OSX does double buffer automatically (???) //glReadBuffer(GL_BACK); unsigned int GL_comps = 0; if (glformat == GL_RGBA) { GL_comps = 4; } else if (glformat == GL_RGB) { GL_comps = 3; } else { std::cerr << "Invalid GLenum" << endl; exit(1); } unsigned char* pixmap = new unsigned char[w*h*GL_comps]; glReadPixels(x, y, w, h, format, GL_UNSIGNED_BYTE, pixmap); // convert to vtkImageData vtkImageData* img = vtkImageData::New(); if (format == GL_RGBA) { img->SetExtent(0, w-1, 0, h-1, 0, 0); } else if (format == GL_RGB) { img->SetExtent(0, w, 0, h, 0, 0); } img->SetSpacing(1.0, 1.0, 1.0); img->SetOrigin(0.0, 0.0, 0.0); img->SetNumberOfScalarComponents(GL_comps); img->SetScalarType(VTK_UNSIGNED_CHAR); int rowSize = w*GL_comps; unsigned char* pixmap2 = pixmap; unsigned char* imgPtr = (unsigned char*) img->GetScalarPointer(0, 0, 0); for (int i = 0; i < h; ++i) { memcpy(imgPtr, pixmap2, rowSize); imgPtr += rowSize; pixmap2 += rowSize; } delete[] pixmap; return img; } void VTKImageDataToPNG(vtkImageData* img, const char* filename) { vtkPNGWriter* pngw = vtkPNGWriter::New(); pngw->SetInput(img); pngw->SetFileName(filename); pngw->Write(); pngw->Delete(); } itksnap/UserInterface/SliceWindow/GLToPNG.h0000644000076500000240000000351310735614410020060 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: GLToPNG.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:28 $ 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 __GLToPNG_h_ #define __GLToPNG_h_ #include /** * * Simple Functions that convert GL buffer into vtkImageData * and save the vtkImageData as PNG file * * 01/25/2004 * Hui Gary Zhang * */ #include "vtkImageData.h" #include "vtkPNGWriter.h" // convert a GL buffer into vtkImageData vtkImageData* GLToVTKImageData(unsigned int format, int x, int y, int w, int h); // output vtkImageData as PNG void VTKImageDataToPNG(vtkImageData* img, const char* filename); #endif itksnap/UserInterface/SliceWindow/IRISSliceWindow.cxx0000644000076500000240000001735411412166664022215 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISSliceWindow.cxx,v $ Language: C++ Date: $Date: 2010/06/28 18:45:08 $ 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. =========================================================================*/ #include "IRISSliceWindow.h" #include "GlobalState.h" #include "OpenGLSliceTexture.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "UserInterfaceBase.h" #include "AnnotationInteractionMode.h" #include "CrosshairsInteractionMode.h" #include "PolygonInteractionMode.h" #include "PaintbrushInteractionMode.h" #include "RegionInteractionMode.h" #include #include #include #include #include #include "itkOrientedImage.h" #include "itkImageRegionIteratorWithIndex.h" using namespace std; IRISSliceWindow ::IRISSliceWindow(int id, UserInterfaceBase *parentUI, FLTKCanvas *canvas) : GenericSliceWindow(id, parentUI, canvas) { // Initialize the interaction modes m_PolygonMode = new PolygonInteractionMode(this); m_RegionMode = new RegionInteractionMode(this); m_PaintbrushMode = new PaintbrushInteractionMode(this); m_AnnotationMode = new AnnotationInteractionMode(this); // Get a pointer to the drawing object m_PolygonDrawing = m_PolygonMode->m_Drawing; m_PolygonDrawing->SetFreehandFittingRate(parentUI->GetFreehandFittingRate()); // Initialize polygon slice canvas to NULL m_PolygonSlice = NULL; // Register the interaction modes m_PolygonMode->Register(); m_RegionMode->Register(); m_PaintbrushMode->Register(); m_AnnotationMode->Register(); } IRISSliceWindow ::~IRISSliceWindow() { delete m_PolygonMode; delete m_RegionMode; delete m_AnnotationMode; delete m_PaintbrushMode; } void IRISSliceWindow ::InitializeSlice(GenericImageData *imageData) { // Call the parent's version of this method GenericSliceWindow::InitializeSlice(imageData); if(!imageData->IsMainLoaded()) return; // Reset the polygon drawing interface m_PolygonDrawing->Reset(); // Initialize the polygon drawing canvas itk::Size<2> imgSize; imgSize[0] = m_SliceSize(0); imgSize[1] = m_SliceSize(1); m_PolygonSlice = PolygonSliceType::New(); m_PolygonSlice->SetRegions(imgSize); m_PolygonSlice->Allocate(); } void IRISSliceWindow ::DrawOverlays() { // Call the parent's version of this method GenericSliceWindow::DrawOverlays(); // Draw the polygon if(!m_ThumbnailIsDrawing) m_PolygonMode->OnDraw(); // Draw the region of interest if selected if(IsInteractionModeAdded(m_RegionMode)) m_RegionMode->OnDraw(); // Draw the paintbrush stuff if selected if(IsInteractionModeAdded(m_PaintbrushMode) && !m_ThumbnailIsDrawing) m_PaintbrushMode->OnDraw(); // Always draw annotations, whether active or not - but not in the thumbnail if(!m_ThumbnailIsDrawing) m_AnnotationMode->OnDraw(); } void IRISSliceWindow ::EnterPolygonMode() { EnterInteractionMode(m_PolygonMode); } void IRISSliceWindow ::EnterPaintbrushMode() { EnterInteractionMode(m_PaintbrushMode); } void IRISSliceWindow ::EnterAnnotationMode() { EnterInteractionMode(m_AnnotationMode); } void IRISSliceWindow ::EnterRegionMode() { EnterInteractionMode(m_RegionMode); } bool IRISSliceWindow ::AcceptPolygon() { assert(m_IsRegistered && m_IsSliceInitialized); // Make sure we are in editing mode if (m_PolygonDrawing->GetState() != PolygonDrawing::EDITING_STATE) return false; #ifdef DRAWING_LOCK if (m_GlobalState->GetDrawingLock(id)) { #endif /* DRAWING_LOCK */ // VERY IMPORTANT - makes GL state current! m_Canvas->make_current(); // Have the polygon drawing object render the polygon slice m_PolygonDrawing->AcceptPolygon(m_PolygonSlice); // take polygon rendered by polygon_drawing and merge with // segmentation slice; send changes to voxel data set LabelType drawing_color = m_GlobalState->GetDrawingColorLabel(); LabelType overwrt_color = m_GlobalState->GetOverWriteColorLabel(); CoverageModeType mode = m_GlobalState->GetCoverageMode(); // Get the current slice from the segmentation image LabelImageWrapper *seg = m_Driver->GetCurrentImageData()->GetSegmentation(); LabelImageWrapper::SlicePointer slice = seg->GetSlice(m_Id); slice->Update(); // Create an iterator to iterate over the slice typedef itk::ImageRegionIteratorWithIndex PolygonIterator; PolygonIterator itPolygon(m_PolygonSlice, m_PolygonSlice->GetLargestPossibleRegion()); // Keep track of the number of pixels changed unsigned int nUpdates = 0; // Iterate for (itPolygon.Begin(); !itPolygon.IsAtEnd(); ++itPolygon) { // Get the current polygon pixel PolygonSliceType::PixelType pxPolygon = itPolygon.Get(); // Check for non-zero alpha of the pixel if((pxPolygon != 0) ^ m_GlobalState->GetPolygonInvert()) { // Get the corresponding segmentation image pixel itk::Index<2> idx = itPolygon.GetIndex(); LabelType pxLabel = slice->GetPixel(idx); // Check if we should be overriding that pixel if (mode == PAINT_OVER_ALL || (mode == PAINT_OVER_ONE && pxLabel == overwrt_color) || (mode == PAINT_OVER_COLORS && pxLabel != 0)) { // Get the index into the image that we'll be updating Vector3f idxImageFloat = MapSliceToImage(Vector3f(idx[0]+0.5f,idx[1]+0.5f,m_DisplayAxisPosition)); // Convert to integer Vector3ui idxImage = to_unsigned_int(idxImageFloat); // Set the value of the pixel in segmentation image m_Driver->GetCurrentImageData()->SetSegmentationVoxel( idxImage, drawing_color); // Increment the counter nUpdates++; } } } #ifdef DRAWING_LOCK m_GlobalState->ReleaseDrawingLock(m_Id); } #endif /* DRAWING_LOCK */ return (nUpdates > 0); } void IRISSliceWindow ::PastePolygon() { assert(m_IsRegistered && m_IsSliceInitialized); #ifdef DRAWING_LOCK if (m_GlobalState->GetDrawingLock(m_Id)) { #endif /* DRAWING_LOCK */ if (m_PolygonDrawing->GetCachedPolygon()) { m_PolygonDrawing->PastePolygon(); m_Canvas->redraw(); } #ifdef DRAWING_LOCK } else { m_GlobalState->ReleaseDrawingLock(m_Id); } #endif /* DRAWING_LOCK */ } void IRISSliceWindow ::ClearPolygon() { m_PolygonDrawing->Delete(); m_Canvas->redraw(); } void IRISSliceWindow ::DeleteSelectedPolygonPoints() { m_PolygonDrawing->Delete(); m_Canvas->redraw(); } void IRISSliceWindow ::InsertPolygonPoints() { m_PolygonDrawing->Insert(); m_Canvas->redraw(); } itksnap/UserInterface/SliceWindow/IRISSliceWindow.h0000644000076500000240000001321411271343670021626 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: IRISSliceWindow.h,v $ Language: C++ Date: $Date: 2009/10/26 16:00:56 $ Version: $Revision: 1.7 $ 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 __IRISSliceWindow_h_ #define __IRISSliceWindow_h_ #include "GenericSliceWindow.h" #include "PolygonDrawing.h" // Forward references to interaction modes that work with this window class PolygonInteractionMode; class RegionInteractionMode; class PaintbrushInteractionMode; class AnnotationInteractionMode; /** * \class IRISSliceWindow * \brief 2D slice window used in the IRIS part of the application. * * These windows allow polygon editing and region of interest selection. */ class IRISSliceWindow : public GenericSliceWindow { public: IRISSliceWindow(int id, UserInterfaceBase *parentUI, FLTKCanvas *canvas); virtual ~IRISSliceWindow(); /** Enter the polygon editing mode of operation */ void EnterPolygonMode(); /** Enter the region of interest mode of operation */ void EnterRegionMode(); /** Enter the paintbrush mode of operation */ void EnterPaintbrushMode(); /** Enter the annotation mode of operation */ void EnterAnnotationMode(); /** * The initialize method extends the parent's version, sets up some * polygon drawing attributes */ void InitializeSlice(GenericImageData *imageData); /** * CachedPolygon() * * purpose: * returns the m_CachedPolygon flag of the polygon drawing object * * post: * return value is 1 if polygon drawing has a cached polygon, 0 otherwise */ int CachedPolygon(); /** * AcceptPolygon() * * purpose: * the gui calls this when the user presses the accept polygon button; this * means the polygon that was being edited will be rasterized into the voxel * data set according to the current drawing color and painting mode * * pre: * Register() and InitializeSlice() have been called * vox data has data with extents matching this window's width and height * * post: * if drawing lock held and polygon_drawing state was EDITING_STATE, * voxels interior to polygon and "writable" are set with the current * color obtained from m_GlobalState->GetDrawingColor(); the three coverage * modes determine which voxels inside the polygon are written over: * PAINT_OVER_ALL: all voxels * PAINT_OVER_COLORS: all voxels that aren't labeled clear * PAINT_OVER_ONE: all voxels of the color obtained from * m_GlobalState->GetOverWriteColor() * drawing lock released and polygon_drawing state is INACTIVE_STATE * else state not changed. */ bool AcceptPolygon(); /** * PastePolygon() * * purpose: * brings the last drawn polygon back for reuse * * pre: * Register() and InitializeSlice() have been called * * post: * if polygon drawing state previously INACTIVE_STATE, drawing lock * obtainable, & polygon_drawing has a cached polygon, * cached polygon becomes the edited polygon, * polygon drawing state is EDITING_STATE * drawing lock is held * else state does not change */ void PastePolygon(); /** Clear the polygon currently being edited */ void ClearPolygon(); /** Delete currently selected polygon points */ void DeleteSelectedPolygonPoints(); /** Insert points between selected polygon points */ void InsertPolygonPoints(); /** Set the rate at which freehand is converted to polygons */ void SetFreehandFittingRate(double rate) { m_PolygonDrawing->SetFreehandFittingRate(rate); } /** Get the polygon drawing object */ irisGetMacro(PolygonDrawing,PolygonDrawing *); // Allow friendly access by interactors friend class RegionInteractionMode; protected: /** Interaction mode used to select the region of interest */ RegionInteractionMode *m_RegionMode; /** Interaction mode used to position the crosshairs */ PolygonInteractionMode *m_PolygonMode; /** Interaction mode for paintbrush tools */ PaintbrushInteractionMode *m_PaintbrushMode; /** Annotation mode */ AnnotationInteractionMode *m_AnnotationMode; /** polygon drawing object */ PolygonDrawing *m_PolygonDrawing; // Type definition for the slice used for polygon rendering typedef itk::Image PolygonSliceType; typedef itk::SmartPointer PolygonSlicePointer; /** Slice used for polygon drawing and merging */ PolygonSlicePointer m_PolygonSlice; /** Draw the region and polygon interactors */ void DrawOverlays(); }; #endif itksnap/UserInterface/SliceWindow/OpenGLSliceTexture.cxx0000755000076500000240000001402711245037431022752 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: OpenGLSliceTexture.cxx,v $ Language: C++ Date: $Date: 2009/08/25 19:44:25 $ 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 "OpenGLSliceTexture.h" OpenGLSliceTexture ::OpenGLSliceTexture() { // Set to -1 to force a call to 'generate' m_IsTextureInitalized = false; // Set the update time to -1 m_UpdateTime = 0; // Init the GL settings to uchar, luminance defautls, which are harmless m_GlComponents = 1; m_GlFormat = GL_LUMINANCE; m_GlType = GL_UNSIGNED_BYTE; m_InterpolationMode = GL_NEAREST; // Initialize the buffer pointer m_Buffer = NULL; } OpenGLSliceTexture ::OpenGLSliceTexture(GLuint components, GLenum format) { // Set to -1 to force a call to 'generate' m_IsTextureInitalized = false; // Set the update time to -1 m_UpdateTime = 0; // Init the GL settings to uchar, luminance defautls, which are harmless m_GlComponents = components; m_GlFormat = format; m_GlType = GL_UNSIGNED_BYTE; m_InterpolationMode = GL_NEAREST; // Initialize the buffer pointer m_Buffer = NULL; } OpenGLSliceTexture ::~OpenGLSliceTexture() { if(m_IsTextureInitalized) glDeleteTextures(1,&m_TextureIndex); } void OpenGLSliceTexture ::SetInterpolation(GLenum interp) { assert(interp == GL_LINEAR || interp == GL_NEAREST); if(interp != m_InterpolationMode) { m_InterpolationMode = interp; m_UpdateTime = 0; // make it out-of-date } } void OpenGLSliceTexture ::Update() { // Better have an image assert(m_Image); // Update the image (necessary?) if(m_Image->GetSource()) m_Image->GetSource()->UpdateLargestPossibleRegion(); // Check if everything is up-to-date and no computation is needed if (m_IsTextureInitalized && m_UpdateTime == m_Image->GetPipelineMTime()) return; // Promote the image dimensions to powers of 2 itk::Size<2> szImage = m_Image->GetLargestPossibleRegion().GetSize(); m_TextureSize = Vector2ui(1); // Use shift to quickly double the coordinates for (unsigned int i=0;i<2;i++) while (m_TextureSize(i) < szImage[i]) m_TextureSize(i) <<= 1; // Create the texture index if necessary if(!m_IsTextureInitalized) { // Generate one texture glGenTextures(1,&m_TextureIndex); m_IsTextureInitalized = true; } // Select the texture for pixel pumping glBindTexture(GL_TEXTURE_2D,m_TextureIndex); // Properties for the texture glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_InterpolationMode ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_InterpolationMode ); // Turn off modulo-4 rounding in GL glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1); // Allocate texture of slightly bigger size glTexImage2D(GL_TEXTURE_2D, 0, m_GlComponents, m_TextureSize(0), m_TextureSize(1), 0, m_GlFormat, m_GlType, NULL); // Copy a subtexture of correct size into the image glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, szImage[0], szImage[1], m_GlFormat, m_GlType, m_Buffer); // Remember the image's timestamp m_UpdateTime = m_Image->GetPipelineMTime(); } void OpenGLSliceTexture ::Draw(const Vector3d &clrBackground) { // Update the texture Update(); // Should have a texture number assert(m_IsTextureInitalized); // GL settings glPushAttrib(GL_TEXTURE_BIT); glEnable(GL_TEXTURE_2D); // Select our texture glBindTexture(GL_TEXTURE_2D,m_TextureIndex); // Set the color to the background color glColor3dv(clrBackground.data_block()); int w = m_Image->GetBufferedRegion().GetSize()[0]; int h = m_Image->GetBufferedRegion().GetSize()[1]; double tx = w * 1.0 / m_TextureSize(0); double ty = h * 1.0 / m_TextureSize(1); // Draw quad glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex2d(0,0); glTexCoord2d(0,ty); glVertex2d(0,h); glTexCoord2d(tx,ty); glVertex2d(w,h); glTexCoord2d(tx,0); glVertex2d(w,0); glEnd(); glPopAttrib(); } void OpenGLSliceTexture ::DrawTransparent(unsigned char alpha) { // Update the texture Update(); // Should have a texture number assert(m_IsTextureInitalized); // GL settings glPushAttrib(GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT); glEnable(GL_TEXTURE_2D); // Turn on alpha blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // Select our texture glBindTexture(GL_TEXTURE_2D,m_TextureIndex); // Set the color to white glColor4ub(255,255,255,alpha); int w = m_Image->GetBufferedRegion().GetSize()[0]; int h = m_Image->GetBufferedRegion().GetSize()[1]; double tx = w * 1.0 / m_TextureSize(0); double ty = h * 1.0 / m_TextureSize(1); // Draw quad glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex2d(0,0); glTexCoord2d(0,ty); glVertex2d(0,h); glTexCoord2d(tx,ty); glVertex2d(w,h); glTexCoord2d(tx,0); glVertex2d(w,0); glEnd(); glPopAttrib(); } itksnap/UserInterface/SliceWindow/OpenGLSliceTexture.h0000644000076500000240000001053511245037431022374 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: OpenGLSliceTexture.h,v $ Language: C++ Date: $Date: 2009/08/25 19:44:25 $ Version: $Revision: 1.9 $ 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 __OpenGLSliceTexture_h_ #define __OpenGLSliceTexture_h_ #include "SNAPCommon.h" #include "SNAPOpenGL.h" #ifndef _WIN32 #ifndef GLU_VERSION_1_2 #define GLU_VERSION_1_2 1 #endif #endif #include "itkOrientedImage.h" /** * \class OpenGLSliceTexture * \brief This class is used to turn a 2D ITK image of (arbitrary) type * into a GL texture. * * The calls to Update will make sure that the texture is up to date. */ class OpenGLSliceTexture { public: // Image typedefs typedef itk::ImageBase<2> ImageBaseType; typedef itk::SmartPointer ImageBasePointer; /** Constructor, initializes the texture object */ OpenGLSliceTexture(); OpenGLSliceTexture(GLuint, GLenum); /** Destructor, deallocates texture memory */ virtual ~OpenGLSliceTexture(); /** Pass in a pointer to a 2D image */ template void SetImage(itk::Image *inImage) { m_Image = inImage; m_Image->GetSource()->UpdateLargestPossibleRegion(); m_Buffer = inImage->GetBufferPointer(); m_UpdateTime = 0; } /** Get the dimensions of the texture image, which are powers of 2 */ irisGetMacro(TextureSize,Vector2ui); /** Get the GL texture number automatically allocated by this object */ irisGetMacro(TextureIndex,int); /** Set the number of components used in call to glTextureImage */ irisSetMacro(GlComponents,GLuint); /** Get the format (e.g. GL_LUMINANCE) in call to glTextureImage */ irisSetMacro(GlFormat,GLenum); /** Get the type (e.g. GL_UNSIGNED_INT) in call to glTextureImage */ irisSetMacro(GlType,GLenum); /** * Make sure that the texture is up to date (reflects the image) */ void Update(); /** * Set the interpolation mode for the texture. If the interpolation mode * is changed, Update() will be called on the next Draw() command. The value * must be GL_NEAREST or GL_LINEAR */ void SetInterpolation(GLenum newmode); /** * Draw the texture in the current OpenGL context on a polygon with vertices * (0,0) - (size_x,size_y). Paramters are the background color of the polygon */ void Draw(const Vector3d &clrBackground); /** * Draw the texture in transparent mode, with given level of alpha blending. */ void DrawTransparent(unsigned char alpha); private: // The dimensions of the texture as stored in memory Vector2ui m_TextureSize; // The pointer to the image from which the texture is computed ImageBasePointer m_Image; // Pointer to the image's data buffer (this should have been provided by ImageBase) void *m_Buffer; // The texture number (index) GLuint m_TextureIndex; // Has the texture been initialized? bool m_IsTextureInitalized; // The pipeline time of the source image (vs. our pipeline time) unsigned long m_UpdateTime; // The number of components for Gl op GLuint m_GlComponents; // The format for Gl op GLenum m_GlFormat; // The type for Gl op GLenum m_GlType; // Interpolation mode GLenum m_InterpolationMode; }; #endif // __OpenGLSliceTexture_h_ itksnap/UserInterface/SliceWindow/PaintbrushInteractionMode.cxx0000644000076500000240000005160111141011672024377 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PaintbrushInteractionMode.cxx,v $ Language: C++ Date: $Date: 2009/01/31 09:02:50 $ Version: $Revision: 1.14 $ 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 "PaintbrushInteractionMode.h" #include "itkRegionOfInterestImageFilter.h" #include "itkGradientAnisotropicDiffusionImageFilter.h" #include "itkGradientMagnitudeImageFilter.h" #include "itkWatershedImageFilter.h" #include "GlobalState.h" #include "PolygonDrawing.h" #include "UserInterfaceBase.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "SNAPAppearanceSettings.h" #include using namespace std; class BrushWatershedPipeline { public: typedef itk::OrientedImage GreyImageType; typedef itk::OrientedImage LabelImageType; typedef itk::OrientedImage 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_COLORS && 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; }; PaintbrushInteractionMode ::PaintbrushInteractionMode(GenericSliceWindow *parent) : GenericSliceWindow::EventHandler(parent) { m_MouseInside = false; m_Watershed = new BrushWatershedPipeline(); } PaintbrushInteractionMode ::~PaintbrushInteractionMode() { } bool PaintbrushInteractionMode:: TestInside(const Vector2d &x, const PaintbrushSettings &ps) { return this->TestInside(Vector3d(x(0), x(1), 0.0), ps); } bool PaintbrushInteractionMode:: TestInside(const Vector3d &x, const PaintbrushSettings &ps) { // Determine how to scale the voxels Vector3d xTest = x; if(ps.isotropic) { double xMinVoxelDim = m_Parent->m_SliceSpacing.min_value(); xTest(0) *= m_Parent->m_SliceSpacing(0) / xMinVoxelDim; xTest(1) *= m_Parent->m_SliceSpacing(1) / xMinVoxelDim; xTest(2) *= m_Parent->m_SliceSpacing(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; } } void PaintbrushInteractionMode:: BuildBrush(const PaintbrushSettings &ps) { // This is a simple 2D marching algorithm. At any given state of the // marching, there is a 'tail' and a 'head' of an arrow. To the right // of the arrow is a voxel that's inside the brush and to the left a // voxel that's outside. Depending on the two voxels that are // ahead of the arrow to the left and right (in in, in out, out out) // at the next step the arrow turns right, continues straight or turns // left. This goes on until convergence // Initialize the marching. This requires constructing the first arrow // and marching it to the left until it is between out and in voxels. // If the brush has even diameter, the arrow is from (0,0) to (1,0). If // the brush has odd diameter (center at voxel center) then the arrow // is from (-0.5, -0.5) to (-0.5, 0.5) Vector2d xTail, xHead; if(fmod(ps.radius,1.0) == 0) { xTail = Vector2d(0.0, 0.0); xHead = Vector2d(0.0, 1.0); } else { xTail = Vector2d(-0.5, -0.5); xHead = Vector2d(-0.5, 0.5); } // Shift the arrow to the left until it is in position while(TestInside(Vector2d(xTail(0) - 0.5, xTail(1) + 0.5),ps)) { xTail(0) -= 1.0; xHead(0) -= 1.0; } // Record the starting point, which is the current tail. Once the head // returns to the starting point, the loop is done Vector2d xStart = xTail; // Do the loop m_Walk.clear(); size_t n = 0; while((xHead - xStart).squared_magnitude() > 0.01 && (++n) < 10000) { // Add the current head to the loop m_Walk.push_back(xHead); // Check the voxels ahead to the right and left Vector2d xStep = xHead - xTail; Vector2d xLeft(-xStep(1), xStep(0)); Vector2d xRight(xStep(1), -xStep(0)); bool il = TestInside(xHead + 0.5 * (xStep + xLeft),ps); bool ir = TestInside(xHead + 0.5 * (xStep + xRight),ps); // Update the tail xTail = xHead; // Decide which way to go if(il && ir) xHead += xLeft; else if(!il && ir) xHead += xStep; else if(!il && !ir) xHead += xRight; else assert(0); } // Add the last vertex m_Walk.push_back(xStart); } /* void PaintbrushInteractionMode:: BuildBrush(const PaintbrushSettings &ps) { Vector2d xFirstVox(0, 0); // Shift in case the center of the brush falls at vertex center // Step left from the center in units of 0.5 until we are outside // of the brush while(TestInside(xFirstVox, ps)) xFirstVox(0) -= 1.0; // Find the left-most voxel that is inside of the brush // Vector2d xFirstVox(0, 0); // while(TestInside(xFirstVox, ps)) // xFirstVox(0) -= 1.0; // Set the starting location Vector2d xStart(xFirstVox(0) + 0.5, -0.5); Vector2d xWalk(xFirstVox(0) + 0.5, 0.5); Vector2d xStep(0, 1); if(fmod(ps.radius, 1.0) == 0) { xStart = Vector2d(xFirstVox(0) + 1.0, 0.0); xWalk = Vector2d(xFirstVox(0) + 1.0, 1.0); } int i = 0; m_Walk.clear(); while((xStart - xWalk).squared_magnitude() > 0.01 && ++i < 10000) { // Add the current walk step m_Walk.push_back(xWalk); // Compute the walk direction Vector2d xLeft(-xStep(1), xStep(0)); Vector2d xRight(xStep(1), -xStep(0)); if(TestInside(xWalk + 0.5 * (xStep + xLeft), ps)) xStep = xLeft; else if(!TestInside(xWalk + 0.5 * (xStep + xRight), ps)) xStep = xRight; // Update the walk xWalk = xWalk + xStep; } // Push the last point on the walk m_Walk.push_back(xStart); } */ void PaintbrushInteractionMode ::OnDraw() { // Leave if mouse outside of slice if(!m_MouseInside) return; // Draw the outline of the paintbrush PaintbrushSettings pbs = m_ParentUI->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); // Paint all the edges in the paintbrush definition const SNAPAppearanceSettings::Element &elt = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::PAINTBRUSH_OUTLINE); // Build the mask edges BuildBrush(pbs); // Set line properties glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); // Apply the line properties glColor3dv(elt.NormalColor.data_block()); SNAPAppearanceSettings::ApplyUIElementLineSettings(elt); // Get the brush position Vector3f xPos; if(fmod(pbs.radius,1.0) == 0) xPos = m_Parent->MapImageToSlice(to_float(m_MousePosition)); else xPos = m_Parent->MapImageToSlice(to_float(m_MousePosition) + Vector3f(0.5f)); // Refit matrix so that the lines are centered on the current pixel glPushMatrix(); glTranslated( xPos(0), xPos(1), 0.0 ); // Draw the lines around the point glBegin(GL_LINE_LOOP); for(std::list::iterator it = m_Walk.begin(); it != m_Walk.end(); ++it) glVertex2d((*it)(0), (*it)(1)); glEnd(); // Pop the matrix glPopMatrix(); // Pop the attributes glPopAttrib(); } /* #include "itkImageFileWriter.h" template void DebugDumpImage(ImageType *img, const char *fname) { typedef itk::ImageFileWriter WriterType; WriterType::Pointer w = WriterType::New(); w->SetFileName(fname); w->SetInput(img); w->Update(); } */ void PaintbrushInteractionMode ::ApplyBrush(FLTKEvent const &event) { // Get the segmentation image LabelImageWrapper *imgLabel = m_Driver->GetCurrentImageData()->GetSegmentation(); // Get the paint properties LabelType drawing_color = m_GlobalState->GetDrawingColorLabel(); LabelType overwrt_color = m_GlobalState->GetOverWriteColorLabel(); CoverageModeType mode = m_GlobalState->GetCoverageMode(); // Get the paintbrush properties PaintbrushSettings pbs = m_ParentUI->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); // Whether watershed filter is used (adaptive brush) bool flagWatershed = ( pbs.mode == PAINTBRUSH_WATERSHED && event.Button == FL_LEFT_MOUSE && event.Id == FL_PUSH); // Define a region of interest LabelImageWrapper::ImageType::RegionType xTestRegion; for(size_t i = 0; i < 3; i++) { if(i != imgLabel->GetDisplaySliceImageAxis(m_Parent->m_Id) || pbs.flat == false) { // 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) { // Precompute the watersheds m_Watershed->PrecomputeWatersheds( m_Driver->GetCurrentImageData()->GetGrey()->GetImage(), m_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(0.0); if(fmod(pbs.radius,1.0)==0) { offset.fill(0.5); offset(m_Parent->m_ImageAxes[2]) = 0.0; } // 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->m_ImageToDisplayTransform.TransformVector(xDelta)); // Check if the pixel is inside if(!TestInside(xDeltaSliceSpace, pbs)) continue; // Check if the pixel is in the watershed LabelImageWrapper::ImageType::IndexType idxoff = to_itkIndex( Vector3l(idx.GetIndex()) - Vector3l(xTestRegion.GetIndex().GetIndex())); if(flagWatershed && !m_Watershed->IsPixelInSegmentation(idxoff)) continue; // if(pbs.shape == PAINTBRUSH_ROUND && xDelta.squared_magnitude() >= r2) // continue; // Paint the pixel LabelType pxLabel = it.Get(); // Standard paint mode if(event.Button == FL_LEFT_MOUSE) { if (mode == PAINT_OVER_ALL || (mode == PAINT_OVER_ONE && pxLabel == overwrt_color) || (mode == PAINT_OVER_COLORS && pxLabel != 0)) { it.Set(drawing_color); if(pxLabel != drawing_color) flagUpdate = true; } } // Background paint mode (clear label over current label) else if(event.Button == FL_RIGHT_MOUSE) { if(drawing_color != 0 && pxLabel == drawing_color) { it.Set(0); if(pxLabel != 0) flagUpdate = true; } else if(drawing_color == 0 && mode == PAINT_OVER_ONE) { it.Set(overwrt_color); if(pxLabel != overwrt_color) flagUpdate = true; } } } // Image has been updated if(flagUpdate) { imgLabel->GetImage()->Modified(); m_ParentUI->OnPaintbrushPaint(); m_ParentUI->RedrawWindows(); } else { m_Parent->GetCanvas()->redraw(); } } int PaintbrushInteractionMode ::OnMousePress(FLTKEvent const &event) { // Get the paintbrush properties PaintbrushSettings pbs = m_ParentUI->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); // Check if the right button was pressed if(event.Button == FL_LEFT_MOUSE || event.Button == FL_RIGHT_MOUSE) { // Scan convert the points into the slice ApplyBrush(event); // Record the event m_LastMouseEvent = event; // Eat the event unless cursor chasing is enabled return pbs.chase ? 0 : 1; } else return 0; } void PaintbrushInteractionMode ::ComputeMousePosition(const Vector3f &xEvent) { // Get the paintbrush properties PaintbrushSettings pbs = m_ParentUI->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); // Find the pixel under the mouse Vector3f xClick = m_Parent->MapWindowToSlice(xEvent.extract(2)); // 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; if(fmod(pbs.radius, 1.0) == 0.0) { Vector3f offset(0.5); offset(m_Parent->m_ImageAxes[2]) = 0.0; xCrossInteger = to_int(xCross + offset); } else 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_Driver->GetCurrentImageData()->GetVolumeExtents()); m_MousePosition = to_unsigned_int( xCrossInteger.clamp(Vector3i(0),xSize - Vector3i(1))); m_MouseInside = true; } int PaintbrushInteractionMode ::OnMouseMotion(FLTKEvent const &event) { // Find the pixel under the mouse ComputeMousePosition(event.XSpace); // Repaint m_ParentUI->RedrawWindows(); return 1; } int PaintbrushInteractionMode ::OnMouseEnter(const FLTKEvent &event) { // Find the pixel under the mouse ComputeMousePosition(event.XSpace); // Repaint m_ParentUI->RedrawWindows(); // Record the event m_LastMouseEvent = event; return 0; } int PaintbrushInteractionMode ::OnMouseLeave(const FLTKEvent &event) { // Repaint m_MouseInside = false; m_ParentUI->RedrawWindows(); // Record the event m_LastMouseEvent = event; return 0; } int PaintbrushInteractionMode ::DragReleaseHandler(FLTKEvent const &event, FLTKEvent const &pressEvent, bool drag) { // Get the paintbrush properties PaintbrushSettings pbs = m_ParentUI->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); // The behavior is different for 'fast' regular brushes and adaptive brush. For the // adaptive brush, dragging is disabled. if(pbs.mode == PAINTBRUSH_WATERSHED && event.Button == FL_LEFT_MOUSE) { if(event.Id == FL_RELEASE) m_Parent->m_ParentUI->StoreUndoPoint("Drawing with paintbrush"); return pbs.chase ? 0 : 1; } else { // Check if the right button was pressed if(event.Button == FL_LEFT_MOUSE || event.Button == FL_RIGHT_MOUSE) { // See how much we have moved since the last event double delta = (event.XCanvas - m_LastMouseEvent.XCanvas).magnitude(); if(delta > pbs.radius) { size_t nSteps = (int)ceil(delta / pbs.radius); for(size_t i = 0; i < nSteps; i++) { float t = (1.0 + i) / nSteps; Vector3f X = t * m_LastMouseEvent.XSpace + (1.0f - t) * event.XSpace; ComputeMousePosition(X); ApplyBrush(event); } } else { // Find the pixel under the mouse ComputeMousePosition(event.XSpace); // Scan convert the points into the slice ApplyBrush(event); // Set an undo point if not in drag mode if(!drag) m_Parent->m_ParentUI->StoreUndoPoint("Drawing with paintbrush"); } // Record the event m_LastMouseEvent = event; // Eat the event unless cursor chasing is enabled return pbs.chase ? 0 : 1; } } return 0; } int PaintbrushInteractionMode ::OnMouseRelease(FLTKEvent const &event, FLTKEvent const &pressEvent) { return DragReleaseHandler(event, pressEvent,false); } int PaintbrushInteractionMode ::OnMouseDrag(FLTKEvent const &event, FLTKEvent const &pressEvent) { return DragReleaseHandler(event, pressEvent,true); } int PaintbrushInteractionMode ::OnKeyDown(FLTKEvent const &event) { // Get the paintbrush properties PaintbrushSettings pbs = m_ParentUI->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); // The behavior is different for 'fast' regular brushes and adaptive brush. For the // adaptive brush, dragging affects parameter settings if(pbs.mode == PAINTBRUSH_WATERSHED) { double shift = 0.0; if(event.Key == ',') shift = -1.0; else if(event.Key == '.') shift = 1.0; if(shift != 0.0) { pbs.watershed.level += shift * 0.05; if(pbs.watershed.level < 0.0) pbs.watershed.level = 0.0; if(pbs.watershed.level > 1.0) pbs.watershed.level = 1.0; m_ParentUI->GetDriver()->GetGlobalState()->SetPaintbrushSettings(pbs); m_ParentUI->UpdatePaintbrushAttributes(); return 1; } } return 0; } int PaintbrushInteractionMode ::OnShortcut(FLTKEvent const &event) { return 0; } itksnap/UserInterface/SliceWindow/PaintbrushInteractionMode.h0000644000076500000240000000660611115325774024045 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PaintbrushInteractionMode.h,v $ Language: C++ Date: $Date: 2008/12/02 21:43:24 $ Version: $Revision: 1.6 $ 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 __PaintbrushInteractionMode_h_ #define __PaintbrushInteractionMode_h_ #include "GenericSliceWindow.h" #include "GlobalState.h" // Reference to watershed filter object class BrushWatershedPipeline; /** * \class PaintbrushInteractionMode * \brief UI interaction mode that takes care of painting with a shaped mask (brush). * * \see GenericSliceWindow */ class PaintbrushInteractionMode : public GenericSliceWindow::EventHandler { public: PaintbrushInteractionMode(GenericSliceWindow *parent); virtual ~PaintbrushInteractionMode(); int OnMousePress(const FLTKEvent &event); int OnKeyDown(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent); int DragReleaseHandler(FLTKEvent const &event, const FLTKEvent &pressEvent, bool drag); int OnShortcut(const FLTKEvent &event); int OnMouseMotion(const FLTKEvent &event); int OnMouseEnter(const FLTKEvent &event); int OnMouseLeave(const FLTKEvent &event); void OnDraw(); private: // The paintbrush shape (depends on the settings parameters) typedef itk::Image MaskType; MaskType::Pointer m_Mask; // The edges in the paintbrush typedef std::pair EdgeType; typedef std::list EdgeList; EdgeList m_MaskEdges; std::list m_Walk; // Build a display list to represent the paint brush void BuildBrush(const PaintbrushSettings &ps); void ApplyBrush(const FLTKEvent &event); void ComputeMousePosition(const Vector3f &xEvent); bool TestInside(const Vector2d &x, const PaintbrushSettings &ps); bool TestInside(const Vector3d &x, const PaintbrushSettings &ps); Vector3ui m_MousePosition; bool m_MouseInside; // Watershed pipeline object BrushWatershedPipeline *m_Watershed; // Last FLTK event involving the mouse FLTKEvent m_LastMouseEvent; // The window handler needs to access our privates friend class IRISSliceWindow; }; #endif // __PaintbrushInteractionMode_h_ itksnap/UserInterface/SliceWindow/PolygonDrawing.cxx0000644000076500000240000010773111455362724022243 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PolygonDrawing.cxx,v $ Language: C++ Date: $Date: 2010/10/13 17:01:08 $ Version: $Revision: 1.15 $ 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 "PolygonDrawing.h" #include "PolygonScanConvert.h" #include "SNAPCommonUI.h" #include "GenericSliceWindow.h" #include "UserInterfaceBase.h" #include "SNAPOpenGL.h" #include #include #include #include #include #include "FL/Fl_Menu_Button.H" #include "itkOrientedImage.h" #include "itkPointSet.h" #include "itkBSplineScatteredDataPointSetToImageFilter.h" #include "SNAPAppearanceSettings.h" using namespace std; // glu Tess callbacks /* #ifdef WIN32 typedef void (CALLBACK *TessCallback)(); #else typedef void (*TessCallback)(); #endif void #ifdef WIN32 CALLBACK #endif BeginCallback(GLenum which) { glBegin(which); } void #ifdef WIN32 CALLBACK #endif EndCallback(void) { glEnd(); } void #ifdef WIN32 CALLBACK #endif ErrorCallback(GLenum errorCode) { const GLubyte *estring; estring = gluErrorString(errorCode); cerr << "Tesselation Error-Exiting: " << estring << endl; exit(-1); } void #ifdef WIN32 CALLBACK #endif CombineCallback(GLdouble coords[3], GLdouble **irisNotUsed(vertex_data), GLfloat *irisNotUsed(weight), GLdouble **dataOut) { GLdouble *vertex; vertex = new GLdouble[3]; vertex[0] = coords[0]; vertex[1] = coords[1]; vertex[2] = coords[2]; *dataOut = vertex; } */ const float PolygonDrawing::m_DrawingModeColor[] = { 1.0f, 0.0f, 0.5f }; const float PolygonDrawing::m_EditModeNormalColor[] = { 1.0f, 0.0f, 0.0f }; const float PolygonDrawing::m_EditModeSelectedColor[] = { 0.0f, 1.0f, 0.0f }; /** * PolygonDrawing() * * purpose: * create initial vertex and m_Cache arrays, init GLUm_Tesselatorelator */ PolygonDrawing ::PolygonDrawing(GenericSliceWindow *parent) { m_CachedPolygon = false; m_State = INACTIVE_STATE; m_SelectedVertices = false; m_DraggingPickBox = false; m_Parent = parent; } /** * ~PolygonDrawing() * * purpose: * free arrays and GLUm_Tesselatorelator */ PolygonDrawing ::~PolygonDrawing() { } /** * ComputeEditBox() * * purpose: * compute the bounding box around selected vertices * * post: * if m_Vertices are selected, sets m_SelectedVertices to 1, else 0 */ void PolygonDrawing ::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; } } } /** * Add() * * purpose: * to add a vertex to the existing contour * * pre: * m_NumberOfAllocatedVertices > 0 */ /* void PolygonDrawing ::Add(float x, float y, int selected) { // add a new vertex Vertex vNew; vNew.x = x; vNew.y = y; vNew.selected = selected; m_Vertices[m_NumberOfUsedVertices].x = x; m_Vertices[m_NumberOfUsedVertices].y = y; m_Vertices[m_NumberOfUsedVertices].selected = selected; m_NumberOfUsedVertices++; } */ void PolygonDrawing ::DropLastPoint() { if(m_State == DRAWING_STATE) { if(m_Vertices.size()) m_Vertices.pop_back(); } } void PolygonDrawing ::ClosePolygon() { if(m_State == DRAWING_STATE) { m_State = EDITING_STATE; m_SelectedVertices = true; for(VertexIterator it = m_Vertices.begin(); it!=m_Vertices.end(); ++it) it->selected = false; ComputeEditBox(); } } /** * 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 PolygonDrawing ::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()) { m_State = INACTIVE_STATE; m_SelectedVertices = false; } ComputeEditBox(); } void PolygonDrawing ::Reset() { m_State = INACTIVE_STATE; m_Vertices.clear(); ComputeEditBox(); } /** * Insert() * * purpose: * insert vertices between adjacent selected vertices * * post: * length of m_Vertices array does not decrease */ void PolygonDrawing ::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; } } int PolygonDrawing ::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 PolygonDrawing ::ProcessFreehandCurve() { // Special case: no fitting if(m_FreehandFittingRate == 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 / m_FreehandFittingRate); 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 PolygonDrawing::Vertex &v1, const PolygonDrawing::Vertex &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 PolygonDrawing ::AcceptPolygon(ByteImageType *image) { // 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(), image); // 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 m_State = INACTIVE_STATE; } /** * 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 PolygonDrawing ::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; m_State = EDITING_STATE; // Compute the edit box ComputeEditBox(); } /** * Draw() * * purpose: * draw the polyline being drawn or the polygon being edited * * parameters: * pixel_x - this is a width in the polygon's space that is a single * pixel width on screen * pixel_y - this is a height in the polygon's space that is a single * pixel height on screen * * pre: none - expected to exit if m_State is INACTIVE_STATE */ void PolygonDrawing ::Draw(float pixel_x, float pixel_y) { // Must be in active state if (m_State == INACTIVE_STATE) return; // Get a pointer to the appearance settings SNAPAppearanceSettings *app = m_Parent->GetParentUI()->GetAppearanceSettings(); SNAPAppearanceSettings::Element &aeDraw = app->GetUIElement(SNAPAppearanceSettings::POLY_DRAW_MAIN); SNAPAppearanceSettings::Element &aeClose = app->GetUIElement(SNAPAppearanceSettings::POLY_DRAW_CLOSE); SNAPAppearanceSettings::Element &aeEdit = app->GetUIElement(SNAPAppearanceSettings::POLY_EDIT); // Push the line state glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); // set line and point drawing parameters glPointSize(4); // glLineWidth(2); // glEnable(GL_LINE_SMOOTH); // glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); // glEnable(GL_BLEND); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Draw the line segments VertexIterator it, itNext; if (m_State == EDITING_STATE) { glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); SNAPAppearanceSettings::ApplyUIElementLineSettings(aeEdit); glBegin(GL_LINES); for(it = m_Vertices.begin(); it!=m_Vertices.end();++it) { // Point to the next vertex itNext = it; ++itNext; if(itNext == m_Vertices.end()) itNext = m_Vertices.begin(); // Set the color based on the mode if (it->selected && itNext->selected) glColor3dv(aeEdit.ActiveColor.data_block()); else glColor3dv(aeEdit.NormalColor.data_block()); // Draw the line glVertex3f(it->x, it->y,0); glVertex3f(itNext->x, itNext->y, 0); } glEnd(); glPopAttrib(); } // Not editing state else { glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); SNAPAppearanceSettings::ApplyUIElementLineSettings(aeDraw); // Draw the vertices glBegin(GL_LINE_STRIP); glColor3dv(aeDraw.NormalColor.data_block()); for(it = m_Vertices.begin(); it!=m_Vertices.end();++it) glVertex3f(it->x, it->y, 0); glEnd(); // Draw the drag vertices if(m_DragVertices.size()) { glBegin(GL_LINE_STRIP); for(it = m_DragVertices.begin(); it!=m_DragVertices.end();++it) glVertex3f(it->x, it->y, 0); glEnd(); } glPopAttrib(); // Draw stippled line from last point to end point if(m_DragVertices.size() + m_Vertices.size() > 2 && aeClose.Visible) { glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); SNAPAppearanceSettings::ApplyUIElementLineSettings(aeClose); glBegin(GL_LINES); glColor3dv(aeClose.NormalColor.data_block()); if(m_DragVertices.size()) glVertex3f(m_DragVertices.back().x, m_DragVertices.back().y, 0); else glVertex3f(m_Vertices.back().x, m_Vertices.back().y, 0); glVertex3f(m_Vertices.front().x, m_Vertices.front().y, 0); glEnd(); glPopAttrib(); } } // draw the vertices glBegin(GL_POINTS); glPushAttrib(GL_COLOR_BUFFER_BIT); for(it = m_Vertices.begin(); it!=m_Vertices.end();++it) { if(it->control) { if (it->selected) glColor3dv(aeEdit.ActiveColor.data_block()); else if (m_State == DRAWING_STATE) glColor3dv(aeDraw.NormalColor.data_block()); else glColor3dv(aeEdit.NormalColor.data_block()); glVertex3f(it->x,it->y,0.0f); } } // Draw the last dragging vertex point if(m_DragVertices.size()) { Vertex last = m_DragVertices.back(); glColor3dv(aeEdit.ActiveColor.data_block()); glVertex3f(last.x, last.y, 0.0f); } glEnd(); glPopAttrib(); // draw edit or pick box if (m_DraggingPickBox) { glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); glLineWidth(1); glColor3dv(aeEdit.ActiveColor.data_block()); glBegin(GL_LINE_LOOP); glVertex3f(m_SelectionBox[0],m_SelectionBox[2],0.0); glVertex3f(m_SelectionBox[1],m_SelectionBox[2],0.0); glVertex3f(m_SelectionBox[1],m_SelectionBox[3],0.0); glVertex3f(m_SelectionBox[0],m_SelectionBox[3],0.0); glEnd(); glPopAttrib(); } else if (m_SelectedVertices) { glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); glLineWidth(1); glColor3dv(aeEdit.ActiveColor.data_block()); float border_x = (float) 4.0 * pixel_x; float border_y = (float) 4.0 * pixel_y; glLineWidth(1); glColor3fv(m_EditModeSelectedColor); glBegin(GL_LINE_LOOP); glVertex3f(m_EditBox[0] - border_x,m_EditBox[2] - border_y,0.0); glVertex3f(m_EditBox[1] + border_x,m_EditBox[2] - border_y,0.0); glVertex3f(m_EditBox[1] + border_x,m_EditBox[3] + border_y,0.0); glVertex3f(m_EditBox[0] - border_x,m_EditBox[3] + border_y,0.0); glEnd(); glPopAttrib(); } glPopAttrib(); } /** * Handle() * * purpose: * handle events from the window that contains polygon drawing object: * if internal m_State is DRAWING_STATE * left-click creates subsequent vertices of the polygon, * right-click closes polygon and puts polygon in EDITING_STATE * if internal m_State is EDITING_STATE * shift-left-click on vertex adds vertex to selection * shift-left-click off vertex begins dragging a rubber-band box * * shift-right-click performs same actions but de-selects vertices * * left-click in selection box begins dragging of selected vertices * left-click outside selection box cancels selection, begins a * dragging of a rubber-band box * * left-release after dragging adds vertices inside rubber-band box * to selection * * pressing the 'insert' key calls Insert() * pressing the 'delete' key calls Delete() * * parameters: * event - an Fl event number * x - the x of the point clicked in the space of the polygon * y - the y of the point clicked in the space of the polygon * pixel_x - see Draw() * pixel_y - see Draw() * * pre: * window that calls this has the drawing lock * * post: * if event is used, 1 is returned, else 0 */ int PolygonDrawing ::Handle(int event, int button, float x, float y, float pixel_x, float pixel_y) { VertexIterator it, itNext; switch (m_State) { case INACTIVE_STATE: if ((event == FL_PUSH) && (button == FL_LEFT_MOUSE)) { m_State = DRAWING_STATE; m_Vertices.push_back( Vertex(x, y, false, true) ); return 1; } else if ((event == FL_PUSH) && (button == FL_RIGHT_MOUSE)) { // Show popup menu Fl_Menu_Button menu(Fl::event_x_root(), Fl::event_y_root(), 80, 1); menu.textsize(12); menu.add("paste last polygon", FL_COMMAND + 'v', NULL); // Disable some options if(m_Cache.size() == 0) { const_cast(menu.menu() + 0)->deactivate(); } menu.popup(); // Branch based on the decision if(menu.value() == 0) { m_Parent->GetParentUI()->OnPastePolygonAction(m_Parent->GetId()); } return 1; } break; case DRAWING_STATE: if (event == FL_PUSH) { m_DragVertices.clear(); if (button == FL_LEFT_MOUSE) { // 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(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) { ClosePolygon(); return 1; } } m_Vertices.push_back( Vertex(x, y, false, true) ); } return 1; } else if (button == FL_RIGHT_MOUSE) { // Show popup menu Fl_Menu_Button menu(Fl::event_x_root(), Fl::event_y_root(), 80, 1); menu.textsize(12); menu.add("close loop && edit", FL_Enter, NULL); menu.add("_close loop && accept", FL_COMMAND + FL_Enter, NULL); menu.add("undo last point", FL_Delete, NULL); menu.add("cancel drawing", FL_Escape, NULL); // Disable some options if(!CanClosePolygon()) { const_cast(menu.menu() + 0)->deactivate(); const_cast(menu.menu() + 1)->deactivate(); } if(!CanDropLastPoint()) { const_cast(menu.menu() + 2)->deactivate(); } menu.popup(); // Branch based on the decision if(menu.value() == 0) { ClosePolygon(); } if(menu.value() == 1) { ClosePolygon(); m_Parent->GetParentUI()->OnAcceptPolygonAction(m_Parent->GetId()); } else if(menu.value() == 2) { DropLastPoint(); } else if(menu.value() == 3) { Reset(); } return 1; } } else if (event == FL_DRAG) { if(m_Vertices.size() == 0) { m_Vertices.push_back(Vertex(x,y,false,true)); } else { if(m_FreehandFittingRate == 0) { m_Vertices.push_back(Vertex(x,y,false,false)); } else { Vertex &v = m_Vertices.back(); double dx = (v.x-x) / pixel_x; double dy = (v.y-y) / pixel_y; double d = dx*dx+dy*dy; if(d >= m_FreehandFittingRate * m_FreehandFittingRate) m_Vertices.push_back(Vertex(x,y,false,true)); } } return 1; } else if (event == FL_RELEASE) { // If some dragging has been done, convert it to polygons // if(m_DragVertices.size() > 0) // { // ProcessFreehandCurve(); // } if(m_Vertices.size() && m_Vertices.back().control == false) m_Vertices.back().control = true; return 1; } else if (event == FL_SHORTCUT) { if(Fl::test_shortcut(FL_COMMAND | FL_Enter)) { if(CanClosePolygon()) { ClosePolygon(); m_Parent->GetParentUI()->OnAcceptPolygonAction(m_Parent->GetId()); } return 1; } else if(Fl::test_shortcut(FL_Enter)) { if(CanClosePolygon()) ClosePolygon(); return 1; } else if(Fl::test_shortcut(FL_Delete) || Fl::test_shortcut(FL_BackSpace)) { if(CanDropLastPoint()) DropLastPoint(); return 1; } else if(Fl::test_shortcut(FL_Escape)) { Reset(); return 1; } } break; case EDITING_STATE: switch (event) { case FL_PUSH: m_StartX = x; m_StartY = y; if (button == FL_LEFT_MOUSE) { // if user is pressing shift key, add/toggle m_Vertices, or drag pick box if (Fl::event_state(FL_SHIFT)) { // check if vertex clicked if(CheckClickOnVertex(x,y,pixel_x,pixel_y,4)) { ComputeEditBox(); return 1; } // check if clicked near a line segment if(CheckClickOnLineSegment(x,y,pixel_x,pixel_y,4)) { ComputeEditBox(); return 1; } // otherwise start dragging pick box m_DraggingPickBox = true; m_SelectionBox[0] = m_SelectionBox[1] = x; m_SelectionBox[2] = m_SelectionBox[3] = y; return 1; } // user not holding shift key; if user clicked inside edit box, // edit box will be moved in drag event if (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))) return 1; // clicked outside of edit box & shift not held, this means the // current selection will be cleared for(it = m_Vertices.begin(); it!=m_Vertices.end(); ++it) it->selected = false; m_SelectedVertices = false; // Check if clicked on a pixel if(CheckClickOnVertex(x,y,pixel_x,pixel_y,4)) { ComputeEditBox(); return 1; } // check if clicked near a line segment if(CheckClickOnLineSegment(x,y,pixel_x,pixel_y,4)) { ComputeEditBox(); return 1; } // didn't click a point - start dragging pick box m_DraggingPickBox = true; m_SelectionBox[0] = m_SelectionBox[1] = x; m_SelectionBox[2] = m_SelectionBox[3] = y; return 1; } // Popup menu business else if(button == FL_RIGHT_MOUSE) { // If nothing is selected, select what's under the cursor if(!m_SelectedVertices) { // Check if clicked on a pixel if(CheckClickOnVertex(x,y,pixel_x,pixel_y,4)) { ComputeEditBox(); m_Parent->GetParentUI()->RedrawWindows(); } // check if clicked near a line segment else if(CheckClickOnLineSegment(x,y,pixel_x,pixel_y,4)) { ComputeEditBox(); m_Parent->GetParentUI()->RedrawWindows(); } } // Show popup menu Fl_Menu_Button menu(Fl::event_x_root(), Fl::event_y_root(), 80, 1); menu.textsize(12); menu.add("_accept", FL_Enter, NULL); menu.add("delete selected points", FL_Delete, NULL); menu.add("_split selected segments", FL_Insert, NULL); menu.add("clear", FL_Escape, NULL); if(!CanInsertVertices()) const_cast(menu.menu() + 2)->deactivate(); if(!m_SelectedVertices) const_cast(menu.menu() + 1)->deactivate(); menu.popup(); // Branch based on the decision if(menu.value() == 0) { m_Parent->GetParentUI()->OnAcceptPolygonAction(m_Parent->GetId()); } if(menu.value() == 1) { m_Parent->GetParentUI()->OnDeletePolygonSelectedAction(m_Parent->GetId()); } else if(menu.value() == 2) { m_Parent->GetParentUI()->OnInsertIntoPolygonSelectedAction(m_Parent->GetId()); } else if(menu.value() == 3) { Reset(); } return 1; } break; case FL_DRAG: if ((button == FL_LEFT_MOUSE) || (button == FL_RIGHT_MOUSE)) { if (m_DraggingPickBox) { m_SelectionBox[1] = x; m_SelectionBox[3] = y; } else { if (button == FL_LEFT_MOUSE) { 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(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; } } return 1; } break; case FL_RELEASE: if ((button == FL_LEFT_MOUSE) || (button == FL_RIGHT_MOUSE)) { 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(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 = (button == 1); } ComputeEditBox(); } return 1; } break; case FL_SHORTCUT: if(Fl::test_shortcut(FL_Enter)) { m_Parent->GetParentUI()->OnAcceptPolygonAction(m_Parent->GetId()); return 1; } else if(Fl::test_shortcut(FL_Delete) || Fl::test_shortcut(FL_BackSpace)) { if(m_SelectedVertices) m_Parent->GetParentUI()->OnDeletePolygonSelectedAction(m_Parent->GetId()); return 1; } else if(Fl::test_shortcut(FL_Insert)) { if(CanInsertVertices()) m_Parent->GetParentUI()->OnInsertIntoPolygonSelectedAction(m_Parent->GetId()); return 1; } else if(Fl::test_shortcut(FL_Escape)) { Reset(); return 1; } break; default: break; } break; default: cerr << "PolygonDrawing::Handle(): unknown m_State " << m_State << endl; } return 0; } /** * Check if a click is within k pixels of a vertex, if so select the vertices * of that line segment */ bool PolygonDrawing ::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 PolygonDrawing ::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 PolygonDrawing ::CanClosePolygon() { return m_Vertices.size() > 2; } /* Can last point be dropped? */ bool PolygonDrawing ::CanDropLastPoint() { return m_Vertices.size() > 0; } /* Can edges be split? */ bool PolygonDrawing ::CanInsertVertices() { return GetNumberOfSelectedSegments() > 0; } /* *$Log: PolygonDrawing.cxx,v $ *Revision 1.15 2010/10/13 17:01:08 pyushkevich *Fixing warnings * *Revision 1.14 2010/10/09 04:20:08 pyushkevich *Added customization to polygon drawing appearance;ability to export/import appearance settings * *Revision 1.13 2010/06/15 16:27:44 pyushkevich *build errors fixed * *Revision 1.12 2010/05/27 11:16:22 pyushkevich *Further improved polygon drawing interface * *Revision 1.11 2010/05/27 07:29:36 pyushkevich *New popup menu for polygon drawing, other improvements to polygon tool * *Revision 1.10 2009/01/23 20:09:38 pyushkevich *FIX: 3D rendering now takes place in Nifti(RAS) world coordinates, rather than the VTK (x spacing + origin) coordinates. As part of this, itk::OrientedImage is now used for 3D images in SNAP. Still have to fix cut plane code in Window3D * *Revision 1.9 2008/10/24 12:52:08 pyushkevich *FIX: Bug on ITK 3.8 with level set being inverted *FIX: Bug with NIFTI orientation *ENH: Clean up itk extras directory * *Revision 1.8 2008/01/10 18:00:51 pyushkevich *took out test.png from Polygon drawing * *Revision 1.7 2007/12/30 04:43:03 pyushkevich *License/Packaging updates * *Revision 1.6 2007/12/25 15:46:23 pyushkevich *Added undo/redo functionality to itk-snap * *Revision 1.5 2007/10/01 00:13:15 pyushkevich *Polygon Drawing updates * *Revision 1.4 2007/09/18 18:42:40 pyushkevich *Added tablet drawing to polygon mode * *Revision 1.3 2007/09/04 16:56:13 pyushkevich *tablet support 1 * *Revision 1.2 2006/12/06 01:26:07 pyushkevich *Preparing for 1.4.1. Seems to be stable in Windows but some bugs might be still there * *Revision 1.1 2006/12/02 04:22:27 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.10 2005/12/19 03:43:12 pauly *ENH: SNAP enhancements and bug fixes for 1.4 release * *Revision 1.9 2005/12/08 18:20:46 hjohnson *COMP: Removed compiler warnings from SGI/linux/MacOSX compilers. * *Revision 1.8 2004/07/22 19:22:50 pauly *ENH: Large image support for SNAP. This includes being able to use more screen real estate to display a slice, a fix to the bug with manual segmentation of images larger than the window size, and a thumbnail used when zooming into the image. * *Revision 1.7 2004/01/27 17:49:47 pauly *FIX: MAC OSX Compilation fixes * *Revision 1.6 2003/10/09 22:45:15 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *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:01 pauly *FIX: Enabled loading of images with different orientations *ENH: Implemented image save and load operations * *Revision 1.4 2003/08/28 14:37:09 pauly *FIX: Clean 'unused parameter' and 'static keyword' warnings in gcc. *FIX: Label editor repaired * *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:50 pauly *Initial checkin of the SNAP application into the InsightApplications tree. * *Revision 1.1 2003/07/11 23:28:10 pauly **** empty log message *** * *Revision 1.3 2003/06/08 23:27:56 pauly *Changed variable names using combination of ctags, egrep, and perl. * *Revision 1.2 2003/04/29 14:01:42 pauly *Charlotte Trip * *Revision 1.1 2003/03/07 19:29:47 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:30 moon *Added Header and Log tags to all files **/ itksnap/UserInterface/SliceWindow/PolygonDrawing.h0000644000076500000240000001634611377452206021667 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PolygonDrawing.h,v $ Language: C++ Date: $Date: 2010/05/27 11:16:22 $ Version: $Revision: 1.7 $ 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 __PolygonDrawing_h_ #define __PolygonDrawing_h_ #include #include "SNAPOpenGL.h" #include #include #include "SNAPCommonUI.h" class GenericSliceWindow; namespace itk { template class Image; }; /** * \class PolygonDrawing * \brief Code for drawing and editing polygons */ class PolygonDrawing { public: // Type of image passed in for rasterization typedef itk::Image ByteImageType; /** States that the polygon drawing is in */ enum PolygonState { INACTIVE_STATE, DRAWING_STATE, EDITING_STATE }; /** Vertex structure */ struct Vertex { float x, y; bool selected; bool control; Vertex(float x_, float y_, bool on_, bool ctl_) : x(x_), y(y_), selected(on_), control(ctl_) {} Vertex() : x(0.0f), y(0.0f), selected(false), control(true) {} float &operator[](unsigned int i) { return (i==0) ? x : y; } }; PolygonDrawing(GenericSliceWindow *parent); virtual ~PolygonDrawing(); void AcceptPolygon(ByteImageType *slice); void PastePolygon(void); void Draw(float pixel_x, float pixel_y); int Handle(int event, int button, float x, float y, float pixel_x, float pixel_y); void Delete(); void Insert(); 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 */ irisGetMacro(FreehandFittingRate, double); irisSetMacro(FreehandFittingRate, double); private: // Array of vertices, cached vertices from last operation typedef std::list VertexList; typedef VertexList::iterator VertexIterator; typedef VertexList::reverse_iterator VertexRIterator; 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 float m_EditBox[4]; // box the user drags to select new points float m_SelectionBox[4]; float m_StartX, m_StartY; 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); int GetNumberOfSelectedSegments(); double m_FreehandFittingRate; // Colors used to draw polygon const static float m_DrawingModeColor[], m_EditModeSelectedColor[], m_EditModeNormalColor[]; // Parent object GenericSliceWindow *m_Parent; }; #endif // __PolygonDrawing_h_ /* *$Log: PolygonDrawing.h,v $ *Revision 1.7 2010/05/27 11:16:22 pyushkevich *Further improved polygon drawing interface * *Revision 1.6 2010/05/27 07:29:36 pyushkevich *New popup menu for polygon drawing, other improvements to polygon tool * *Revision 1.5 2007/12/30 04:05:28 pyushkevich *GPL License * *Revision 1.4 2007/12/25 15:46:23 pyushkevich *Added undo/redo functionality to itk-snap * *Revision 1.3 2007/10/01 00:13:15 pyushkevich *Polygon Drawing updates * *Revision 1.2 2007/09/18 18:42:40 pyushkevich *Added tablet drawing to polygon mode * *Revision 1.1 2006/12/02 04:22:27 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:18 pauly2 *Import * *Revision 1.12 2005/12/19 03:43:12 pauly *ENH: SNAP enhancements and bug fixes for 1.4 release * *Revision 1.11 2005/02/04 17:01:09 lorensen *COMP: last of gcc 2.96 changes (I hope). * *Revision 1.10 2005/02/04 14:17:10 lorensen *COMP: gcc 2.96 problems. * *Revision 1.8 2004/07/22 19:22:51 pauly *ENH: Large image support for SNAP. This includes being able to use more screen real estate to display a slice, a fix to the bug with manual segmentation of images larger than the window size, and a thumbnail used when zooming into the image. * *Revision 1.7 2004/01/27 17:34:00 pauly *FIX: Compiling on Mac OSX, issue with GLU include file * *Revision 1.6 2003/10/09 22:45:15 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.5 2003/10/02 20:57:46 pauly *FIX: Made sure that the previous check-in compiles on Linux * *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:01 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.4 2003/07/12 01:34:18 pauly *More final changes before ITK checkin * *Revision 1.3 2003/07/11 23:28:10 pauly **** empty log message *** * *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.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:30 moon *Added Header and Log tags to all files **/ itksnap/UserInterface/SliceWindow/PolygonInteractionMode.cxx0000644000076500000240000000605211377417540023726 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PolygonInteractionMode.cxx,v $ Language: C++ Date: $Date: 2010/05/27 07:29:36 $ Version: $Revision: 1.8 $ Copyright (c) 2007 Paul A. Yushkevich 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 "PolygonInteractionMode.h" #include "GlobalState.h" #include "PolygonDrawing.h" #include "UserInterfaceBase.h" #include "IRISApplication.h" PolygonInteractionMode ::PolygonInteractionMode(GenericSliceWindow *parent) : GenericSliceWindow::EventHandler(parent) { m_Drawing = new PolygonDrawing(parent); } PolygonInteractionMode ::~PolygonInteractionMode() { delete m_Drawing; } int PolygonInteractionMode ::OnEitherEvent(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)) { // We'll need these shorthands int id = m_Parent->m_Id; #ifdef DRAWING_LOCK if (!m_GlobalState->GetDrawingLock(id)) break; #endif /* DRAWING_LOCK */ // Compute the dimension of a pixel on the screen Vector2f pixelSize = GetPixelSizeVector(); // Masquerade keypress events as mouse-clicks at the cursor position int rc; if(event.Key == ' ') { // Map the cursor position into slice coordinates Vector3f xEvent = m_Parent->MapImageToSlice( to_float(m_Driver->GetCursorPosition())); // Get the event state based on shift-ctrl int fakeButton = (event.State & FL_SHIFT) ? FL_RIGHT_MOUSE : FL_LEFT_MOUSE; // Handle the event rc = m_Drawing->Handle(FL_PUSH, fakeButton, xEvent(0), xEvent(1), pixelSize(0),pixelSize(1)); } else { // Map the event into slice coordinates Vector3f xEvent = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); // Handle the event rc = m_Drawing->Handle(event.Id,event.SoftButton,xEvent(0),xEvent(1), pixelSize(0),pixelSize(1)); } #ifdef DRAWING_LOCK m_GlobalState->ReleaseDrawingLock(id); #endif /* DRAWING_LOCK */ // Update the display m_Parent->GetCanvas()->redraw(); // Let the parent UI know that the polygon state has changed m_ParentUI->OnPolygonStateUpdate(id); // Even though no action may have been performed, we don't want other handlers // to get the left and right mouse button events return rc; } Vector2f PolygonInteractionMode ::GetPixelSizeVector() { Vector3f x = m_Parent->MapWindowToSlice(Vector2f(1.0f)) - m_Parent->MapWindowToSlice(Vector2f(0.0f)); return Vector2f(x[0],x[1]); } void PolygonInteractionMode ::OnDraw() { // Compute the dimension of a pixel on the screen Vector2f pixelSize = GetPixelSizeVector(); // Call the poly's draw method m_Drawing->Draw(pixelSize(0),pixelSize(1)); } itksnap/UserInterface/SliceWindow/PolygonInteractionMode.h0000644000076500000240000000617610735620727023362 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PolygonInteractionMode.h,v $ Language: C++ Date: $Date: 2007/12/30 04:43:03 $ 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 __PolygonInteractionMode_h_ #define __PolygonInteractionMode_h_ #include "GenericSliceWindow.h" #include "PolygonDrawing.h" /** * \class PolygonInteractionMode * \brief UI interaction mode that takes care of polygon drawing and editing. * * \see GenericSliceWindow */ class PolygonInteractionMode : public GenericSliceWindow::EventHandler { public: PolygonInteractionMode(GenericSliceWindow *parent); virtual ~PolygonInteractionMode(); int OnMousePress(const FLTKEvent &event) { return OnEitherEvent(event,event); } int OnKeyDown(const FLTKEvent &event) { return OnEitherEvent(event,event); } int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent) { return OnEitherEvent(event,pressEvent); } int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent) { return OnEitherEvent(event,pressEvent); } int OnShortcut(const FLTKEvent &event) { return OnEitherEvent(event,event); } void OnDraw(); private: /** * The polygon handling object */ PolygonDrawing *m_Drawing; /** Handler that gets envoked regardless of the event */ int OnEitherEvent(const FLTKEvent &event, const FLTKEvent &pressEvent); /** Get a vector that represents the size of the pixel on the screen */ Vector2f GetPixelSizeVector(); // The window handler needs to access our privates friend class IRISSliceWindow; }; #endif // __PolygonInteractionMode_h_ itksnap/UserInterface/SliceWindow/PolygonScanConvert.cxx0000644000076500000240000001330511136422002023044 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PolygonScanConvert.cxx,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ Version: $Revision: 1.8 $ 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 "PolygonScanConvert.h" #include "SNAPCommon.h" #include "itkOrientedImage.h" #include // Typecast for the callback functions #ifdef WIN32 typedef void (CALLBACK *TessCallback)(); #elif defined (__APPL__) typedef GLvoid (*TessCallback)(...); #else typedef void (*TessCallback)(); #endif void PolygonScanConvertBase ::RasterizeFilled(double *vArray, unsigned int nVertices, unsigned int width, unsigned int height, GLenum glType, void *buffer) { // Push the GL attributes to preserve everything glPushAttrib(GL_ALL_ATTRIB_BITS); // Tesselate the polygon and save the tesselation as a display list GLint dl = glGenLists(1); glNewList(dl, GL_COMPILE); // Set the background to black glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Paint in white glColor3d(1.0, 1.0, 1.0); // Start the tesselation GLUtesselator *tess = gluNewTess(); gluTessCallback(tess,(GLenum) GLU_TESS_VERTEX, (TessCallback) &glVertex3dv); gluTessCallback(tess,(GLenum) GLU_TESS_BEGIN, (TessCallback) &glBegin); gluTessCallback(tess,(GLenum) GLU_TESS_END, (TessCallback) &glEnd); gluTessCallback(tess,(GLenum) GLU_TESS_ERROR, (TessCallback) &PolygonScanConvertBase::ErrorCallback); gluTessCallback(tess,(GLenum) GLU_TESS_COMBINE, (TessCallback) &PolygonScanConvertBase::CombineCallback); gluTessProperty(tess,(GLenum) GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); gluTessNormal(tess,0.0,0.0,1.0); gluTessBeginPolygon(tess,NULL); gluTessBeginContour(tess); // Add the vertices for(unsigned int i=0; i < nVertices; i++) { gluTessVertex(tess, vArray + 3*i, vArray + 3*i); } // End the tesselation gluTessEndContour(tess); gluTessEndPolygon(tess); // End the display list glEndList(); // Draw polygon into back buffer - back buffer should get redrawn // anyway before it gets swapped to the screen. glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); // We will perform a tiled drawing, because the backbuffer may be smaller // than the size of the image. First get the viewport size, i.e., tile size GLint xViewport[4]; glGetIntegerv(GL_VIEWPORT, xViewport); unsigned int wTile = (unsigned int) xViewport[2]; unsigned int hTile = (unsigned int) xViewport[3]; // Figure out the number of tiles in x and y dimension unsigned int nTilesX = (unsigned int) ceil( width * 1.0 / wTile ); unsigned int nTilesY = (unsigned int) ceil( height * 1.0 / hTile ); // Draw and retrieve each tile for(unsigned int iTileX = 0; iTileX < nTilesX; iTileX++) { for(unsigned int iTileY = 0; iTileY < nTilesY; iTileY++) { // Get the corner of the tile unsigned int xTile = iTileX * wTile, yTile = iTileY * hTile; // Set the projection matrix glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(xTile, xTile + wTile, yTile, yTile + hTile); // Set the model view matrix glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Draw the triangles glCallList(dl); // Figure out the size of the data chunk to copy unsigned int wCopy = width - xTile < wTile ? width - xTile : wTile; unsigned int hCopy = height - yTile < hTile ? height - yTile : hTile; // Set up the copy so that the strides are correct glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ROW_LENGTH, width); glPixelStorei(GL_PACK_SKIP_PIXELS, xTile); glPixelStorei(GL_PACK_SKIP_ROWS, yTile); // Copy the pixels to the buffer glReadPixels(0, 0, wCopy, hCopy, GL_RED, glType, buffer); // Restore the GL state glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } } // Get rid of the display list glDeleteLists(dl,1); // Delete the tesselator gluDeleteTess(tess); // Restore the GL state glPopAttrib(); } void PolygonScanConvertBase::ErrorCallback(GLenum errorCode) { std::cerr << "Tesselation Error: " << gluErrorString(errorCode) << std::endl; } void PolygonScanConvertBase::CombineCallback(GLdouble coords[3], GLdouble **vertex_data, GLfloat *weight, GLdouble **dataOut) { GLdouble *vertex = new GLdouble[3]; vertex[0] = coords[0]; vertex[1] = coords[1]; vertex[2] = coords[2]; *dataOut = vertex; } itksnap/UserInterface/SliceWindow/PolygonScanConvert.h0000644000076500000240000000603311136422002022471 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PolygonScanConvert.h,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. =========================================================================*/ #ifndef __PolygonScanConvert_h_ #define __PolygonScanConvert_h_ #include "SNAPOpenGL.h" #include "itkOrientedImage.h" class PolygonScanConvertBase { public: // Private version of the method, takes a flattened array of coordinates // and is not templated static void RasterizeFilled( double *vArray, unsigned int nVertices, unsigned int width, unsigned int height, GLenum glType, void *buffer); // Callbacks for tesselation static void ErrorCallback(GLenum errorCode); static void CombineCallback( GLdouble coords[3], GLdouble **vertex_data, GLfloat *weight, GLdouble **dataOut); }; template class PolygonScanConvert : public PolygonScanConvertBase { public: typedef itk::Image ImageType; /** * This method uses OpenGL to scan-convert a polygonal curve. The input is a pair * of iterators (begin, end) to a list/array of double arrays or vectors, i.e., * objects for which indices [0] and [1] are supported. */ static void RasterizeFilled( TVertexIterator first, unsigned int n, ImageType *image) { double *vArray = new double[3 * (n + 1)], *vPointer = vArray; for (unsigned int i = 0; i < n; ++i, ++first) { *vPointer++ = (double) (*first)[0]; *vPointer++ = (double) (*first)[1]; *vPointer++ = 0.0; } // Set up the image properties unsigned int width = image->GetBufferedRegion().GetSize()[0]; unsigned int height = image->GetBufferedRegion().GetSize()[1]; PolygonScanConvertBase::RasterizeFilled( vArray, n, width, height, VGlPixelType, image->GetBufferPointer()); delete vArray; } private: }; #endif itksnap/UserInterface/SliceWindow/PopupButtonInteractionMode.cxx0000644000076500000240000000662211455103175024572 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PopupButtonInteractionMode.cxx,v $ Language: C++ Date: $Date: 2010/10/12 16:02:05 $ 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 "PopupButtonInteractionMode.h" #include "SNAPOpenGL.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "SNAPAppearanceSettings.h" #include "UserInterfaceBase.h" #include "SliceWindowCoordinator.h" #include "UserInterfaceLogic.h" PopupButtonInteractionMode ::PopupButtonInteractionMode(GenericSliceWindow *parent) : GenericSliceWindow::EventHandler(parent) { } #include #include int PopupButtonInteractionMode ::OnMousePress (const FLTKEvent &event) { // Only draw when UI is not visible DisplayLayout dl = m_ParentUI->GetDisplayLayout(); if(dl.show_main_ui) return 0; // Get the coordiantes of the mouse click int w = this->GetCanvas()->w(); int h = this->GetCanvas()->h(); int x = event.XCanvas[0], y = event.XCanvas[1]; if(x <= w-5 && x >= w-15 && y <= h-5 && y >= h-15 && (x - (w-15)) > ((h-5) - y)) { // Dynamically create menu, pop it up Fl_Menu_Button menu(Fl::event_x_root(), Fl::event_y_root(), 80, 1); Fl_Menu_Bar *bar = m_ParentUI->GetMainMenuBar(); menu.copy(bar->menu()); // Put the menu so that it's as deep in the widget hierarchy as the main menubar Fl_Group g1(Fl::event_x_root(), Fl::event_y_root(), 80, 1); Fl_Group g2(Fl::event_x_root(), Fl::event_y_root(), 80, 1); m_ParentUI->GetMainWindow()->add(g1); g1.add(g2); g2.add(menu); // Popup menu.popup(); m_ParentUI->GetMainWindow()->remove(g1); g1.remove(g2); g2.remove(menu); // Eat up the event return 1; } return 0; } void PopupButtonInteractionMode ::OnDraw() { // Only draw when UI is not visible DisplayLayout dl = m_ParentUI->GetDisplayLayout(); if(dl.show_main_ui) return; // Set line properties glPushAttrib(GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT); glColor3d(0.0, 0.75, 0.0); int w = this->GetCanvas()->w(); int h = this->GetCanvas()->h(); glDisable(GL_LIGHTING); glBegin(GL_TRIANGLES); glVertex2f(w-5, h-5); glVertex2f(w-5, h-15); glVertex2f(w-15, h-5); glEnd(); glPopAttrib(); } itksnap/UserInterface/SliceWindow/PopupButtonInteractionMode.h0000644000076500000240000000361011377417540024220 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: PopupButtonInteractionMode.h,v $ Language: C++ Date: $Date: 2010/05/27 07:29:36 $ Version: $Revision: 1.1 $ 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 __PopupButtonInteractionMode_h_ #define __PopupButtonInteractionMode_h_ #include "GenericSliceWindow.h" #include "GlobalState.h" /** * \class PopupButtonInteractionMode * \brief UI interaction mode that takes care of crosshair positioning. * * \see GenericSliceWindow */ class PopupButtonInteractionMode : public GenericSliceWindow::EventHandler { public: PopupButtonInteractionMode(GenericSliceWindow *parent); void OnDraw(); int OnMousePress(const FLTKEvent &event); }; #endif // __PopupButtonInteractionMode_h_ itksnap/UserInterface/SliceWindow/RegionInteractionMode.cxx0000644000076500000240000002365210753707352023527 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RegionInteractionMode.cxx,v $ Language: C++ Date: $Date: 2008/02/10 23:55:22 $ 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. =========================================================================*/ #include "RegionInteractionMode.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "IRISSliceWindow.h" #include "UserInterfaceBase.h" #include "IRISVectorTypesToITKConversion.h" #include "SNAPAppearanceSettings.h" #include #include // The click detection radius (delta) const unsigned int RegionInteractionMode::m_PixelDelta = 4; RegionInteractionMode ::RegionInteractionMode(GenericSliceWindow *parent) :GenericSliceWindow::EventHandler(parent) { // Initialize the edges for(unsigned int dir=0;dir<2;dir++) { for(unsigned int i=0;i<2;i++) { m_EdgeHighlighted[dir][i] = false; } } } void RegionInteractionMode ::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 RegionInteractionMode ::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; } int RegionInteractionMode ::OnMousePress(const FLTKEvent &event) { // Flag indicating whether we respond to this event or not m_IsAnyEdgeHighlighted = false; // Convert the event location into slice u,v coordinates Vector3f xSlice = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); Vector2f uvSlice(xSlice(0),xSlice(1)); // Record the system's corners at the time of drag start GetSystemROICorners(m_CornerDragStart); // 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,m_CornerDragStart); if(d < dToClosest) { dToClosest = d; iClosest = i; } } // Highlight the selected edge if(iClosest >= 0) { m_EdgeHighlighted[dir][iClosest] = true; m_IsAnyEdgeHighlighted = true; } } // If nothing was highlighted, then return and let the next handler process // the event if(!m_IsAnyEdgeHighlighted) return 0; // Event has been handled return 1; } void RegionInteractionMode ::GetSystemROICorners(Vector3f corner[2]) { // Get the region of interest in image coordinates GlobalState::RegionType roi = m_GlobalState->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 RegionInteractionMode ::UpdateCorners(const FLTKEvent &event, const FLTKEvent &pressEvent) { // Compute the corners in slice coordinates Vector3f corner[2]; GetSystemROICorners(corner); // Convert the location of the events into slice u,v coordinates Vector3f uvSliceNow = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); Vector3f uvSlicePress = m_Parent->MapWindowToSlice(pressEvent.XSpace.extract(2)); // Get the current bounds and extents of the region of interest Vector3f xCornerImage[2] = { m_Parent->MapSliceToImage(corner[0]), m_Parent->MapSliceToImage(corner[1]) }; // TODO: For dragging entire region, the clamps should be just image extents // Compute the clamps for each of the corners 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_Driver->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_EdgeHighlighted[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::RegionType roiSystem = m_GlobalState->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->m_ImageAxes[2]; roiCorner.SetIndex(idx,roiSystem.GetIndex(idx)); roiCorner.SetSize(idx,roiSystem.GetSize(idx)); // Update the system's ROI m_GlobalState->SetSegmentationROI(roiCorner); // Cause a system redraw m_ParentUI->RedrawWindows(); } int RegionInteractionMode ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent) { // Only do something if there is a highlight if(m_IsAnyEdgeHighlighted) { // Update the corners in response to the dragging UpdateCorners(event,pressEvent); // Event has been handled return 1; } return 0; } int RegionInteractionMode ::OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent) { // Only do something if there is a highlight if(m_IsAnyEdgeHighlighted) { // Update the corners in response to the dragging UpdateCorners(event,pressEvent); // Clear highlights of the connected edges for(unsigned int i=0;i<2;i++) m_EdgeHighlighted[0][i] = m_EdgeHighlighted[1][i] = false; // Clear the summary highlight flag m_IsAnyEdgeHighlighted = false; // Event has been handled return 1; } // Event has not been handled return 0; } void RegionInteractionMode ::OnDraw() { // The region of interest should be in effect assert(m_GlobalState->GetIsValidROI()); // Compute the corners in slice coordinates Vector3f corner[2]; GetSystemROICorners(corner); // Check that the current slice is actually within the bounding box // int slice = m_Parent->m_SliceIndex; int dim = m_Parent->m_ImageAxes[2]; int slice = m_Driver->GetCursorPosition()[dim]; int bbMin = m_GlobalState->GetSegmentationROI().GetIndex(dim); int bbMax = bbMin + m_GlobalState->GetSegmentationROI().GetSize(dim); // And if so, return without painting anything if(bbMin > slice || bbMax <= slice) return; // Get the line color, thickness and dash spacing const SNAPAppearanceSettings::Element &elt = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::ROI_BOX); // Set line properties glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT); // Apply the line properties SNAPAppearanceSettings::ApplyUIElementLineSettings(elt); // Start drawing the lines glBegin(GL_LINES); // Draw each of the edges for(unsigned int dir=0;dir<2;dir++) { for(unsigned int i=0;i<2;i++) { // Select color according to edge state glColor3dv( m_EdgeHighlighted[dir][i] ? elt.ActiveColor.data_block() : elt.NormalColor.data_block() ); // Compute the vertices of the edge Vector2f x0,x1; GetEdgeVertices(dir,i,x0,x1,corner); // Draw the line glVertex2f(x0[0],x0[1]); glVertex2f(x1[0],x1[1]); } } glEnd(); glPopAttrib(); } itksnap/UserInterface/SliceWindow/RegionInteractionMode.h0000644000076500000240000000633510735614410023143 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: RegionInteractionMode.h,v $ Language: C++ Date: $Date: 2007/12/30 04:05:28 $ 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 __RegionInteractionMode_h_ #define __RegionInteractionMode_h_ #include "GenericSliceWindow.h" /** * \class RegionInteractionMode * \brief UI interaction mode that takes care of ROI positioning. * * \see GenericSliceWindow */ class RegionInteractionMode : public GenericSliceWindow::EventHandler { public: RegionInteractionMode(GenericSliceWindow *parent); int OnMousePress(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent); void OnDraw(); private: // 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) */ bool m_EdgeHighlighted[2][2]; /** * Is any one of the edges highlighted? */ bool m_IsAnyEdgeHighlighted; /** 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 FLTKEvent &event,const FLTKEvent &pressEvent); }; #endif // __RegionInteractionMode_h_ itksnap/UserInterface/SliceWindow/SliceWindowCoordinator.cxx0000644000076500000240000001303411455120567023720 0ustar paulystaff/*========================================================================= 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 "IRISApplication.h" #include "IRISImageData.h" #include "OpenGLSliceTexture.h" #include "UserInterfaceBase.h" SliceWindowCoordinator ::SliceWindowCoordinator() { m_Window[0] = m_Window[1] = m_Window[2] = NULL; m_Canvas[0] = m_Canvas[1] = m_Canvas[2] = NULL; m_LinkedZoom = false; m_WindowsRegistered = false; } SliceWindowCoordinator ::~SliceWindowCoordinator() { } void SliceWindowCoordinator ::RegisterWindows(GenericSliceWindow *windows[3]) { for(unsigned int i=0;i<3;i++) { m_Window[i] = windows[i]; m_Canvas[i] = m_Window[i]->GetCanvas(); m_Window[i]->SetManagedZoom(m_LinkedZoom); } m_WindowsRegistered = true; } void SliceWindowCoordinator ::SetLinkedZoom(bool flag) { m_LinkedZoom = flag; if(m_WindowsRegistered) { // Tell the windows whether they are managed or not for(unsigned int i=0;i<3;i++) m_Window[i]->SetManagedZoom(m_LinkedZoom); // Set the common zoom if(m_LinkedZoom) SetCommonZoomToSmallestWindowZoom(); } } void SliceWindowCoordinator ::ResetViewToFitInAllWindows() { // Only if initialized assert(m_WindowsRegistered); // Set each zoom to optimal value for that window for(unsigned int i=0;i<3;i++) { m_Window[i]->ResetViewToFit(); } // If linked, zoom back to smallest zoom if(m_LinkedZoom) SetCommonZoomToSmallestWindowZoom(); } void SliceWindowCoordinator ::SetZoomFactorAllWindows(float factor) { // First, fit all windows ResetViewToFitInAllWindows(); // Now scale the zoom in each window for(unsigned int i=0;i<3;i++) { m_Window[i]->SetViewZoom(m_Window[i]->GetViewZoom() * factor); } } void SliceWindowCoordinator ::SetZoomLevelAllWindows(float level) { // Now scale the zoom in each window for(unsigned int i=0;i<3;i++) { m_Window[i]->SetViewZoom(level); } } void SliceWindowCoordinator ::ResetViewToFitInOneWindow(unsigned int window) { // Only if initialized assert(m_WindowsRegistered); // Reset zoom to fit in the current window m_Window[window]->ResetViewToFit(); if(m_LinkedZoom) SetZoomLevelAllWindows(m_Window[window]->GetViewZoom()); } void SliceWindowCoordinator ::OnZoomUpdateInWindow(unsigned int irisNotUsed(window), float zoom) { // Only if initialized assert(m_WindowsRegistered); if(m_LinkedZoom) { for(unsigned int i=0;i<3;i++) { m_Window[i]->SetViewZoom(zoom); } } } void SliceWindowCoordinator ::OnWindowResize() { if(m_LinkedZoom) SetCommonZoomToSmallestWindowZoom(); } void SliceWindowCoordinator ::SetCommonZoomToSmallestWindowZoom() { // Compute the minimum zoom float minZoom = m_Window[0]->GetViewZoom(); for(unsigned int i=1;i<3;i++) { if(minZoom > m_Window[i]->GetViewZoom()) minZoom = m_Window[i]->GetViewZoom(); } // Assign the minimum zoom for(unsigned int j=0;j<3;j++) { m_Window[j]->SetViewZoom(minZoom); } } float SliceWindowCoordinator ::GetCommonZoomLevel() { assert(m_LinkedZoom && m_WindowsRegistered); return m_Window[0]->GetViewZoom(); } float SliceWindowCoordinator ::GetCommonOptimalFitZoomLevel() { assert(m_LinkedZoom && m_WindowsRegistered); return m_Window[0]->GetOptimalZoom(); } float SliceWindowCoordinator ::ClampZoom(unsigned int window,float zoom) { assert(m_WindowsRegistered); float maxZoom = 0.0f; float minZoom = 0.0f; for(unsigned int i=0;i<3;i++) { if(m_LinkedZoom || window == i) { // Maximum zoom is constrained by the requirement that at least four // pixels are visible in at least one dimensions float zMax1 = 0.25 * m_Canvas[i]->w() / m_Window[i]->GetSliceSpacing()[0]; float zMax2 = 0.25 * m_Canvas[i]->h() / m_Window[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_Window[i]->GetOptimalZoom(); minZoom = (minZoom == 0.0f || minZoom > zMin) ? zMin : minZoom; } } // Apply the clamp if(zoom < minZoom) return minZoom; if(zoom > maxZoom) return maxZoom; return zoom; } itksnap/UserInterface/SliceWindow/SliceWindowCoordinator.h0000644000076500000240000001162311177646375023362 0ustar paulystaff/*========================================================================= 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 "GenericSliceWindow.h" /** * \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: /** Constructor */ SliceWindowCoordinator(); /** Virtual destructor */ virtual ~SliceWindowCoordinator(); /** Assigns three windows for the coordinator to manage */ void RegisterWindows(GenericSliceWindow *windows[3]); /** Specify whether the coordinator should maintain linked zoom * in the three slice windows */ void SetLinkedZoom(bool flag); /** Specify whether the coordinator should maintain linked zoom * in the three slice windows */ irisGetMacro(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); /** 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); /** 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(); /** Constrain a zoom factor to reasonable limits */ float ClampZoom(unsigned int window,float zoom); /** Get the window number n */ GenericSliceWindow *GetWindow(unsigned int window) { return m_Window[window]; } protected: /** Pointer to the application driver for this UI object */ //IRISApplication *m_Driver; /** Pointer to the global state object (shorthand) */ //GlobalState *m_GlobalState; /** Pointer to GUI that contains this Window3D object */ //UserInterfaceBase *m_ParentUI; /** The image data object that is displayed in this window */ //GenericImageData *m_ImageData; /** The pointers to three window interactors managed by this class */ GenericSliceWindow *m_Window[3]; /** The pointers to the windows managed by the interactors */ FLTKCanvas *m_Canvas[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(); }; #endif // __SliceWindowCoordinator_h_ itksnap/UserInterface/SliceWindow/SNAPSliceWindow.cxx0000644000076500000240000001242011245054710022165 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPSliceWindow.cxx,v $ Language: C++ Date: $Date: 2009/08/25 21:38:16 $ Version: $Revision: 1.7 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include #include typedef itk::SmartPointer SNAPSliceWindowDummyCommandSPType; #include "SNAPBorlandDummyTypes.h" #endif #include "SNAPSliceWindow.h" #include "BubblesInteractionMode.h" #include "GlobalState.h" #include "SNAPImageData.h" #include "SNAPAppearanceSettings.h" #include "OpenGLSliceTexture.h" #include "UserInterfaceBase.h" SNAPSliceWindow ::SNAPSliceWindow(int id, UserInterfaceBase *parentUI, FLTKCanvas *canvas) : GenericSliceWindow(id, parentUI, canvas) { // Initialize the texture for displaying the speed image m_SpeedTexture = new OpenGLSliceTexture; m_SpeedTexture->SetGlComponents(4); m_SpeedTexture->SetGlFormat(GL_RGBA); m_SpeedTexture->SetGlType(GL_UNSIGNED_BYTE); // Initialize the snake image texture m_SnakeTexture = new OpenGLSliceTexture; m_SnakeTexture->SetGlComponents(4); m_SnakeTexture->SetGlFormat(GL_RGBA); m_SnakeTexture->SetGlType(GL_UNSIGNED_BYTE); // Initialize the overlay texture m_OverlayTexture = new OpenGLSliceTexture; m_OverlayTexture->SetGlComponents(4); m_OverlayTexture->SetGlFormat(GL_RGBA); m_OverlayTexture->SetGlType(GL_UNSIGNED_BYTE); // Initialize the bubbles interaction mode m_BubblesMode = new BubblesInteractionMode(this); // Register the interaction modes m_BubblesMode->Register(); } SNAPSliceWindow ::~SNAPSliceWindow() { delete m_SpeedTexture; } void SNAPSliceWindow ::InitializeSlice(GenericImageData *imageData) { // Call parent's init GenericSliceWindow::InitializeSlice(imageData); if(!imageData->IsMainLoaded()) return; // Set the pointer to the SNAP data m_ImageData = dynamic_cast(imageData); // Test that the cast worked assert(m_ImageData); } void SNAPSliceWindow ::DrawMainTexture() { // The preprocessing image is shown when the corresponding flag is set and // the display mode is not set to overlay if(m_GlobalState->GetShowSpeed() && !m_GlobalState->GetSpeedViewZero()) { // We should have a slice to draw assert(m_ImageData->IsSpeedLoaded() && m_ImageSliceIndex >= 0); // Get the color to use for background Vector3d clrBackground = m_ThumbnailIsDrawing ? m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::ZOOM_THUMBNAIL).NormalColor : Vector3d(1.0); // Make sure the correct image is pointed to m_SpeedTexture->SetImage(m_ImageData->GetSpeed()->GetDisplaySlice(m_Id).GetPointer()); // Paint the grey texture m_SpeedTexture->Draw(clrBackground); } else { GenericSliceWindow::DrawMainTexture(); } } void SNAPSliceWindow ::DrawSegmentationTexture() { // First check if we are in overlay drawing mode if(m_GlobalState->GetSpeedViewZero()) { // We should have a slice to draw assert(m_ImageData->IsSpeedLoaded() && m_ImageSliceIndex >= 0); // Make sure that the right image is there m_OverlayTexture->SetImage(m_ImageData->GetSpeed()->GetOverlaySlice(m_Id)); // Paint the grey texture m_OverlayTexture->DrawTransparent(m_GlobalState->GetSegmentationAlpha()); } // Otherwize draw the snake else if(m_GlobalState->GetSnakeActive()) { // We should have a slice to draw assert(m_ImageData->IsSnakeLoaded() && m_ImageSliceIndex >= 0); // Make sure that the right image is there m_SnakeTexture->SetImage(m_ImageData->GetSnake()->GetDisplaySlice(m_Id).GetPointer()); // Paint the grey texture m_SnakeTexture->DrawTransparent(m_GlobalState->GetSegmentationAlpha()); } // Otherwize use the parent's method else { // Call the parent's method GenericSliceWindow::DrawSegmentationTexture(); } } void SNAPSliceWindow ::DrawOverlays() { // Call the parent's code GenericSliceWindow::DrawOverlays(); // Draw the bubbles if(!m_ThumbnailIsDrawing) m_BubblesMode->OnDraw(); } itksnap/UserInterface/SliceWindow/SNAPSliceWindow.h0000644000076500000240000000520511245037612021617 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPSliceWindow.h,v $ Language: C++ Date: $Date: 2009/08/25 19:46:18 $ 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 __SNAPSliceWindow_h_ #define __SNAPSliceWindow_h_ #include "GenericSliceWindow.h" // Forward references class SNAPImageData; class BubblesInteractionMode; /** * \class SNAPSliceWindow * \brief The window used to display slices in SnAP part of the application. * * SnAP not only has the original greyscale image, but also a floating * point preprocessing image. The overlays for SnAP include bubble display. */ class SNAPSliceWindow : public GenericSliceWindow { public: SNAPSliceWindow(int id,UserInterfaceBase *parentUI, FLTKCanvas *canvas); ~SNAPSliceWindow(); /** Overrides the parent's method */ void InitializeSlice(GenericImageData *imageData); protected: // The interaction mode for bubble drawing BubblesInteractionMode *m_BubblesMode; // SNAP image data object displayed in this window (overrides parent's // m_ImageData SNAPImageData *m_ImageData; // Preprocessed slice texture object OpenGLSliceTexture *m_SpeedTexture; // Segmentation slice texture object OpenGLSliceTexture *m_SnakeTexture; // The overlay texture object OpenGLSliceTexture *m_OverlayTexture; // Overlay and texture drawing are customized in this class void DrawOverlays(); void DrawMainTexture(); void DrawSegmentationTexture(); }; #endif // SNAP Slice Window itksnap/UserInterface/SliceWindow/ThumbnailInteractionMode.cxx0000644000076500000240000000667411215766401024226 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ThumbnailInteractionMode.cxx,v $ Language: C++ Date: $Date: 2009/06/16 19:05:37 $ 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. =========================================================================*/ #include "ThumbnailInteractionMode.h" #include "IRISApplication.h" #include "IRISImageData.h" #include "UserInterfaceBase.h" ThumbnailInteractionMode ::ThumbnailInteractionMode(GenericSliceWindow *parent) : GenericSliceWindow::EventHandler(parent) { m_PanFlag = false; } int ThumbnailInteractionMode ::OnMousePress(const FLTKEvent &event) { // Clear the pan flag m_PanFlag = false; // Only react to left mouse button presses if(event.SoftButton == FL_LEFT_MOUSE) { // Check if the event is inside of the thumbnail boundaries Vector2i xThumb = m_Parent->m_ThumbnailPosition; Vector2i sThumb = m_Parent->m_ThumbnailSize; if(m_Parent->IsThumbnailOn() && event.XCanvas[0] > xThumb[0] && event.XCanvas[0] < xThumb[0] + sThumb[0] && event.XCanvas[1] > xThumb[1] && event.XCanvas[1] < xThumb[1] + sThumb[1]) { // Set the panning flag m_PanFlag = true; // Store the starting view position m_StartViewPosition = m_Parent->m_ViewPosition; // Return success return 1; } } // Pass the event on return 0; } int ThumbnailInteractionMode ::OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent) { // Call the drag code return OnMouseDrag(event, pressEvent); } int ThumbnailInteractionMode ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent) { // Must be in pan mode if(m_PanFlag && event.SoftButton == FL_LEFT_MOUSE) { // Get the number of pixels that the thumbnail was moved by Vector2i xMoved = pressEvent.XCanvas - event.XCanvas; // Figure out how this movement translates to space units Vector2f xOffset( xMoved[0] / m_Parent->m_ThumbnailZoom, xMoved[1] / m_Parent->m_ThumbnailZoom); // Add to the position m_Parent->m_ViewPosition = m_StartViewPosition - xOffset; m_Parent->m_ParentUI->OnViewPositionsUpdate(); // Tell parent to repaint m_Parent->GetCanvas()->redraw(); // The event's been handled return 1; } else return 0; } void ThumbnailInteractionMode:: OnDraw() { } itksnap/UserInterface/SliceWindow/ThumbnailInteractionMode.h0000644000076500000240000000427010735614411023640 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ThumbnailInteractionMode.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 __ThumbnailInteractionMode_h_ #define __ThumbnailInteractionMode_h_ #include "GenericSliceWindow.h" #include "GlobalState.h" /** * \class ThumbnailInteractionMode * \brief UI interaction mode that takes care of the zoom thumbnail shown in the * bottom left corner of the window * * \see GenericSliceWindow */ class ThumbnailInteractionMode : public GenericSliceWindow::EventHandler { public: ThumbnailInteractionMode(GenericSliceWindow *parent); void OnDraw(); int OnMousePress(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &irisNotUsed(pressEvent)); private: bool m_PanFlag; Vector2f m_StartViewPosition; }; #endif // __ThumbnailInteractionMode_h_ itksnap/UserInterface/SliceWindow/ZoomPanInteractionMode.cxx0000644000076500000240000001013511177646375023667 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ZoomPanInteractionMode.cxx,v $ Language: C++ Date: $Date: 2009/05/04 20:15:57 $ 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. =========================================================================*/ #include "ZoomPanInteractionMode.h" #include "UserInterfaceBase.h" #include "SliceWindowCoordinator.h" #include #include ZoomPanInteractionMode ::ZoomPanInteractionMode(GenericSliceWindow *parent) : GenericSliceWindow::EventHandler(parent) { m_NeedUIUpdateOnRepaint = false; } int ZoomPanInteractionMode ::OnMousePress(const FLTKEvent &event) { if(event.SoftButton == FL_LEFT_MOUSE || event.SoftButton == FL_RIGHT_MOUSE) { // Record the current zoom and view position m_StartViewZoom = m_Parent->GetViewZoom(); m_StartViewPosition = m_Parent->m_ViewPosition; // Done return 1; } else return 0; } int ZoomPanInteractionMode ::OnMouseDrag(const FLTKEvent &event,const FLTKEvent &dragEvent) { if (dragEvent.SoftButton == FL_LEFT_MOUSE) { // Compute the start and end point in slice coordinates Vector3f xStart = m_Parent->MapWindowToSlice(dragEvent.XSpace.extract(2)); Vector3f xEnd = m_Parent->MapWindowToSlice(event.XSpace.extract(2)); Vector2f xOffset(xEnd[0] - xStart[0],xEnd[1] - xStart[1]); // Remove the scaling by spacing xOffset(0) *= m_Parent->m_SliceSpacing(0); xOffset(1) *= m_Parent->m_SliceSpacing(1); // Under the left button, the tool changes the view_pos by the // distance traversed m_Parent->SetViewPosition(m_StartViewPosition - xOffset); m_ParentUI->OnViewPositionsUpdate(); } else if (dragEvent.SoftButton == FL_RIGHT_MOUSE) { // Under the right button, the tool causes us to zoom based on the vertical // motion float zoom = m_StartViewZoom * pow(1.02f,(float)(event.XSpace(1) - dragEvent.XSpace(1))); // Clamp the zoom factor to reasonable limits zoom = m_ParentUI->GetSliceCoordinator()->ClampZoom(m_Parent->m_Id,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); m_ParentUI->GetSliceCoordinator()->OnZoomUpdateInWindow(m_Parent->m_Id,zoom); // Schedule an update of the zoom percentage display in the parent m_NeedUIUpdateOnRepaint = true; } else return 0; // Redraw the screen m_Parent->GetCanvas()->redraw(); // Nothing to do here. return 1; } int ZoomPanInteractionMode ::OnMouseRelease(const FLTKEvent &event, const FLTKEvent &irisNotUsed(dragEvent)) { // Eat the left and right buttons if(event.SoftButton == FL_LEFT_MOUSE || event.SoftButton == FL_RIGHT_MOUSE) return 1; else return 0; } void ZoomPanInteractionMode ::OnDraw() { if(m_NeedUIUpdateOnRepaint) { m_ParentUI->OnZoomUpdate(); m_NeedUIUpdateOnRepaint = false; } } itksnap/UserInterface/SliceWindow/ZoomPanInteractionMode.h0000644000076500000240000000433110735614411023276 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: ZoomPanInteractionMode.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 __ZoomPanInteractionMode_h_ #define __ZoomPanInteractionMode_h_ #include "GenericSliceWindow.h" /** * \class ZoomPanInteractionMode * \brief UI interaction mode that takes care of zooming and panning. * * \see GenericSliceWindow */ class ZoomPanInteractionMode : public GenericSliceWindow::EventHandler { public: ZoomPanInteractionMode(GenericSliceWindow *parent); int OnMousePress(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent); void OnDraw(); // void OnDraw(); protected: /** The starting point for panning */ Vector2f m_StartViewPosition; /** The starting zoom level */ float m_StartViewZoom; /** Used to schedule UI repaint updates */ bool m_NeedUIUpdateOnRepaint; }; #endif // __ZoomPanInteractionMode_h_ itksnap/UserInterface/SNAPMain.cxx0000644000076500000240000005713611560267766016431 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: SNAPMain.cxx,v $ Language: C++ Date: $Date: 2011/05/04 15:25:42 $ 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. =========================================================================*/ // Borland compiler is very lazy so we need to instantiate the template // by hand #if defined(__BORLANDC__) #include "SNAPBorlandDummyTypes.h" #endif #include "FL/Fl.H" #include "FL/Fl_Native_File_Chooser.H" #include #include "CommandLineArgumentParser.h" #include "ImageCoordinateGeometry.h" #include "ImageIORoutines.h" #include "IRISApplication.h" #include "IRISException.h" #include "IRISImageData.h" #include "SNAPRegistryIO.h" #include "SystemInterface.h" #include "UserInterfaceLogic.h" #include "itkOrientedImage.h" #include "itkImageFileReader.h" #include "itkNumericTraits.h" #include //#include using namespace std; // Define the verbose output stream ostream &verbose = cout; // MAX grey value - TODO find somewhere to stick this const GreyType MAXGREYVAL = itk::NumericTraits::max(); const GreyType MINGREYVAL = itk::NumericTraits::min(); #include "Registry.h" // A templated load image method template bool LoadImageFromFileInteractive( const char *file, typename itk::SmartPointer< itk::OrientedImage > &target) { try { LoadImageFromFile(file,target); return true; } catch(itk::ExceptionObject &exc) { cerr << "Error loading file '" << file << "'" << endl; cerr << "Reason: " << exc << endl; return false; } } /** Make sure we have a path to the SNAP root directory * so that we can read external files */ bool FindDataDirectoryInteractive(const char *sExePath, SystemInterface &system) { // Try to find the root directory while(!system.FindDataDirectory(sExePath)) { // Tell the user the directory is missing int choice = fl_choice("The SNAP root directory could not be found!\n" "Please click 'Search' to find this directory\n" "or click 'Exit' to terminate the program.", "Exit Program","Browse...","More Info..."); if(choice == 0) { // Exit the application return false; } else if(choice == 2) { // More help fl_message(" SNAP could not find its help files and other data that it needs\n" "to run properly. These files should be located in a subdirectory \n" "called ProgramData in the same directory where the SNAP executable \n" "is located. \n" " If you are not sure where to look, search your computer's hard drive \n" "for a file called '%s', and then use the Browse... button to tell SNAP \n" "where this file is located.\n" " For more help, consult your system administrator.", system.GetProgramDataDirectoryTokenFileName()); } else { // The file we're looking for string sMissingFile = system.GetProgramDataDirectoryTokenFileName(); string sTitle = "Find a directory that contains file " + sMissingFile; // Look for the file using a file chooser Fl_Native_File_Chooser fc; fc.type(Fl_Native_File_Chooser::BROWSE_FILE); fc.title(sTitle.c_str()); fc.filter("Directory Token File\t*.txt"); fc.preset_file(sMissingFile.c_str()); // Show the file chooser const char *fName = NULL; if (fc.show()) { fName = fc.filename(); } // If user hit cancel, continue if(!fName || !strlen(fName)) continue; // Make sure that the filename matches and get the path string sBrowseName = itksys::SystemTools::GetFilenameName(fName); // If not, check if they've selected a valid directory if(sBrowseName != sMissingFile) { // Wrong directory specified! fl_alert("The directory must contain file '%s'!",sMissingFile.c_str()); continue; } // Make sure that the filename matches and get the path string sBrowsePath = itksys::SystemTools::GetFilenamePath(fName); // Set the path system["System.ProgramDataDirectory"] << sBrowsePath; } } return true; } bool LoadUserPreferencesInteractive(SystemInterface &system) { try { system.LoadUserPreferences(); } catch(std::string &exc) { if(0 == fl_choice("Error reading preferences file %s\n%s", "Exit Program","Continue",NULL, system.GetUserPreferencesFileName(),exc.c_str())) { return false; } } return true; } // Setup printing of stack trace on segmentation faults. This only // works on select GNU systems #if defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__APPLE__) && !defined(sun) && !defined(WIN32) #include #include void SegmentationFaultHandler(int sig) { cerr << "*************************************" << endl; cerr << "ITK-SNAP: Segmentation Fault! " << endl; cerr << "BACKTRACE: " << endl; void *array[50]; int nsize = backtrace(array, 50); backtrace_symbols_fd(array, nsize, 2); cerr << "*************************************" << endl; exit(-1); } void SetupSignalHandlers() { signal(SIGSEGV, SegmentationFaultHandler); } #else void SetupSignalHandlers() { // Nothing to do! } #endif void usage() { // Print usage info and exit cout << "ITK-SnAP Command Line Usage:" << endl; cout << " snap [options] [main_image]" << endl; cout << "Options:" << endl; cout << " --main, -m FILE : " << "Load main image FILE; automatically determine grey or RGB" << endl; cout << " --grey, -g FILE : " << "Load main image FILE as greyscale" << endl; cout << " --rgb FILE : " << "Load main image FILE as RGB image" << endl; cout << " --segmentation, -s FILE : " << "Load segmentation image FILE" << endl; cout << " --labels, -l FILE : " << "Load label description file FILE" << endl; cout << " --overlay, -o FILE : " << "Load overlay image FILE; automatically determine grey or RGB" << endl; cout << " --compact : " << "Launch in compact single-slice mode (axial, coronal, sagittal)" << endl; cout << " --zoom, -z FACTOR : " << "Specify initial zoom in screen pixels / physical mm" << endl; } // creates global pointers // sets up the GUI and lets things run int main(int argc, char **argv) { // Handle signals gracefully, with trace-back SetupSignalHandlers(); // Turn off ITK warning windows itk::Object::GlobalWarningDisplayOff(); // Parse command line parameters CommandLineArgumentParser parser; parser.AddOption("--grey",1); parser.AddSynonim("--grey","-g"); parser.AddOption("--main",1); parser.AddSynonim("--main","-m"); parser.AddOption("--rgb", 1); parser.AddOption("--segmentation",1); parser.AddSynonim("--segmentation","-s"); parser.AddSynonim("--segmentation","-seg"); parser.AddOption("--overlay", 1); parser.AddSynonim("--overlay", "-o"); parser.AddOption("--labels",1); parser.AddSynonim("--labels","--label"); parser.AddSynonim("--labels","-l"); parser.AddOption("--zoom", 1); parser.AddSynonim("--zoom", "-z"); parser.AddOption("--compact", 1); parser.AddSynonim("--compact", "-c"); parser.AddOption("--help", 0); parser.AddSynonim("--help", "-h"); CommandLineArgumentParseResult parseResult; int iTrailing = 0; if(!parser.TryParseCommandLine(argc,argv,parseResult,false,iTrailing)) { cerr << "Unable to parse command line. Run " << argv[0] << " -h for help" << endl; return -1; } if(parseResult.IsOptionPresent("--help")) { usage(); return 0; } // Create a new IRIS application IRISApplication *iris = new IRISApplication; // Initialize the operating system interface SystemInterface &system = *iris->GetSystemInterface(); // Load the user preferences into the system if(!LoadUserPreferencesInteractive(system)) return -1; // Work with the user to try to find the root directory if(!FindDataDirectoryInteractive(argv[0],system)) return -1; // Create a UI object UserInterfaceLogic *ui = new UserInterfaceLogic(iris); // Initialize FLTK Fl::visual(FL_DOUBLE|FL_INDEX); Fl::gl_visual(FL_RGB); Fl::background(236,233,216); // Show the IRIS Interface ui->Launch(); // Show the splash screen ui->ShowSplashScreen(); // The following situations are possible for main image // itksnap file <- load as main image, detect file type // itksnap --main file <- load as main image, detect file type // itksnap --gray file <- load as main image, force gray // itksnap --rgb file <- load as main image, force RGB // itksnap --gray file1 --rgb file2 <- error // itksnap --gray file1 file2 <- ignore file2 // itksnap --rgb file1 file2 <- ignore file2 // Check validity of options for main image if(parseResult.IsOptionPresent("--grey") && parseResult.IsOptionPresent("--rgb")) { cerr << "Error: options --rgb and --grey are mutually exclusive." << endl; return -1; } if(parseResult.IsOptionPresent("--main") && parseResult.IsOptionPresent("--rgb")) { cerr << "Error: options --main and --rgb are mutually exclusive." << endl; return -1; } if(parseResult.IsOptionPresent("--main") && parseResult.IsOptionPresent("--grey")) { cerr << "Error: options --main and --grey are mutually exclusive." << endl; return -1; } // Check if a main image file is specified bool force_grey = false, force_rgb = false; const char *fnMain = NULL; if(parseResult.IsOptionPresent("--main")) { fnMain = parseResult.GetOptionParameter("--main"); } else if(parseResult.IsOptionPresent("--grey")) { fnMain = parseResult.GetOptionParameter("--grey"); force_grey = true; } else if(parseResult.IsOptionPresent("--rgb")) { fnMain = parseResult.GetOptionParameter("--rgb"); force_rgb = true; } else if(iTrailing < argc) { fnMain = argv[iTrailing]; } // If no main, there should be no overlays, segmentation if(!fnMain && parseResult.IsOptionPresent("--segmentation")) { cerr << "Error: --segmentation can not be used without --main, --grey, or --rgb" << endl; return -1; } if(!fnMain && parseResult.IsOptionPresent("--overlay")) { cerr << "Error: --overlay can not be used without --main, --grey, or --rgb" << endl; return -1; } // Load main image file if(fnMain) { // Update the splash screen ui->UpdateSplashScreen("Loading image..."); // Try loading the image try { ui->NonInteractiveLoadMainImage(fnMain, force_grey, force_rgb); } catch(itk::ExceptionObject &exc) { cerr << "Error loading file '" << fnMain << "'" << endl; cerr << "Reason: " << exc << endl; return -1; } } // If one main image loaded, load segmentation if(fnMain) { // Load the segmentation if supplied if(parseResult.IsOptionPresent("--segmentation")) { // Get the filename const char *fname = parseResult.GetOptionParameter("--segmentation"); // Update the splash screen ui->UpdateSplashScreen("Loading segmentation image..."); // Try to load the image try { ui->NonInteractiveLoadSegmentation(fname); } catch(itk::ExceptionObject &exc) { cerr << "Error loading file '" << fname << "'" << endl; cerr << "Reason: " << exc << endl; return -1; } } // Load overlay is supplied if(parseResult.IsOptionPresent("--overlay")) { // Get the filename const char *fname = parseResult.GetOptionParameter("--overlay"); // Update the splash screen ui->UpdateSplashScreen("Loading overlay image..."); // Try to load the image try { ui->NonInteractiveLoadOverlayImage(fname, false, false); } catch(itk::ExceptionObject &exc) { cerr << "Error loading file '" << fname << "'" << endl; cerr << "Reason: " << exc << endl; return -1; } } } // Load labels if supplied if(parseResult.IsOptionPresent("--labels")) { // Get the filename const char *fname = parseResult.GetOptionParameter("--labels"); // Update the splash screen ui->UpdateSplashScreen("Loading label descriptions..."); try { // Load the label file ui->NonInteractiveLoadLabels(fname); } catch(itk::ExceptionObject &exc) { cerr << "Error reading label descriptions: " << exc.GetDescription() << endl; } } // Set initial zoom if specified if(parseResult.IsOptionPresent("--zoom")) { double zoom = atof(parseResult.GetOptionParameter("--zoom")); cout << "Using ZOOM " << zoom << endl; if(zoom >= 0.0) { ui->SetZoomLevelAllWindows(zoom); } else { cerr << "Invalid zoom level (" << zoom << ") specified" << endl; } } if(parseResult.IsOptionPresent("--compact")) { string slice = parseResult.GetOptionParameter("--compact"); if(slice.length() == 0 || !(slice[0] == 'a' || slice[0] == 'c' || slice[0] == 's')) cerr << "Wrong parameter passed for '--compact', ignoring" << endl; else { DisplayLayout dl = ui->GetDisplayLayout(); dl.show_main_ui = false; ui->SetDisplayLayout(dl); dl.show_panel_ui = false; ui->SetDisplayLayout(dl); dl.size = HALF_SIZE; ui->SetDisplayLayout(dl); dl.slice_config = slice[0] == 'a' ? AXIAL : (slice[0] == 'c' ? CORONAL : SAGITTAL); ui->SetDisplayLayout(dl); } } // Show the welcome message ui->UpdateSplashScreen("Welcome to SnAP!"); // Show the splash screen ui->HideSplashScreen(); // Run the UI try { Fl::run(); } catch(std::exception &exc) { int salvage = fl_choice( "A run-time exception has occurred. ITK-SNAP must terminate.\n\n" "The text of the exception follows:\n%s\n\n" "ITK-SNAP can try to save your segmentation image before exiting.\n" "Do you wish to do so?", "No, quit without saving!", "Yes, try saving segmentation!", NULL, exc.what()); if(salvage) { char *fnsave = fl_file_chooser( "Select a filename to save segmentation", "*.nii", "recovered_segmentation.nii"); if(fnsave) { try { typedef itk::ImageFileWriter WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetInput( ui->GetDriver()->GetIRISImageData()->GetSegmentation()->GetImage()); writer->SetFileName(fnsave); writer->Update(); } catch(...) { fl_alert("Failed to save segmentation image. Unfortunately, your work was lost."); } } } } // Write the user's preferences to disk try { system.SaveUserPreferences(); } catch(std::string &exc) { fl_alert("Failed to write preferences to file %s\n%s", system.GetUserPreferencesFileName(),exc.c_str()); } // Delete the UI object delete ui; // Terminate the application delete iris; return 0; } /* *$Log: SNAPMain.cxx,v $ *Revision 1.23 2011/05/04 15:25:42 pyushkevich *Fixes to build with fltk 1.3.0rc3 * *Revision 1.22 2010/04/16 04:02:35 pyushkevich *ENH: implemented drag and drop, OSX events, new command-line interface * *Revision 1.21 2010/03/23 21:23:07 pyushkevich *Added display halving capability, *command line switches --zoom, --help, --compact * *Revision 1.20 2009/10/28 08:05:36 pyushkevich *FIX: Multisession pan causing continuous screen updates * *Revision 1.19 2009/10/26 20:19:12 pyushkevich *ENH: added top-level exception handler with option to save work * *Revision 1.18 2009/10/17 20:39:50 pyushkevich *ENH: added tip of the day * *Revision 1.17 2009/07/23 15:50:58 pyushkevich *Got the template-removal changes to compile on Linux * *Revision 1.16 2009/07/22 21:06:24 pyushkevich *Changed the IO system and wizards, removed templating * *Revision 1.15 2009/07/14 20:41:56 pyushkevich *Making Linux compilation work * *Revision 1.14 2009/06/09 05:50:04 garyhuizhang *ENH: main image & grey overlay support * *Revision 1.13 2009/05/25 17:09:44 garyhuizhang *ENH: switch from Fl_File_Chooser to Fl_Native_File_Chooser which requires the fltk to be patched with Fl_Native_File_Chooser add-on. * *Revision 1.12 2009/02/05 14:58:30 pyushkevich *FIX: save slice layout appearance settings to registry; ENH: added linear interpolation option for grey images * *Revision 1.11 2009/01/23 20:09:38 pyushkevich *FIX: 3D rendering now takes place in Nifti(RAS) world coordinates, rather than the VTK (x spacing + origin) coordinates. As part of this, itk::OrientedImage is now used for 3D images in SNAP. Still have to fix cut plane code in Window3D * *Revision 1.10 2008/11/15 12:20:38 pyushkevich *Several new features added for release 1.8, including (1) support for reading floating point and mapping to short range; (2) use of image direction cosines to determine image orientation; (3) new reorient image dialog and changes to the IO wizard; (4) display of NIFTI world coordinates and yoking based on them; (5) multi-session zoom; (6) fixes to the way we keep track of unsaved changes to segmentation, including a new discard dialog; (7) more streamlined code for offline loading; (8) new command-line options, allowing RGB files to be read and opening SNAP by doubleclicking images; (9) versioning for IPC communications; (10) ruler for display windows; (11) bug fixes and other stuff I can't remember * *Revision 1.9 2008/11/01 11:32:00 pyushkevich *Compatibility with ITK 3.8 support for reading oriented images *Command line loading of RGB images *Improved load-image commands in UserInterfaceLogic * *Revision 1.8 2007/12/30 04:43:03 pyushkevich *License/Packaging updates * *Revision 1.7 2007/12/26 12:26:52 pyushkevich *Removed the OpenGL extension thing * *Revision 1.6 2007/12/06 20:43:37 pyushkevich *More gentle parsing of command line arguments * *Revision 1.5 2007/09/17 20:33:09 pyushkevich *VTK5 compatibility * *Revision 1.4 2007/09/17 16:10:31 pyushkevich *Updated to VTK 5 * *Revision 1.3 2007/09/17 14:22:06 pyushkevich *fixed slicing bug * *Revision 1.2 2007/05/11 13:06:50 pyushkevich *Sun compatibility fix * *Revision 1.1 2006/12/02 04:22:26 pyushkevich *Initial sf checkin * *Revision 1.1.1.1 2006/09/26 23:56:17 pauly2 *Import * *Revision 1.16 2006/02/01 20:21:26 pauly *ENH: An improvement to the main SNAP UI structure: one set of GL windows is used to support SNAP and IRIS modes * *Revision 1.15 2005/11/07 15:50:33 pauly *COMP: Fixed problem with execinfo.h missing on some platforms. Also fixed *compilation error in GuidedImageIO.h * *Revision 1.14 2005/11/03 18:45:29 pauly *ENH: Enabled SNAP to read DICOM Series * *Revision 1.13 2005/10/29 14:00:14 pauly *ENH: SNAP enhacements like color maps and progress bar for 3D rendering * *Revision 1.12 2005/04/21 14:46:30 pauly *ENH: Improved management and editing of color labels in SNAP * *Revision 1.11 2005/02/04 17:01:09 lorensen *COMP: last of gcc 2.96 changes (I hope). * *Revision 1.10 2004/08/26 19:43:27 pauly *ENH: Moved the Borland code into Common folder * *Revision 1.9 2004/08/03 23:26:32 ibanez *ENH: Modification for building in multple platforms. By Julien Jomier. * *Revision 1.8 2004/07/09 23:07:38 pauly *ENH: Added a zoom-locator frame inside of the slice display window. * *Revision 1.7 2003/12/16 13:19:26 pauly *FIX: Removed Fl::lock() * *Revision 1.6 2003/11/25 23:32:48 pauly *FIX: Snake evolution did not work in multiprocessor mode * *Revision 1.5 2003/10/06 12:30:01 pauly *ENH: Added history lists, remembering of settings, new snake parameter preview * *Revision 1.4 2003/10/02 14:55:52 pauly *ENH: Development during the September code freeze * *Revision 1.3 2003/09/15 19:06:58 pauly *FIX: Trying to get last changes to compile * *Revision 1.2 2003/09/13 15:18:01 pauly *FIX: Got SNAP to work properly with different image orientations * *Revision 1.1 2003/09/11 13:51:01 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:50 pauly *Initial checkin of the SNAP application into the InsightApplications tree. * *Revision 1.1 2003/07/11 23:33:57 pauly **** empty log message *** * *Revision 1.8 2003/07/11 21:41:38 pauly *Preparation for ITK checkin * *Revision 1.7 2003/07/01 16:53:59 pauly **** empty log message *** * *Revision 1.6 2003/06/23 23:59:32 pauly *Command line argument parsing * *Revision 1.5 2003/06/14 22:42:06 pauly *Several changes. Started working on implementing the level set function *in ITK. * *Revision 1.4 2003/05/05 12:30:18 pauly **** empty log message *** * *Revision 1.3 2003/04/18 17:32:18 pauly **** empty log message *** * *Revision 1.2 2003/04/16 05:04:17 pauly *Incorporated intensity modification into the snap pipeline *New IRISApplication *Random goodies * *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.8 2002/04/01 22:27:57 moon *Took out global snake3D. It's now part of SnakeVoxDataClass * *Revision 1.7 2002/03/26 19:20:13 moon *Changed full_data back to VoxDataClass, from SnakeVoxDataClass. roi_data *is a SnakeVoxDataClass now. * *Revision 1.6 2002/03/23 02:16:37 scheuerm *Added subclass of VoxData called SnakeVoxData which includes *a preprocessed image. Doesn't do much yet but it's a start. * *Revision 1.5 2002/03/19 19:35:06 moon *added snakewrapper to makefile so it gets compiled. started putting in callback, *etc. for snake vcr buttons. added snake object to IrisGlobals, instantiated in Main * *Revision 1.4 2002/03/08 13:54:47 moon *trying to add log tags **/ itksnap/UserInterface/Window3D/0000755000076500000240000000000011560342171015741 5ustar paulystaffitksnap/UserInterface/Window3D/CVS/0000755000076500000240000000000011560342171016374 5ustar paulystaffitksnap/UserInterface/Window3D/CVS/Entries0000644000076500000240000000026511560342171017733 0ustar paulystaff/Trackball.cxx/1.2/Sun Dec 30 04:05:29 2007// /Trackball.h/1.2/Sun Dec 30 04:05:29 2007// /Window3D.cxx/1.13/Mon Oct 26 16:40:19 2009// /Window3D.h/1.4/Fri Jan 23 20:09:38 2009// D itksnap/UserInterface/Window3D/CVS/Repository0000644000076500000240000000003711560342171020476 0ustar paulystaffitksnap/UserInterface/Window3D itksnap/UserInterface/Window3D/CVS/Root0000644000076500000240000000010011560342171017231 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/UserInterface/Window3D/Trackball.cxx0000644000076500000240000001377510735614411020403 0ustar paulystaff/*========================================================================= 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/UserInterface/Window3D/Trackball.h0000644000076500000240000000745010735614411020021 0ustar paulystaff/*========================================================================= 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/UserInterface/Window3D/Window3D.cxx0000644000076500000240000012161311271350363020130 0ustar paulystaff/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: Window3D.cxx,v $ Language: C++ Date: $Date: 2009/10/26 16:40:19 $ 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. =========================================================================*/ #include "Window3D.h" #include "IRISImageData.h" #include "SNAPImageData.h" #include "UserInterfaceBase.h" #include "GlobalState.h" #include #include "IRISApplication.h" #include "ImageRayIntersectionFinder.h" #include "SNAPAppearanceSettings.h" #include "FLTKCanvas.h" #include "GenericSliceWindow.h" #include #include #include #if VXL_VERSION_DATE_FULL > 20040406 # include # define itk_cross_3d vnl_cross_3d #else # define itk_cross_3d cross_3d #endif #include /** 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 { assert(m_LabelTable); const ColorLabel &cl = m_LabelTable->GetColorLabel(label); return (cl.IsValid() && cl.IsVisible() && cl.IsVisibleIn3D()) ? 1 : 0; } private: const ColorLabelTable *m_LabelTable; }; class SnakeImageHitTester { public: int operator()(float levelSetValue) const { return levelSetValue <= 0 ? 1 : 0; } }; /** * \class Trackball3DInteractionMode * \brief 3D interaction mode that takes care of 3D rotation and zoom * * \see Window3D */ class Trackball3DInteractionMode : public Window3D::EventHandler { public: Trackball3DInteractionMode(Window3D *parent) : Window3D::EventHandler(parent) {} int OnMousePress(const FLTKEvent &event); int OnMouseRelease(const FLTKEvent &event, const FLTKEvent &pressEvent); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent); }; int Trackball3DInteractionMode ::OnMousePress(const FLTKEvent &event) { int x = event.XCanvas[0]; int y = event.XCanvas[1]; switch (event.SoftButton) { case FL_LEFT_MOUSE: m_Parent->OnRotateStartAction(x,y);break; case FL_MIDDLE_MOUSE: m_Parent->OnPanStartAction(x,y);break; case FL_RIGHT_MOUSE: m_Parent->OnZoomStartAction(x,y);break; default: return 0; } return 1; } int Trackball3DInteractionMode ::OnMouseRelease(const FLTKEvent &irisNotUsed(event), const FLTKEvent &irisNotUsed(startEvent)) { m_Parent->OnTrackballStopAction(); return 1; } int Trackball3DInteractionMode ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &irisNotUsed(startEvent)) { m_Parent->OnTrackballDragAction(event.XCanvas[0],event.XCanvas[1]); return 1; } /** * \class Crosshair3DInteractionMode * \brief 3D interaction mode that takes care of 3D crosshair interaction * * \see Window3D */ class Crosshairs3DInteractionMode : public Window3D::EventHandler { public: Crosshairs3DInteractionMode(Window3D *parent) : Window3D::EventHandler(parent) {} int OnMousePress(const FLTKEvent &event); }; int Crosshairs3DInteractionMode ::OnMousePress(const FLTKEvent &event) { if(event.SoftButton == FL_LEFT_MOUSE) { m_Parent->OnCrosshairClickAction(event.XCanvas[0],event.XCanvas[1]); return 1; } else return 0; } /** * \class Scalpel3DInteractionMode * \brief 3D interaction mode that takes care of cutting 3D view in two * * \see Window3D */ class Scalpel3DInteractionMode : public Window3D::EventHandler { public: Scalpel3DInteractionMode(Window3D *parent) : Window3D::EventHandler(parent) { m_Started = false; m_Inside = false; } int OnMousePress(const FLTKEvent &event); int OnMouseMotion(const FLTKEvent &event); int OnMouseEnter(const FLTKEvent &event); int OnMouseExit(const FLTKEvent &event); irisGetMacro(Started,bool); irisGetMacro(Inside,bool); irisGetMacro(StartPoint,Vector2i); irisGetMacro(EndPoint,Vector2i); protected: bool m_Inside; bool m_Started; Vector2i m_StartPoint; Vector2i m_EndPoint; }; int Scalpel3DInteractionMode ::OnMousePress(const FLTKEvent &event) { if(!m_Started) { // Only for left button if(event.SoftButton != FL_LEFT_MOUSE) return 0; // Record the starting and ending points m_StartPoint = event.XCanvas; m_EndPoint = event.XCanvas; m_Started = true; m_Inside = true; } else { // This is the second click, after the user finished dragging m_EndPoint = event.XCanvas; m_Started = false; // If the user clicks another button, disengage, otherwise // draw the plane in the parent if(event.SoftButton == FL_LEFT_MOUSE) m_Parent->OnScalpelPointPairAction( m_StartPoint[0],m_StartPoint[1],m_EndPoint[0],m_EndPoint[1]); } // Redraw the parent m_Canvas->redraw(); // Eat the event return 1; } int Scalpel3DInteractionMode:: OnMouseMotion(const FLTKEvent &event) { // Only valid if drawing has started if(!m_Started) return 0; // Record the end point m_EndPoint = event.XCanvas; // Redraw the parent m_Canvas->redraw(); // Eat the event return 1; } int Scalpel3DInteractionMode ::OnMouseEnter(const FLTKEvent &event) { // Only valid if drawing has started if(!m_Started) return 0; // Record that we're inside m_Inside = true; // Record the end point m_EndPoint = event.XCanvas; // Redraw the parent m_Canvas->redraw(); // Eat the event return 1; } int Scalpel3DInteractionMode ::OnMouseExit(const FLTKEvent &irisNotUsed(event)) { // Only valid if drawing has started if(!m_Started) return 0; // Record that we're inside m_Inside = false; // Redraw the parent m_Canvas->redraw(); // Eat the event return 1; } /** * \class Spraypaint3DInteractionMode * \brief 3D interaction mode that takes care of spraying on top of the 3D view * * \see Window3D */ class Spraypaint3DInteractionMode : public Window3D::EventHandler { public: Spraypaint3DInteractionMode(Window3D *parent) : Window3D::EventHandler(parent) {} int OnMousePress(const FLTKEvent &event); int OnMouseDrag(const FLTKEvent &event, const FLTKEvent &pressEvent); }; int Spraypaint3DInteractionMode ::OnMousePress(const FLTKEvent &event) { if(event.SoftButton == FL_LEFT_MOUSE) { m_Parent->OnSpraypaintClickAction(event.XCanvas[0],event.XCanvas[1]); return 1; } else return 0; } int Spraypaint3DInteractionMode ::OnMouseDrag(const FLTKEvent &event, const FLTKEvent &irisNotUsed(startEvent)) { if(event.SoftButton == FL_LEFT_MOUSE) { m_Parent->OnSpraypaintClickAction(event.XCanvas[0],event.XCanvas[1]); return 1; } else return 0; } Window3D ::Window3D(UserInterfaceBase *parentUI, FLTKCanvas *canvas) : RecursiveInteractionMode(canvas) { // Copy parent pointers m_ParentUI = parentUI; m_Driver = m_ParentUI->GetDriver(); m_GlobalState = m_Driver->GetGlobalState(); // Pass parent pointer to the mesh object this->m_Mesh.Initialize(m_Driver); // Make sure FLTK canvas does not flip the Y coordinate m_Canvas->SetFlipYCoordinate(false); m_Canvas->SetGrabFocusOnEntry(true); // Clear the flags m_NeedsInitialization = 1; m_CursorVisible = 0; m_Mode = WIN3D_NONE; m_Plane.valid = -1; // Reset the vectors to zero m_WorldMatrix.set_identity(); m_ImageSize.fill(0); m_Center.fill(0); m_DefaultHalf.fill(0); m_ViewHalf.fill(0); // Initialize the interaction modes m_CrosshairsMode = new Crosshairs3DInteractionMode(this); m_TrackballMode = new Trackball3DInteractionMode(this); m_SpraypaintMode = new Spraypaint3DInteractionMode(this); m_ScalpelMode = new Scalpel3DInteractionMode(this); // Start with the trackball mode, which is prevailing PushInteractionMode(m_TrackballMode); } /** Enter the cross-hairs mode of operation */ void Window3D ::EnterCrosshairsMode() { PopInteractionMode(); PushInteractionMode(m_CrosshairsMode); } /** Enter the trackball mode of operation */ void Window3D ::EnterTrackballMode() { PopInteractionMode(); PushInteractionMode(m_TrackballMode); } /** Enter the scalpel mode of operation */ void Window3D ::EnterScalpelMode() { PopInteractionMode(); PushInteractionMode(m_ScalpelMode); } /** Enter the spraypaint mode of operation */ void Window3D ::EnterSpraypaintMode() { PopInteractionMode(); PushInteractionMode(m_SpraypaintMode); } Window3D ::~Window3D() { delete m_CrosshairsMode; delete m_TrackballMode; delete m_SpraypaintMode; delete m_ScalpelMode; } /* Initializes lightning and other basic GL-state for the window */ void Window3D ::Initialize() { glClearColor(0.0, 0.0, 0.0, 0.0); glEnable( GL_DEPTH_TEST ); // Set up the materials GLfloat light0Pos[4] = { 0.0, 0.0, 1.0, 0.0}; GLfloat matAmb[4] = { 0.01, 0.01, 0.01, 1.00}; GLfloat matDiff[4] = { 0.65, 0.65, 0.65, 1.00}; GLfloat matSpec[4] = { 0.30, 0.30, 0.30, 1.00}; GLfloat matShine = 10.0; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, matAmb); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matDiff); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matSpec); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, matShine); glEnable(GL_COLOR_MATERIAL); // Setup Lighting glLightfv(GL_LIGHT0, GL_POSITION, light0Pos); glEnable(GL_LIGHT0); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); glEnable(GL_LIGHTING); } void Window3D ::ClearScreen() { // Hide the crosshairs. m_CursorVisible = 0; // Hide the mesh. m_Mesh.Reset(); } void Window3D ::ResetView() { // Reset the trackball m_Trackball.Reset(); // Rotate a little bit, so we see the three axes m_Trackball.StartRot(12,10,20,20); m_Trackball.TrackRot(10,8,20,20); m_Trackball.StopRot(); if (m_Driver->GetCurrentImageData()->IsMainLoaded()) { // data dimensions m_ImageSize = m_Driver->GetCurrentImageData()->GetVolumeExtents(); // world transform m_WorldMatrix = m_Driver->GetCurrentImageData()->GetMain()->GetNiftiSform(); // Volume size m_VolumeSize = vector_multiply_mixed( m_Driver->GetCurrentImageData()->GetImageSpacing(), m_ImageSize); } else { m_ImageSize.fill(0); m_WorldMatrix.set_identity(); m_VolumeSize.fill(0.0); } // Compute the maximum extent of the image cube float xMaxDim = m_VolumeSize.max_value(); m_DefaultHalf[X] = m_DefaultHalf[Y] = m_DefaultHalf[Z] = xMaxDim * 0.7 + 1.0; m_DefaultHalf[Z] *= 4.0; m_CursorVisible = 1; // Show the crosshairs. m_Plane.valid = -1; // Resets the Cut m_Plane m_Samples.clear(); // Fire 3D view update event m_ParentUI->OnTrackballUpdate(); } void Window3D ::UpdateMesh(itk::Command *command) { // make_current(); try { m_Mesh.GenerateMesh(command); } catch(vtkstd::bad_alloc &) { fl_alert("Out of memory error when generating 3D mesh."); } m_Canvas->redraw(); } void Window3D ::Accept() { // Get the current drawing color unsigned char colorid = m_GlobalState->GetDrawingColorLabel(); // Apply the spraypaint samples to the image for(SampleListIterator it=m_Samples.begin();it!=m_Samples.end();++it) { m_Driver->GetCurrentImageData()->SetSegmentationVoxel( to_unsigned_int(*it), colorid ); } // Clear the list of samples m_Samples.clear(); // If the plane is valid, apply it for relabeling if (1 == m_Plane.valid ) { m_Driver->RelabelSegmentationWithCutPlane(m_Plane.vNormal, m_Plane.dIntercept); m_Plane.valid = -1; } } void Window3D ::CheckErrors() { GLenum error; while ((error = glGetError()) != GL_NO_ERROR) { std::cerr << "GL-Error: " << (char *) gluErrorString(error) << std::endl; } } void Window3D ::OnDraw() { // Respond to a resize if necessary if (!m_Canvas->valid()) { Initialize(); glViewport(0,0,m_Canvas->w(),m_Canvas->h()); } // Initialize GL if necessary if(m_NeedsInitialization) { ResetView(); Initialize(); m_NeedsInitialization = false; } // Get the properties for the background color Vector3d clrBack = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::BACKGROUND_3D).NormalColor; glClearColor(clrBack[0],clrBack[1],clrBack[2],1); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Compute the center of rotation m_CenterOfRotation = affine_transform_point(m_WorldMatrix, m_Driver->GetCursorPosition()); // Set up the projection matrix SetupProjection(); // Set up the model view matrix glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Update the screen geometry glTranslatef( m_Trackball.GetPanX(), m_Trackball.GetPanY(), 0.0 ); glMultMatrixf( m_Trackball.GetRot() ); glTranslate( - m_CenterOfRotation ); // Apply the world matrix - all subsequent ops are in voxel space glPushMatrix(); glMultMatrixd(m_WorldMatrix.transpose().data_block()); // glTranslate(m_Origin); // glScale(m_Spacing); // Draw things in pixel coords DrawCrosshairs(); DrawSamples(); // Undo world matrix - back to drawing in native space glPopMatrix(); // Draw the cut plane DrawCutPlane(); // The mesh is already in isotropic coords (needed for marching cubes) m_Mesh.Display(); // Restore the matrix state glPopMatrix(); // Draw any overlays there may be if(m_ScalpelMode->GetStarted() && m_ScalpelMode->GetInside()) { glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0,m_Canvas->w(),m_Canvas->h(),0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glPushAttrib(GL_DEPTH_BUFFER_BIT); glDepthFunc(GL_ALWAYS); // Get the points Vector2d x1 = to_double(m_ScalpelMode->GetStartPoint()); Vector2d x2 = to_double(m_ScalpelMode->GetEndPoint()); Vector2d d = x2-x1; // Draw a line between the two points glColor3d(1,1,1); glBegin(GL_LINES); glVertex2d(x1[0],x1[1]); glVertex2d(x2[0],x2[1]); glEnd(); // If the points are far enough apart, draw the normal if(d.two_norm() > 10) { // Draw the normal midway through the line Vector2d n = Vector2d(-d[1],d[0]).normalize(); Vector2d p1 = 0.5 * (x1 + x2); Vector2d p2 = p1 + 10.0 * n; glBegin(GL_LINES); glVertex2d(p1[0],p1[1]); glVertex2d(p2[0],p2[1]); glEnd(); // Draw a colored triangle d.normalize(); Vector2d u1 = p2 + 4.0 * d; Vector2d u2 = p2 - 4.0 * d; Vector2d u3 = p2 + 1.732 * 4.0 * n; glBegin(GL_TRIANGLES); glVertex2d(u1[0],u1[1]); glVertex2d(u2[0],u2[1]); glVertex2d(u3[0],u3[1]); glEnd(); } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glPopAttrib(); } glFlush(); // CheckErrors(); } void Window3D ::OnRotateStartAction(int x, int y) { if ( m_Mode != WIN3D_NONE ) return; m_Mode = WIN3D_ROTATE; m_Trackball.StartRot(x, y, m_Canvas->w(), m_Canvas->h()); m_ParentUI->OnTrackballUpdate(); } void Window3D ::OnPanStartAction(int x, int y) { if ( m_Mode != WIN3D_NONE ) return; m_Mode = WIN3D_PAN; m_Trackball.StartPan(x, y); m_ParentUI->OnTrackballUpdate(); } void Window3D ::OnZoomStartAction(int irisNotUsed(x), int y) { if ( m_Mode != WIN3D_NONE ) return; m_Mode = WIN3D_ZOOM; m_Trackball.StartZoom(y); m_ParentUI->OnTrackballUpdate(); } void Window3D ::OnTrackballDragAction(int x, int y) { switch (m_Mode) { case WIN3D_ROTATE: m_Trackball.TrackRot(x, y, m_Canvas->w(), m_Canvas->h()); break; case WIN3D_ZOOM: m_Trackball.TrackZoom(y); // this->SetupProjection(); break; case WIN3D_PAN: m_Trackball.TrackPan(x, y, m_Canvas->w(), m_Canvas->h(), 2 * m_ViewHalf[X], 2 * m_ViewHalf[Y]); break; default: break; } m_ParentUI->OnTrackballUpdate(); m_Canvas->redraw(); } void Window3D ::OnTrackballStopAction() { switch (m_Mode) { case WIN3D_ROTATE: m_Trackball.StopRot(); break; case WIN3D_PAN: m_Trackball.StopPan(); break; case WIN3D_ZOOM: m_Trackball.StopZoom(); // this->SetupProjection(); break; default: break; } m_Mode = WIN3D_NONE; m_ParentUI->OnTrackballUpdate(); } void Window3D ::OnCrosshairClickAction(int x,int y) { // Make sure that there is a valid image if (!m_Driver->GetCurrentImageData()->IsMainLoaded()) return; // Only respond to the left mouse button (why?) Vector3i hit; if (IntersectSegData(x, y, hit)) { m_Driver->SetCursorPosition(to_unsigned_int(hit)); } m_ParentUI->OnCrosshairPositionUpdate(); m_ParentUI->RedrawWindows(); } void Window3D ::OnSpraypaintClickAction(int x,int y) { // Make sure that there is a valid image if (!m_Driver->GetCurrentImageData()->IsMainLoaded()) return; Vector3i hit; if (this->IntersectSegData(x, y, hit)) { AddSample( hit ); m_ParentUI->OnIRISMeshEditingAction(); m_Canvas->redraw(); } } void Window3D ::OnScalpelPointPairAction(int x1, int y1, int x2, int y2) { // Requires a loaded image if (!m_Driver->GetCurrentImageData()->IsMainLoaded()) return; // Pass in the first point // OnCutPlanePointRayAction(x1, y1, 1); // OnCutPlanePointRayAction(x2, y2, 2); if(ComputeCutPlane(x1,y1,x2,y2)) { m_Plane.valid = 1; m_ParentUI->OnIRISMeshEditingAction(); } m_Canvas->redraw(); } /** Does translation and rotation from the m_Trackball (not anisotropic scaling). Make sure to glPopMatrix() after use! */ void Window3D ::SetupModelView() { } void Window3D ::SetupProjection() { // Get the view extent 'radius' m_ViewHalf = m_DefaultHalf / (double) m_Trackball.GetZoom(); double x, y; if(m_ViewHalf[X] * m_Canvas->h() > m_ViewHalf[Y] * m_Canvas->w()) { x = m_ViewHalf[X]; y = m_Canvas->h() * x / m_Canvas->w(); } else { y = m_ViewHalf[Y]; x = m_Canvas->w() * y / m_Canvas->h(); } // Set up the coordinate projection glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho(-x, x, -y, y, -m_DefaultHalf[Z], m_DefaultHalf[Z]); } // Get a copy of the viewport/m_Modelview/projection matrices OpenGL uses void Window3D::ComputeMatricies( GLint *vport, double *mview, double *proj ) { // Compute the center of rotation m_CenterOfRotation = affine_transform_point(m_WorldMatrix, m_Driver->GetCursorPosition()); // Set up the model view matrix glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Update the screen geometry glTranslatef( m_Trackball.GetPanX(), m_Trackball.GetPanY(), 0.0 ); glMultMatrixf( m_Trackball.GetRot() ); glTranslate( -m_CenterOfRotation ); glMultMatrixd(m_WorldMatrix.transpose().data_block()); glGetIntegerv( GL_VIEWPORT, vport ); glGetDoublev( GL_MODELVIEW_MATRIX, mview ); glGetDoublev( GL_PROJECTION_MATRIX, proj ); glPopMatrix(); } void Window3D::ComputeRay( int x, int y, double *mvmatrix, double *projmatrix, GLint *viewport, Vector3d &v, Vector3d &r ) { int val; val = gluUnProject( (GLdouble) x, (GLdouble) y, 0.0, mvmatrix, projmatrix, viewport, &(v[0]), &(v[1]), &(v[2]) ); if ( val == GL_FALSE ) std::cerr << "gluUnProject #1 FAILED!!!!!" << std::endl; val = gluUnProject( (GLdouble) x, (GLdouble) y, 1.0, mvmatrix, projmatrix, viewport, &(r[0]), &(r[1]), &(r[2]) ); if ( val == GL_FALSE ) std::cerr << "gluUnProject #2 FAILED!!!!!" << std::endl; r[0] = r[0] - v[0]; r[1] = r[1] - v[1]; r[2] = r[2] - v[2]; } bool Window3D ::ComputeCutPlane(int wx1, int wy1, int wx2, int wy2) { // Open GL matrices double mvmatrix[16]; double projmatrix[16]; GLint viewport[4]; Vector3d x1,x2,p1,p2; // Compute the GL matrices ComputeMatricies( viewport, mvmatrix, projmatrix ); // Flip the y coordinate wy1 = viewport[3] - wy1 - 1; wy2 = viewport[3] - wy2 - 1; // Compute the normal to the viewplane gluUnProject(0,0,0,mvmatrix,projmatrix,viewport, p1.data_block(),p1.data_block()+1,p1.data_block()+2); gluUnProject(0,0,1,mvmatrix,projmatrix,viewport, p2.data_block(),p2.data_block()+1,p2.data_block()+2); // W is a vector pointing into the screen Vector3d w = p2 - p1; // Compute the vector connecting the two points currently lying on the // view plane gluUnProject(wx1,wy1,0,mvmatrix,projmatrix,viewport, x1.data_block(),x1.data_block()+1,x1.data_block()+2); gluUnProject(wx2,wy2,0,mvmatrix,projmatrix,viewport, x2.data_block(),x2.data_block()+1,x2.data_block()+2); // Now we have two orthogonal vectors laying on the cut plane. All we have // to do is take the cross product Vector3d delta = x2-x1; Vector3d n = - itk_cross_3d(delta, w); // Compute the length of the normal and exit if it's zero double l = n.two_norm(); if(l == 0.0) return false; // Compute the distance to the origin m_Plane.vNormal = n.normalize(); // Check if the normal requires flipping (if the Jacobian of the world matrix // is negative) if(vnl_det(m_WorldMatrix) < 0) m_Plane.vNormal = -m_Plane.vNormal; m_Plane.dIntercept = dot_product(x1,m_Plane.vNormal); // Now, this is not enough, because we want to be able to draw the plane // in space. In order to do this we need four corners of a square on the // plane. // Compute the points on the plane in world space Vector3d x1World = affine_transform_point(m_WorldMatrix, x1); Vector3d x2World = affine_transform_point(m_WorldMatrix, x2); Vector3d p1World = affine_transform_point(m_WorldMatrix, p1); Vector3d p2World = affine_transform_point(m_WorldMatrix, p2); // Compute the normal in world coordinates Vector3d nWorld = - itk_cross_3d((vnl_vector) (x2World - x1World), (vnl_vector) (p2World - p1World)); m_Plane.vNormalWorld = nWorld.normalize(); // Compute the intercept in world coordinates double interceptWorld = dot_product(x1World,m_Plane.vNormalWorld); // Compute the center of the volume Vector3d xVol = to_double(m_VolumeSize) * 0.5; // Compute the center of the volume in world coordinates Vector3d xVolCenter = affine_transform_point(m_WorldMatrix, to_double(m_ImageSize) * 0.5); // Use that to compute the center of the square double edgeLength = (xVol[0] > xVol[1]) ? xVol[0] : xVol[1]; edgeLength = (edgeLength > xVol[2]) ? edgeLength : xVol[2]; // Now compute the center point of the square Vector3d m_Origin = m_Driver->GetCurrentImageData()->GetImageOrigin(); Vector3d xCenter = xVolCenter - m_Plane.vNormalWorld * (dot_product(xVolCenter,m_Plane.vNormalWorld) - interceptWorld); // Compute the 'up' vector and the 'in' vector Vector3d vUp = (x2World - x1World).normalize(); Vector3d vIn = (p2World - p1World).normalize(); // Compute the corners m_Plane.xDisplayCorner[0] = xCenter + edgeLength * (vUp + vIn); m_Plane.xDisplayCorner[1] = xCenter + edgeLength * (vUp - vIn); m_Plane.xDisplayCorner[2] = xCenter + edgeLength * (- vUp - vIn); m_Plane.xDisplayCorner[3] = xCenter + edgeLength * (- vUp + vIn); return true; } //------------------------------------------------------------------------ // IntersectSegData(int mouse_x, int mouse_y, Vector3i *hit) // computes a m_Ray going straight back from the current viewpoint // from the mouse click position on screen. // The output Vector3i hit is in image coords. // //------------------------------------------------------------------------ int Window3D::IntersectSegData(int mouse_x, int mouse_y, Vector3i &hit) { double mvmatrix[16]; double projmatrix[16]; GLint viewport[4]; m_Canvas->make_current(); // update GL state ComputeMatricies( viewport, mvmatrix, projmatrix ); int x = mouse_x; int y = viewport[3] - mouse_y - 1; ComputeRay( x, y, mvmatrix, projmatrix, viewport, m_Point, m_Ray ); // The result int result = 0; // Depending on the situation, we may intersect with a snake image or a // segmentation image // TODO: Need both conditions? if(m_GlobalState->GetSnakeActive() && m_Driver->GetSNAPImageData()->IsSnakeLoaded()) { typedef ImageRayIntersectionFinder< float,SnakeImageHitTester> RayCasterType; RayCasterType caster; result = caster.FindIntersection( m_Driver->GetSNAPImageData()->GetLevelSetImage(), m_Point,m_Ray,hit); } else { typedef ImageRayIntersectionFinder< LabelType,LabelImageHitTester> RayCasterType; RayCasterType caster; caster.SetHitTester(LabelImageHitTester(m_Driver->GetColorLabelTable())); result = caster.FindIntersection( m_Driver->GetCurrentImageData()->GetSegmentation()->GetImage(), m_Point,m_Ray,hit); } // m_Ray now has the proj m_Ray, m_Point is the m_Point in image space switch (result) { case 1: return 1; case -1: /*std::cerr << "RAY WAS INVALID!" << std::endl;*/ break; /* default: std::cerr << "No hit found" << std::endl;*/ } return 0; } void Window3D::AddSample( Vector3i s ) { // Add another sample. m_Samples.push_back(s); } void DrawCube( Vector3f &x, Vector3f &y ) { glBegin(GL_LINE_LOOP); glVertex3f( x[0], x[1], x[2] ); glVertex3f( x[0], y[1], x[2] ); glVertex3f( x[0], y[1], y[2] ); glVertex3f( x[0], x[1], y[2] ); glEnd(); glBegin(GL_LINE_LOOP); glVertex3f( y[0], x[1], x[2] ); glVertex3f( y[0], y[1], x[2] ); glVertex3f( y[0], y[1], y[2] ); glVertex3f( y[0], x[1], y[2] ); glEnd(); glBegin(GL_LINES); glVertex3f( x[0], x[1], x[2] ); glVertex3f( y[0], x[1], x[2] ); glVertex3f( x[0], y[1], x[2] ); glVertex3f( y[0], y[1], x[2] ); glVertex3f( x[0], x[1], y[2] ); glVertex3f( y[0], x[1], y[2] ); glVertex3f( x[0], y[1], y[2] ); glVertex3f( y[0], y[1], y[2] ); glEnd(); } void DrawCoordinateCutPlane( unsigned int iPlane, Vector3f &a, Vector3f &b, Vector3f &x ) { glBegin(GL_LINE_LOOP); switch(iPlane) { case 0 : glVertex3f( x[0], a[1], a[2] ); glVertex3f( x[0], a[1], b[2] ); glVertex3f( x[0], b[1], b[2] ); glVertex3f( x[0], b[1], a[2] ); break; case 1 : glVertex3f( a[0], x[1], a[2] ); glVertex3f( a[0], x[1], b[2] ); glVertex3f( b[0], x[1], b[2] ); glVertex3f( b[0], x[1], a[2] ); break; case 2 : glVertex3f( a[0], a[1], x[2] ); glVertex3f( a[0], b[1], x[2] ); glVertex3f( b[0], b[1], x[2] ); glVertex3f( b[0], a[1], x[2] ); break; } glEnd(); } void Window3D::DrawCrosshairs() { if ( !m_CursorVisible ) return; // Set up the GL state glPushAttrib(GL_LINE_BIT | GL_LIGHTING_BIT | GL_COLOR_BUFFER_BIT); glDisable(GL_LIGHTING); // Get the crosshair position Vector3f xCross = to_float(m_Driver->GetCursorPosition()) + Vector3f(0.5f); // Get the UI element properties for the image box SNAPAppearanceSettings::Element &eltBox = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::IMAGE_BOX_3D); // Draw the image box if(eltBox.Visible) { // Set the line properties SNAPAppearanceSettings::ApplyUIElementLineSettings(eltBox); // The cube around the image glColor3dv(eltBox.NormalColor.data_block()); Vector3f zeros(0.0f); Vector3f imageSize = to_float(m_ImageSize); DrawCube( zeros, imageSize ); // The slice planes glColor3dv(eltBox.ActiveColor.data_block()); for(int d = 0; d < 3; d++) DrawCoordinateCutPlane(d, zeros, imageSize, xCross ); } // Get the UI element properties for the crosshairs SNAPAppearanceSettings::Element &eltCross = m_ParentUI->GetAppearanceSettings()->GetUIElement( SNAPAppearanceSettings::CROSSHAIRS_3D); // Exit if crosshairs are to be ignored if(eltCross.Visible) { // Set up the line properties glColor3dv(eltCross.NormalColor.data_block()); SNAPAppearanceSettings::ApplyUIElementLineSettings(eltCross); // Draw the lines glBegin( GL_LINES ); for (int i=0; i<3; i++) { float end1[3], end2[3]; for (int j=0; j<3; j++) end1[j] = end2[j] = xCross[j]; // end1[i] = m_Center[i] - m_ImageSize[i]*0.7+1; // end2[i] = m_Center[i] + m_ImageSize[i]*0.7+1; end1[i] = 0.0f; end2[i] = m_ImageSize[i]; glVertex3fv(end1); glVertex3fv(end2); } glEnd(); } #if DEBUGGING glColor3f( 1.0, 1.0, 0.0 ); glBegin( GL_LINES ); glVertex3d( m_Point[0], m_Point[1], m_Point[2] ); glVertex3d( m_Point[0]+m_Ray[0], m_Point[1]+m_Ray[1], m_Point[2]+m_Ray[2] ); glEnd(); std::cerr << "A = ( " << m_Point[0] << ", " << m_Point[1] << ", " << m_Point[2] << " )" << std::endl; std::cerr << "B = ( " << m_Point[0]+m_Ray[0] << ", " << m_Point[1]+m_Ray[1] << ", " << m_Point[2]+m_Ray[2] << " )" << std::endl; #endif // Finish glPopAttrib(); } void Window3D::DrawSamples() { unsigned char index = m_GlobalState->GetDrawingColorLabel(); unsigned char rgb[3]; m_Driver->GetColorLabelTable()->GetColorLabel(index).GetRGBVector(rgb); glColor3ubv(rgb); for (SampleListIterator it=m_Samples.begin();it!=m_Samples.end();it++) { glPushMatrix(); glTranslate(to_float(*it)); GLUquadric *quad = gluNewQuadric(); gluSphere(quad,1.0,4,4); gluDeleteQuadric(quad); // glutSolidSphere( 1.0, 4, 4 ); glPopMatrix(); } } //----------------------------------------------------------------------- // DrawCutPlane // Provides User Feedback Information when in 3D Window Mode // when a Cutplane is defined. // Input: the 'plane' member // Output: GLplane representing the Cutplane that is defined by the user //----------------------------------------------------------------------- void Window3D ::DrawCutPlane() { if (m_Plane.valid != 1) return; Vector3d corner[4]; corner[0] = m_Plane.xDisplayCorner[0]; corner[1] = m_Plane.xDisplayCorner[1]; corner[2] = m_Plane.xDisplayCorner[2]; corner[3] = m_Plane.xDisplayCorner[3]; // Use the current label color unsigned char rgb[3]; m_Driver->GetColorLabelTable()->GetColorLabel( m_GlobalState->GetDrawingColorLabel()).GetRGBVector(rgb); // Save the settings glPushAttrib(GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_LIGHTING_BIT); // Create a stipple pattern GLubyte stipple[128]; for(unsigned int q = 0; q < 128; q++) stipple[q] = ((q >> 2) % 2 == 0) ? 0xAA : 0x55; // Draw a semi-transparent quad glEnable(GL_POLYGON_STIPPLE); glPolygonStipple(stipple); glColor3ubv(rgb); glBegin(GL_QUADS); glVertex(corner[0]); glVertex(corner[1]); glVertex(corner[2]); glVertex(corner[3]); glEnd(); glDisable(GL_POLYGON_STIPPLE); // Draw the plane using lines glEnable(GL_LINE_SMOOTH); glEnable(GL_LINE_STIPPLE); glEnable(GL_BLEND); glDisable(GL_LIGHTING); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glLineWidth(2.0); glLineStipple(2,0xAAAA); // Start with a white color glColor3d(1,1,1); Vector3d planeUnitNormal = Vector3d(m_Plane.vNormalWorld).normalize(); for(unsigned int i=0;i<4;i++) { glPushMatrix(); // Create a parallel plane to create a moving effect Vector3d vOffset = planeUnitNormal * (1.0 * i); glTranslate(vOffset); // Draw a line loop glBegin(GL_LINE_LOOP); glVertex(corner[0]); glVertex(corner[1]); glVertex(corner[2]); glVertex(corner[3]); glEnd(); // Switch to new color glColor3ubv(rgb); glPopMatrix(); } glPopAttrib(); }; int Window3D ::OnKeyAction(int key) { if(Fl::event_state() != FL_CTRL && key == 's') { // Store the state of the trackball m_TrackballBackup = m_Trackball; return 1; } else if(Fl::event_state() != FL_CTRL && key == 'r') { // Restore the trackball state m_Trackball = m_TrackballBackup; m_Canvas->redraw(); m_ParentUI->OnTrackballUpdate(); return 1; } return 0; } /* *$Log: Window3D.cxx,v $ *Revision 1.13 2009/10/26 16:40:19 pyushkevich *FIX(2039124): spray paint was not respecting hidden labels. Also update mesh was not on after label vis change * *Revision 1.12 2009/10/26 07:34:11 pyushkevich *ENH: substantially reduced memory footprint when loading float NIFTI images * *Revision 1.11 2009/10/25 13:17:05 pyushkevich *FIX: bugs in SF.net, crash on mesh update in large images, bad vols/stats output * *Revision 1.10 2009/07/16 22:02:29 pyushkevich *Made OpenGLTexture non-templated * *Revision 1.9 2009/06/02 04:32:46 garyhuizhang *ENH: layer support * *Revision 1.8 2009/01/30 23:08:21 garyhuizhang *ENH: better implementation of the keyboard shortcuts that do not require the SHIFT key * *Revision 1.7 2009/01/30 20:57:34 garyhuizhang *Bug Fix: check if any of the CTRL keys is pressed in Windows3D::OnKeyAction() * *Revision 1.6 2009/01/23 20:54:11 pyushkevich *FIX: fixed cut plane behavior, which was broken by earlier 3D changes * *Revision 1.5 2009/01/23 20:09:38 pyushkevich *FIX: 3D rendering now takes place in Nifti(RAS) world coordinates, rather than the VTK (x spacing + origin) coordinates. As part of this, itk::OrientedImage is now used for 3D images in SNAP. Still have to fix cut plane code in Window3D * *Revision 1.4 2009/01/17 10:40:28 pyushkevich *Added synchronization to 3D window viewpoint * *Revision 1.3 2008/02/10 23:55:22 pyushkevich *Added "Auto" button to the intensity curve window; Added prompt before quitting on unsaved data; Fixed issues with undo on segmentation image load; Added synchronization between SNAP sessions. * *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.33 2006/02/01 20:21:27 pauly *ENH: An improvement to the main SNAP UI structure: one set of GL windows is used to support SNAP and IRIS modes * *Revision 1.32 2006/01/05 18:03:09 pauly *STYLE: Removed unnecessary console messages from SNAP * *Revision 1.31 2005/12/12 00:27:45 pauly *ENH: Preparing SNAP for 1.4 release. Snapshot functionality * *Revision 1.30 2005/11/23 14:32:15 ibanez *BUG: 2404. Patch provided by Paul Yushkevish. * *Revision 1.29 2005/10/29 14:00:15 pauly *ENH: SNAP enhacements like color maps and progress bar for 3D rendering * *Revision 1.28 2005/08/10 03:24:21 pauly *BUG: Corrected problems with 3D window, label IO from association files * *Revision 1.27 2005/04/21 14:46:30 pauly *ENH: Improved management and editing of color labels in SNAP * *Revision 1.26 2005/03/08 03:12:51 pauly *BUG: Minor bugfixes in SNAP, mostly to the user interface * *Revision 1.25 2004/12/31 17:34:04 lorensen *COMP: gcc3.4 issues. * *Revision 1.24 2004/10/04 17:41:46 pauly *FIX: Filename extensions for FLTK includes * *Revision 1.23 2004/09/21 16:13:35 jjomier *FIX: Linux, gcc3.3 fixes * *Revision 1.22 2004/09/21 15:50:51 jjomier *FIX: vector_multiply_mixed requires template parameters otherwise MSVC cannot deduce them * *Revision 1.21 2004/09/14 14:11:11 pauly *ENH: Added an activation manager to main UI class, improved snake code, various UI fixes and additions * *Revision 1.20 2004/08/26 18:29:20 pauly *ENH: New user interface for configuring the UI options * *Revision 1.19 2004/07/29 14:02:05 pauly *ENH: An interface for changing SNAP appearance settings * *Revision 1.18 2004/07/22 19:22:51 pauly *ENH: Large image support for SNAP. This includes being able to use more screen real estate to display a slice, a fix to the bug with manual segmentation of images larger than the window size, and a thumbnail used when zooming into the image. * *Revision 1.17 2004/06/01 13:33:55 king *ERR: Fix for cross_3d to work with both the ITK version and current cvs version of vxl. * *Revision 1.16 2004/05/12 18:09:12 pauly *FIX:Error with cross_3d symbol * *Revision 1.15 2004/01/27 18:18:44 pauly *FIX: Last MAC OSX fix * *Revision 1.14 2004/01/27 18:05:38 pauly *FIX: More MAC OSX fixes. Also removed old snake code no longer in use * *Revision 1.13 2004/01/27 17:34:00 pauly *FIX: Compiling on Mac OSX, issue with GLU include file * *Revision 1.12 2003/11/25 23:32:48 pauly *FIX: Snake evolution did not work in multiprocessor mode * *Revision 1.11 2003/11/10 00:24:50 pauly **** empty log message *** * *Revision 1.10 2003/10/14 13:44:27 pauly *FIX: Fixed warnings on gcc-3.3 * *Revision 1.9 2003/10/10 15:04:21 pauly *ENH: Cut plane improvements (paint-over-visible and paint-over-one) * *Revision 1.8 2003/10/10 14:25:55 pauly *FIX: Ensured that code compiles on gcc 3-3 * *Revision 1.7 2003/10/09 22:45:15 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *Revision 1.6 2003/10/02 20:57:46 pauly *FIX: Made sure that the previous check-in compiles on Linux * *Revision 1.5 2003/10/02 14:55:53 pauly *ENH: Development during the September code freeze * *Revision 1.2 2003/09/11 19:23:29 pauly *FIX: Code compiles and runs on UNIX platform * *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.4 2003/08/28 22:58:30 pauly *FIX: Erratic scrollbar behavior * *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.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.9 2003/06/08 23:27:56 pauly *Changed variable names using combination of ctags, egrep, and perl. * *Revision 1.8 2003/06/04 04:52:17 pauly *More UI fixes for the demo * *Revision 1.7 2003/05/08 21:59:05 pauly *SNAP is almost working * *Revision 1.6 2003/05/07 19:14:46 pauly *More progress on getting old segmentation working in the new SNAP. Almost there, region of interest and bubbles are working. * *Revision 1.5 2003/04/29 14:01:42 pauly *Charlotte Trip * *Revision 1.4 2003/04/23 06:05:18 pauly **** empty log message *** * *Revision 1.3 2003/04/18 17:32:18 pauly **** empty log message *** * *Revision 1.2 2003/04/16 05:04:17 pauly *Incorporated intensity modification into the snap pipeline *New IRISApplication *Random goodies * *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.10 2002/06/04 20:26:23 seanho *new rotation code from iris * *Revision 1.9 2002/04/27 18:31:05 moon *Finished commenting * *Revision 1.8 2002/04/24 17:15:13 bobkov *made no changes * *Revision 1.7 2002/04/23 22:00:39 moon *Just put in a couple glMatrixMode(GL_MODELVIEW) in a few places I thought it should *be just to be cautious. I don't think it changed anything. * *Revision 1.6 2002/04/22 21:56:21 moon *Put in code to get crosshairs m_Mode working in 3D window. Just checks global *flag in the mouseclick method to see if we're in snake m_Mode, so that the *right windows get redrawn. * *Revision 1.5 2002/04/13 16:22:40 moon *Fixed the problem with the 3D window drawing in black. *The draw method needed to call Init() in the if (!valid()) block. It had two *checks, for !valid, and m_NeedsInitialization. They just needed to be combined so that *lighting, etc. was set up either way. The bug seems to be fixed. * *Revision 1.4 2002/04/10 21:22:12 moon *added some make_current calls to some methods, which seems to help the window to *update better, but the color/lighting problem is still there. * *Revision 1.3 2002/04/10 20:26:24 moon *just put in some debug statements. Trying to debug the 3dwindow not drawing right. * *Revision 1.2 2002/03/08 14:06:32 moon *Added Header and Log tags to all files **/ itksnap/UserInterface/Window3D/Window3D.h0000644000076500000240000002604511136422002017546 0ustar paulystaff/*========================================================================= Program: ITK-SNAP Module: $RCSfile: Window3D.h,v $ Language: C++ Date: $Date: 2009/01/23 20:09:38 $ 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 __Window3D_h_ #define __Window3D_h_ #include #include #include #include "SNAPOpenGL.h" #include "RecursiveInteractionMode.h" #include "SNAPCommonUI.h" #include "Trackball.h" #include "MeshObject.h" // Forward references to parent classes class UserInterfaceBase; class IRISApplication; class GlobalState; // Forward references to interactors defined in Window3D.cxx class Crosshairs3DInteractionMode; class Trackball3DInteractionMode; class Spraypaint3DInteractionMode; class Scalpel3DInteractionMode; namespace itk { class Command; } //-------------------------------------------------------------- // plane struct //------------------------------------------------------------- struct CutPlaneStruct { // The normal vector in image coordinates Vector3d vNormal; // The intercept (distance to zero) in image coordinates double dIntercept; // The normal vector in world coordinates Vector3d vNormalWorld; // The four corners used to display the plane Vector3d xDisplayCorner[4]; // The valid/invalid state of the plane int valid; }; /** * \class Window3D * \brief Window used to display the 3D segmentation */ class Window3D : public RecursiveInteractionMode { public: enum {X,Y,Z}; /** * Constructor: registers this interactor with the parent UI and the * client window */ Window3D(UserInterfaceBase *parentUI, FLTKCanvas *canvas); virtual ~Window3D(void); /* Needed to be virtual to avoid compiler warning */ Window3D& operator= ( const Window3D& W ) { return *this; }; /** Initialize the window */ void Initialize(); /** Clear the 3D display */ void ClearScreen(); /** Reset the trackball to default position */ void ResetView(); /** Perform the GL drawing operations */ void OnDraw(); /** Recompute the mesh (slow operation) */ void UpdateMesh(itk::Command *xProgressCommand); /** Respond to user pressing the accept button */ void Accept(); /** Enter the cross-hairs mode of operation */ virtual void EnterCrosshairsMode(); /** Enter the trackball mode of operation */ virtual void EnterTrackballMode(); /** Enter the scalpel mode of operation */ virtual void EnterScalpelMode(); /** Enter the spraypaint mode of operation */ virtual void EnterSpraypaintMode(); irisGetMacro(Trackball, Trackball); irisSetMacro(Trackball, Trackball); /** A parent class from which all the Fl event handlers associated * with this class should be derived */ class EventHandler : public InteractionMode { public: EventHandler(Window3D *parent) : InteractionMode(parent->GetCanvas()) { m_Parent = parent; } void Register() { m_Driver = m_Parent->m_Driver; m_ParentUI = m_Parent->m_ParentUI; m_GlobalState = m_Parent->m_GlobalState; } virtual int OnKeyDown(const FLTKEvent &e) { return m_Parent->OnKeyAction(e.Key); } protected: Window3D *m_Parent; GlobalState *m_GlobalState; UserInterfaceBase *m_ParentUI; IRISApplication *m_Driver; }; // Allow friendly access by interactors friend class EventHandler; friend class Trackball3DInteractionMode; friend class Crosshairs3DInteractionMode; friend class Scalpel3DInteractionMode; friend class Spraypaint3DInteractionMode; // Methods called by these interactors (unlike GenericSliceWindow, this window // does not delegate much of its control to the interactors and only provides // these interfaces for them void OnRotateStartAction(int x, int y); void OnPanStartAction(int x, int y); void OnZoomStartAction(int x, int y); void OnTrackballDragAction(int x, int y); void OnTrackballStopAction(); void OnCrosshairClickAction(int x,int y); void OnSpraypaintClickAction(int x,int y); void OnScalpelPointPairAction(int x1, int y1, int x2, int y2); int OnKeyAction(int key); private: // Pointer to the application driver for this UI object IRISApplication *m_Driver; // Pointer to the global state object (shorthand) GlobalState *m_GlobalState; // Pointer to GUI that contains this Window3D object UserInterfaceBase *m_ParentUI; // Interaction modes Crosshairs3DInteractionMode *m_CrosshairsMode; Trackball3DInteractionMode *m_TrackballMode; Spraypaint3DInteractionMode *m_SpraypaintMode; Scalpel3DInteractionMode *m_ScalpelMode; // Cut planes 3d mode added by Robin enum Win3DMode { WIN3D_NONE, WIN3D_PAN, WIN3D_ZOOM, WIN3D_ROTATE, WIN3D_CUT }; Win3DMode m_Mode; Trackball m_Trackball, m_TrackballBackup; MeshObject m_Mesh; CutPlaneStruct m_Plane; // id of window int m_NeedsInitialization; int m_CursorVisible; // Coordinate systems: image coords have unit distance equal to one voxel, // origin is at the origin of the image (left bottom back). // World coords have same origin, but distance is in mm. // The mesh object is in world coords, but the crosshairs position and the // rays to fire into the seg data are in image coords. // Extent of the image, in image coords Vector3ui m_ImageSize; // Matrix from voxel space to world coordinates (NIFTI/RAS coords) typedef vnl_matrix_fixed Mat4d; Mat4d m_WorldMatrix; // Dimensions of a voxel, in mm // Vector3f m_Spacing, m_Origin; // Dimensions of the image (dimensions * spacing) Vector3d m_VolumeSize; // Center of image, in world coords Vector3d m_Center; // Center of rotation, in world coords Vector3d m_CenterOfRotation; // width of view, in world coords Vector3d m_DefaultHalf, m_ViewHalf; // A ray and a point used for projecting mouse-clicks onto the 3D surface Vector3d m_Ray; Vector3d m_Point; // An array of samples typedef std::list SampleListType; typedef SampleListType::iterator SampleListIterator; SampleListType m_Samples; void MousePressFunc(int button); void MouseReleaseFunc(); void MouseMotionFunc(); void MouseCrossPressFunc(int button); void MousePointPressFunc(int button); void MouseCutPressFunc(int button); void SetupModelView(); void SetupProjection(); void ComputeMatricies( GLint *vport, double *mview, double *proj ); void ComputeRay( int x, int y, double *mvmatrix, double *projmat, GLint *viewport, Vector3d &v, Vector3d &r ); /** Try to compute cut plane. Return false if the points coincide */ bool ComputeCutPlane(int wx1, int wy1, int wx2, int wy2); int IntersectSegData(int mouse_x, int mouse_y, Vector3i &hit); // void OnCutPlanePointRayAction(int mouse_x, int mouse_y, int i); // void ComputePlane(); void DrawCutPlane(); // Added by Robin & Ming void AddSample( Vector3i s ); void DrawCrosshairs(); void DrawSamples(); void CheckErrors(); }; #endif // __Window3D_h_ /* *$Log: Window3D.h,v $ *Revision 1.4 2009/01/23 20:09:38 pyushkevich *FIX: 3D rendering now takes place in Nifti(RAS) world coordinates, rather than the VTK (x spacing + origin) coordinates. As part of this, itk::OrientedImage is now used for 3D images in SNAP. Still have to fix cut plane code in Window3D * *Revision 1.3 2009/01/17 10:40:28 pyushkevich *Added synchronization to 3D window viewpoint * *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.13 2006/02/01 20:21:27 pauly *ENH: An improvement to the main SNAP UI structure: one set of GL windows is used to support SNAP and IRIS modes * *Revision 1.12 2005/12/12 00:27:45 pauly *ENH: Preparing SNAP for 1.4 release. Snapshot functionality * *Revision 1.11 2005/12/08 18:20:46 hjohnson *COMP: Removed compiler warnings from SGI/linux/MacOSX compilers. * *Revision 1.10 2005/10/29 14:00:16 pauly *ENH: SNAP enhacements like color maps and progress bar for 3D rendering * *Revision 1.9 2004/09/14 14:11:11 pauly *ENH: Added an activation manager to main UI class, improved snake code, various UI fixes and additions * *Revision 1.8 2004/08/26 18:29:21 pauly *ENH: New user interface for configuring the UI options * *Revision 1.7 2004/01/27 18:05:38 pauly *FIX: More MAC OSX fixes. Also removed old snake code no longer in use * *Revision 1.6 2004/01/27 17:34:00 pauly *FIX: Compiling on Mac OSX, issue with GLU include file * *Revision 1.5 2003/10/09 22:45:15 pauly *EMH: Improvements in 3D functionality and snake parameter preview * *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.6 2003/07/12 01:34:18 pauly *More final changes before ITK checkin * *Revision 1.5 2003/07/11 23:25:33 pauly **** empty log message *** * *Revision 1.4 2003/06/08 23:27:56 pauly *Changed variable names using combination of ctags, egrep, and perl. * *Revision 1.3 2003/04/23 06:05:18 pauly **** empty log message *** * *Revision 1.2 2003/04/16 05:04:17 pauly *Incorporated intensity modification into the snap pipeline *New IRISApplication *Random goodies * *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/Utilities/0000755000076500000240000000000011560342171013517 5ustar paulystaffitksnap/Utilities/CVS/0000755000076500000240000000000011560342171014152 5ustar paulystaffitksnap/Utilities/CVS/Entries0000644000076500000240000000000211560342171015476 0ustar paulystaffD itksnap/Utilities/CVS/Entries.Log0000644000076500000240000000011611560342171016224 0ustar paulystaffA D/FLTK//// A D/Forwarding//// A D/InnoSetup//// A D/Linux//// A D/MacOS//// itksnap/Utilities/CVS/Repository0000644000076500000240000000002211560342171016246 0ustar paulystaffitksnap/Utilities itksnap/Utilities/CVS/Root0000644000076500000240000000010011560342171015007 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/0000755000076500000240000000000011560342171014257 5ustar paulystaffitksnap/Utilities/FLTK/CVS/0000755000076500000240000000000011560342171014712 5ustar paulystaffitksnap/Utilities/FLTK/CVS/Entries0000644000076500000240000000000211560342171016236 0ustar paulystaffD itksnap/Utilities/FLTK/CVS/Entries.Log0000644000076500000240000000006011560342171016762 0ustar paulystaffA D/Fl_Native_File_Chooser//// A D/Fl_Table//// itksnap/Utilities/FLTK/CVS/Repository0000644000076500000240000000002711560342171017013 0ustar paulystaffitksnap/Utilities/FLTK itksnap/Utilities/FLTK/CVS/Root0000644000076500000240000000010011560342171015547 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/0000755000076500000240000000000011560342171020547 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/.TAR_RELEASE.sh0000644000076500000240000000234511250505506022752 0ustar paulystaff#!/bin/sh # # CREATE RELEASE TAR FILE # For maintainer only! # VERSION=`awk '/^[0-9]*\.[^ \t]/ {print $1; exit(0);}' /dev/null mkdir -p $RELEASEDIR cp -rp .TAR_RELEASE.sh * ./.release/Fl_Native_File_Chooser-$VERSION sleep 1 # prevents 'file changed' during tar # Remove local settings awk '/REMOVE:START/ { remove = 1; } /REMOVE:END/ { remove = 0; } { if ( remove == 0 ) print $0 }' \ < $RELEASEDIR/Makefile \ > $RELEASEDIR/Makefile.new mv $RELEASEDIR/Makefile.new $RELEASEDIR/Makefile # CREATE TAR FILE ( cd ./.release; \ tar cvfz $TARFILE --numeric-owner \ --owner=0 \ --group=0 \ --exclude=scp-to-seriss \ Fl_Native_File_Chooser-$VERSION ) # CLEANUP rm -rf ./.release 2> /dev/null echo "*** Created: $TARFILE" # UPLOAD if [ -x ./scp-to-seriss ]; then ./scp-to-seriss $TARFILE; fi exit 0 itksnap/Utilities/FLTK/Fl_Native_File_Chooser/CHANGES0000644000076500000240000001372311250505506021547 0ustar paulystaff# WARNING: Makefile automatically derives version number from the first line # of this file that starts with x.x # | # \|/ # v 0.86 - 09/05/2009 -- applied patches received from users ----------------------------------------------------- o NO API CHANGES IN THIS RELEASE -- FIXES ONLY o Applied Gary Hui Zhang's patch from fltk.developer 09/04/2009 to use FsRef instead of FsSpec (to support OSX 10.6 Snow Leopard) o Applied Walter Garm's recommendations to remove BIF_SHAREABLE from the WIN32 flags. When BIF_SHAREABLE is specified, apparently it HIDES shareable drives in BROWSE_DIRECTORY mode. In his case (and mine) mapped network drives (eg. Z: -> \\meade\net) would NOT show up in BROWSE_DIRECTORY mode if the BIF_SHAREABLE enabled. (Drive would only show up if specified as the preset filename) o Applied Manolo's 3 suggestions from Oct 08 2008: > Remove the 'Default' format from BROWSE_SAVE_FILE dialog by adding kNavGenericSignature as 3rg argument of NavCreatePutFileDialog() > Fixed BROWSE_SAVE_FILE to not fail if file does not exist yet > Converted all kCFStringEncodingASCII -> kCFStringEncodingUTF8 o 4 space indent conformation 0.85 ----------------------------------------------------- o Enable options(fltk::NativeFileChooser::NEW_FOLDER) to work for Mac. fltk1 & fltk2 docs for options() updated; NEW_FOLDER flag turned green for Mac. Feature added for Mark Brevoort. Posted patch to fltk.general on 11/21/07. o Updated the COPYING file to be LGPL (somehow a GPL license got in there) 0.84 -- 10/01/2007 release ----------------------------------------------------- o FLTK2 SUPPORT! Merged in Frederic Hoerni's mods for fltk2 support, converted to fltk2 style namespace. o Makefile fix for IMGLIB from Gonzalo Garramuno (fltk.general 09/30/07) o Incorporated Ian MacArthur's Makefile for fltk-config o 80 column compliance o Build: > Tar file now extracts with version number in the directory name, > 'make tar' logic now moved into separate shell script o Windows: > Fixed memory leak with lpstrFile (alloc'ed twice) > Removed unused strfree() code in ClearOFN() > Small code formatting fixes 0.83d -- 11/23/2006 fix release ----------------------------------------------------- o Linux: > Added Shane Hill's #include to Fl_Native_File_Chooser_FLTK.cxx o Windows: > Added OFN_NOCHANGEDIR flag to prevent GetOpenFileName() from changing the CWD > Added Andreas Sch?mann's fix for _nfilters++ to the add_filter() method > Fixed showfile() GetCurrentDirectory() call o Copyright fixes and README.txt brought up to date 0.83c -- 02/03/2006 fix release ----------------------------------------------------- o Windows: > Fixed behavior of BROWSE_SAVE_FILE > Fixed SAVEAS_CONFIRM > Mods to getcwd() macros in test-browser.cxx for MINGW > Fixes to test-browser.cxx for MinGW compiles (removed #ifdef VISUAL_STUDIO) o Linux: > "Save File" now has working "Show SaveAs Confirm" dialog > "Save File" under linux wasn't letting user type in a filename > 'int type()' method wasn't implemented, added _btype > Removed ifeq() cruft from Makefile.LINUX o Applied Ian's fixes to Makefile.fltkconfig o Makefile was sourcing user's ~/.cshrc, added '-f' to SHELL=/bin/csh -f 0.83b -- 01/07/2006 erco: small patches from Ian ------------------------------------------------ o Makefile.fltkconfig WINDOWS -> WIN32 o Tar forces owner/group to 0/0, fixed perms on images dir 0.83a -- 01/06/2006 erco: small patches from Ian ------------------------------------------------ o Makefile.fltkconfig patch o Enforce correct perms on files in tar file 0.83 -- 12/06/2005 erco: misc ----------------------------------------------- o Windows: Added preset_file() support o Linux: missing filters() method o All: Added options() to API, removed macosx_*() o Added options() to the API with flags: o NEW_FOLDER o PREVIEW o SAVEAS_CONFIRM o Removed macosx_*() stuff -- bad idea 0.82 -- 12/05/2005 erco: misc fixes after merge ----------------------------------------------- o win32 filter fixes for Alessandro's 11/29 report o Rellocated Macos reference links from code to REFERENCE-MAC.txt o Made sure all #ifdefs use _WIN32 instead of other variants. o Fixed problem with FLTK chooser not showing its icons (natevw) o Added macosx_*() platform specific accessors TODO: See ./TODO for outstanding items 0.81 -- 11/28/2005 erco: linux version cleanup ---------------------------------------------- o Support documented filters o free/strdup -> new/delete o added #ifndef at top of FL/Fl_Native_File_Chooser.H to prevent recursion 0.80 -- 11/27/2005 erco+nate: code merge, cleanup ------------------------------------------------- o Merge in Nathan's changes o Updated docs for all changes noted here o Rewrote mac's filter code to support old "*.a" and new "Desc1\t*.a\nDesc2\t*.b" syntax o Changed Nathan's set_filter() -> filter_value() (consistent with FLTK's own chooser) o Renamed test program to test-browser.cxx o Code cleanup: variable + method naming, 80 column conformance, indents, strdup/malloc -> new TODO: Linux version needs code cleanup (strdup -> new, etc) 0.70 -- 08/04/05 erco: general cleanup -------------------------------------- o Implemented Linux. This was easy -- just use Fl_File_Chooser. o Removed references to non-existant size() method from docs; the correct method name is total_filenames() 0.62 -- 02/02/2005 ------------------ o Added Ian's mods to main.cxx to use getcwd() instead of fixed path o Added #include/#define's to support getcwd() under Windows VS 0.61 -- 01/26/2005 ------------------ o Applied Ian MacArthur's 01/26/2005 fixes, and Makefile.fltkconfig, including tab fixes and line 90 .obj -> .o fix. o Enabled /Wall on Windows VS7.x, and fixed all problems related to Fl_Native* (mainly, ctor type value was being ignored) itksnap/Utilities/FLTK/Fl_Native_File_Chooser/CMakeLists.txt0000644000076500000240000000025211226666572023323 0ustar paulystaffINCLUDE_DIRECTORIES(${SNAP_SOURCE_DIR}/Utilities/FLTK/Fl_Native_File_Chooser) ADD_DEFINITIONS(-DFLTK1) ADD_LIBRARY(fltk_native_file_chooser Fl_Native_File_Chooser.cxx)itksnap/Utilities/FLTK/Fl_Native_File_Chooser/common.cxx0000644000076500000240000000457411226666572022612 0ustar paulystaff// // common.cxx -- common string subs for Fl_Native_File_Chooser // // Copyright 2004 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please keep code 80 column compliant. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // #include // COPY A STRING WITH 'new' // Value can be NULL // static char *strnew(const char *val) { if ( val == NULL ) return(NULL); char *s = new char[strlen(val)+1]; strcpy(s, val); return(s); } // FREE STRING CREATED WITH strnew(), NULLS OUT STRING // Value can be NULL // static char *strfree(char *val) { if ( val ) delete [] val; return(NULL); } // 'DYNAMICALLY' APPEND ONE STRING TO ANOTHER // Returns newly allocated string, or NULL // if s && val == NULL. // 's' can be NULL; returns a strnew(val). // 'val' can be NULL; s is returned unmodified. // // Usage: // char *s = strnew("foo"); // s = "foo" // s = strapp(s, "bar"); // s = "foobar" // static char *strapp(char *s, const char *val) { if ( ! val ) { return(s); // Nothing to append? return s } if ( ! s ) { return(strnew(val)); // New string? return copy of val } char *news = new char[strlen(s)+strlen(val)+1]; strcpy(news, s); strcat(news, val); delete [] s; // delete old string return(news); // return new copy } // APPEND A CHARACTER TO A STRING // This does NOT allocate space for the new character. // static void chrcat(char *s, char c) { char tmp[2] = { c, '\0' }; strcat(s, tmp); } itksnap/Utilities/FLTK/Fl_Native_File_Chooser/COPYING0000644000076500000240000006505711250505506021616 0ustar paulystaff Fl_Native_File_Chooser License December 16, 2002 The Fl_Native_File_Chooser (FNFC) library and included programs are provided under the terms of the GNU Library General Public License (LGPL) with the following exceptions: 1. Modifications to the FNFC configure script, config header file, and makefiles by themselves to support a specific platform do not constitute a modified or derivative work. The authors do request that such modifications be contributed to the FNFC project - send all contributions to "erco at seriss dot com". 2. Widgets that are subclassed from FNFC widgets do not constitute a derivative work. 3. Static linking of applications and widgets to the FNFC library does not constitute a derivative work and does not require the author to provide source code for the application or widget, use the shared FNFC libraries, or link their applications or widgets against a user-supplied version of FNFC. If you link the application or widget to a modified version of FNFC, then the changes to FNFC must be provided under the terms of the LGPL in sections 1, 2, and 4. 4. You do not have to provide a copy of the FNFC license with programs that are linked to the FNFC library, nor do you have to identify the FNFC license in your program or documentation as required by section 6 of the LGPL. However, programs must still identify their use of FNFC. The following example statement can be included in user documentation to satisfy this requirement: [program/widget] is based in part on the work of of the Fl_Native_File_Chooser project http://seriss.com/people/erco/fltk/Fl_Native_File_Chooser/ ----------------------------------------------------------------------- GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! itksnap/Utilities/FLTK/Fl_Native_File_Chooser/CREDITS0000644000076500000240000000100311250505506021560 0ustar paulystaffAuthor ------------ Greg Ercolano -- initial version for mac + win32, code maintainer Contrubutors ------------ Ian MacArthur -- various fixes, additions, Makefile.fltkconfig Nathan Vander Wilt -- filter code for WIN32 and Mac, FLTK chooser Boris Mayer -- various debugging Frederic Hoerni -- fltk2 port Walter Garms -- Recommended WIN32 fixes for network shares Manolo Gouy -- various MAC fixes; utf8, kNavGenericSignature Gary Hui Zhang -- snow leopard mods: FsSpec -> FsRef itksnap/Utilities/FLTK/Fl_Native_File_Chooser/CVS/0000755000076500000240000000000011560342171021202 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/CVS/Entries0000644000076500000240000000216011560342171022535 0ustar paulystaff/.TAR_RELEASE.sh/1.2/Sat Sep 5 16:01:10 2009// /CHANGES/1.2/Sat Sep 5 16:01:10 2009// /CMakeLists.txt/1.1/Mon Jul 13 17:23:06 2009// /COPYING/1.2/Sat Sep 5 16:01:10 2009// /CREDITS/1.2/Sat Sep 5 16:01:10 2009// /Fl_Native_File_Chooser.cxx/1.1/Mon Jul 13 17:23:06 2009// /Fl_Native_File_Chooser_FLTK.cxx/1.1/Mon Jul 13 17:23:06 2009// /Fl_Native_File_Chooser_MAC.cxx/1.4/Mon Sep 21 21:55:19 2009// /Fl_Native_File_Chooser_WIN32.cxx/1.2/Sat Sep 5 16:01:10 2009// /Makefile/1.2/Sat Sep 5 16:01:10 2009// /Makefile-fltk1/1.1/Mon Jul 13 17:23:06 2009// /Makefile-fltk1.MICROSOFT/1.2/Sat Sep 5 16:01:10 2009// /Makefile-fltk2/1.1/Mon Jul 13 17:23:06 2009// /Makefile-fltk2.MICROSOFT/1.1/Mon Jul 13 17:23:06 2009// /Makefile.MICROSOFT/1.2/Sat Sep 5 16:01:10 2009// /README.txt/1.1/Mon Jul 13 17:23:06 2009// /TODO/1.2/Sat Sep 5 16:01:10 2009// /common.cxx/1.1/Mon Jul 13 17:23:06 2009// /make.bat/1.1/Mon Jul 13 17:23:06 2009// /simple-app-fltk2.cxx/1.2/Sat Sep 5 16:01:10 2009// /simple-app.cxx/1.2/Sat Sep 5 16:01:10 2009// /test-browser-fltk2.cxx/1.2/Sat Sep 5 16:01:10 2009// /test-browser.cxx/1.2/Sat Sep 5 16:01:10 2009// D itksnap/Utilities/FLTK/Fl_Native_File_Chooser/CVS/Entries.Log0000644000076500000240000000016011560342171023253 0ustar paulystaffA D/FL//// A D/documentation//// A D/fltk//// A D/reference//// A D/simple-app.app//// A D/test-browser.app//// itksnap/Utilities/FLTK/Fl_Native_File_Chooser/CVS/Repository0000644000076500000240000000005611560342171023305 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser itksnap/Utilities/FLTK/Fl_Native_File_Chooser/CVS/Root0000644000076500000240000000010011560342171022037 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/0000755000076500000240000000000011560342171023420 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/build-output-FEDORA3.txt0000644000076500000240000000537011226666572027661 0ustar paulystaff*** *** FLTK1 BUILD *** ( make -f Makefile-fltk1 all "FLTKCONFIG=/usr/local/src/fltk-1.1.x-svn/fltk-config --use-images") make[1]: Entering directory `/meade/net/erco/src/Fl_Native_File_Chooser' g++ -Wall -O3 -I. -I/usr/local/src/fltk-1.1.x-svn -I/usr/X11R6/include -DFLTK1 test-browser.cxx -c g++ -Wall -O3 -I. -I/usr/local/src/fltk-1.1.x-svn -I/usr/X11R6/include -DFLTK1 Fl_Native_File_Chooser.cxx -c g++ test-browser.o Fl_Native_File_Chooser.o -L/usr/X11R6/lib -L/usr/local/src/fltk-1.1.x-svn/lib /usr/local/src/fltk-1.1.x-svn/lib/libfltk_images.a -lpng -lz -ljpeg /usr/local/src/fltk-1.1.x-svn/lib/libfltk.a -ldl -lm -lXext -lX11 -lm -o test-browser strip test-browser /usr/local/src/fltk-1.1.x-svn/fltk-config --use-images --post test-browser g++ -Wall -O3 -I. -I/usr/local/src/fltk-1.1.x-svn -I/usr/X11R6/include -DFLTK1 simple-app.cxx -c g++ simple-app.o Fl_Native_File_Chooser.o -L/usr/X11R6/lib -L/usr/local/src/fltk-1.1.x-svn/lib /usr/local/src/fltk-1.1.x-svn/lib/libfltk_images.a -lpng -lz -ljpeg /usr/local/src/fltk-1.1.x-svn/lib/libfltk.a -ldl -lm -lXext -lX11 -lm -o simple-app strip simple-app /usr/local/src/fltk-1.1.x-svn/fltk-config --use-images --post simple-app make[1]: Leaving directory `/meade/net/erco/src/Fl_Native_File_Chooser' *** *** FLTK2 BUILD *** ( make -f Makefile-fltk2 all "FLTK2CONFIG=/usr/local/src/fltk-2.0-svn/fltk2-config --use-images") make[1]: Entering directory `/meade/net/erco/src/Fl_Native_File_Chooser' gcc -Wall -O3 -I. -I/usr/local/src/fltk-2.0-svn -I/usr/include/freetype2 -I/usr/X11R6/include -Wno-non-virtual-dtor -DFLTK2 test-browser-fltk2.cxx -c gcc -Wall -O3 -I. -I/usr/local/src/fltk-2.0-svn -I/usr/include/freetype2 -I/usr/X11R6/include -Wno-non-virtual-dtor -DFLTK2 Fl_Native_File_Chooser.cxx -c -o NativeFileChooser.o gcc test-browser-fltk2.o NativeFileChooser.o -L/usr/local/src/fltk-2.0-svn/lib /usr/local/src/fltk-2.0-svn/lib/libfltk2_images.a /usr/local/src/fltk-2.0-svn/lib/libfltk2.a -L/usr/X11R6/lib -lX11 -lXi -lXinerama -lXft -lpthread -lm -lXext -lsupc++ -lpng -lfltk2_images -ljpeg -lz -lm -o test-browser-fltk2 strip test-browser-fltk2 /usr/local/src/fltk-2.0-svn/fltk2-config --use-images --post test-browser-fltk2 gcc -Wall -O3 -I. -I/usr/local/src/fltk-2.0-svn -I/usr/include/freetype2 -I/usr/X11R6/include -Wno-non-virtual-dtor -DFLTK2 simple-app-fltk2.cxx -c gcc simple-app-fltk2.o NativeFileChooser.o -L/usr/local/src/fltk-2.0-svn/lib /usr/local/src/fltk-2.0-svn/lib/libfltk2_images.a /usr/local/src/fltk-2.0-svn/lib/libfltk2.a -L/usr/X11R6/lib -lX11 -lXi -lXinerama -lXft -lpthread -lm -lXext -lsupc++ -lpng -lfltk2_images -ljpeg -lz -lm -o simple-app-fltk2 strip simple-app-fltk2 /usr/local/src/fltk-2.0-svn/fltk2-config --use-images --post simple-app-fltk2 make[1]: Leaving directory `/meade/net/erco/src/Fl_Native_File_Chooser' itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/build-output-MAC.txt0000644000076500000240000000473711226666572027244 0ustar paulystaff*** *** FLTK1 BUILD *** ( make -f Makefile-fltk1 all "FLTKCONFIG=/usr/local/src/fltk-1.1.x-svn/fltk-config --use-images") g++ -Wall -O3 -I. -I/usr/local/src/fltk-1.1.x-svn -I/usr/local/src/fltk-1.1.x-svn/png -DFLTK1 test-browser.cxx -c g++ -Wall -O3 -I. -I/usr/local/src/fltk-1.1.x-svn -I/usr/local/src/fltk-1.1.x-svn/png -DFLTK1 Fl_Native_File_Chooser.cxx -c g++ test-browser.o Fl_Native_File_Chooser.o -L/usr/local/src/fltk-1.1.x-svn/lib /usr/local/src/fltk-1.1.x-svn/lib/libfltk_images.a -lfltk_png -lz -ljpeg /usr/local/src/fltk-1.1.x-svn/lib/libfltk.a -framework Carbon -framework ApplicationServices -lm -framework CoreFoundation -o test-browser strip test-browser /usr/local/src/fltk-1.1.x-svn/fltk-config --use-images --post test-browser g++ -Wall -O3 -I. -I/usr/local/src/fltk-1.1.x-svn -I/usr/local/src/fltk-1.1.x-svn/png -DFLTK1 simple-app.cxx -c g++ simple-app.o Fl_Native_File_Chooser.o -L/usr/local/src/fltk-1.1.x-svn/lib /usr/local/src/fltk-1.1.x-svn/lib/libfltk_images.a -lfltk_png -lz -ljpeg /usr/local/src/fltk-1.1.x-svn/lib/libfltk.a -framework Carbon -framework ApplicationServices -lm -framework CoreFoundation -o simple-app strip simple-app /usr/local/src/fltk-1.1.x-svn/fltk-config --use-images --post simple-app *** *** FLTK2 BUILD *** ( make -f Makefile-fltk2 all "FLTK2CONFIG=/usr/local/src/fltk-2.0-svn/fltk2-config --use-images") g++ -Wall -O3 -I. -I/usr/local/src/fltk-2.0-svn -Wno-non-virtual-dtor -DFLTK2 test-browser-fltk2.cxx -c g++ -Wall -O3 -I. -I/usr/local/src/fltk-2.0-svn -Wno-non-virtual-dtor -DFLTK2 Fl_Native_File_Chooser.cxx -c -o NativeFileChooser.o g++ test-browser-fltk2.o NativeFileChooser.o -L/usr/local/src/fltk-2.0-svn/lib /usr/local/src/fltk-2.0-svn/lib/libfltk2_images.a /usr/local/src/fltk-2.0-svn/lib/libfltk2.a -lpthread -framework Carbon -framework ApplicationServices -lpng -lfltk2_images -ljpeg -lz -lm -framework CoreFoundation -o test-browser-fltk2 strip test-browser-fltk2 /usr/local/src/fltk-2.0-svn/fltk2-config --use-images --post test-browser-fltk2 g++ -Wall -O3 -I. -I/usr/local/src/fltk-2.0-svn -Wno-non-virtual-dtor -DFLTK2 simple-app-fltk2.cxx -c g++ simple-app-fltk2.o NativeFileChooser.o -L/usr/local/src/fltk-2.0-svn/lib /usr/local/src/fltk-2.0-svn/lib/libfltk2_images.a /usr/local/src/fltk-2.0-svn/lib/libfltk2.a -lpthread -framework Carbon -framework ApplicationServices -lpng -lfltk2_images -ljpeg -lz -lm -framework CoreFoundation -o simple-app-fltk2 strip simple-app-fltk2 /usr/local/src/fltk-2.0-svn/fltk2-config --use-images --post simple-app-fltk2 itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/build-output-WIN32.txt0000644000076500000240000000775511226666572027451 0ustar paulystaff*** *** FLTK1 BUILD *** gmake -f Makefile-fltk1.MICROSOFT FLTKDIR=c:/fltk-1.1.7 gmake[1]: Entering directory `Z:/erco/src/Fl_Native_File_Chooser' cl /MD /EHsc /DVS2000 /DVISUAL_STUDIO /Zi /D_CRT_SECURE_NO_DEPRECATE /Ic:/fltk-1.1.7 /I. /DFLTK1 /Tp test-browser.cxx /c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. test-browser.cxx cl /MD /EHsc /DVS2000 /DVISUAL_STUDIO /Zi /D_CRT_SECURE_NO_DEPRECATE test-browser.obj Fl_Native_File_Chooser.obj /link c:/fltk-1.1.7/lib/fltk.lib wsock32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib winmm.lib ole32.lib oleaut32.lib uuid.lib imm32.lib /nologo /machine:I386 /subsystem:console del test-browser.obj 2> nul cl /MD /EHsc /DVS2000 /DVISUAL_STUDIO /Zi /D_CRT_SECURE_NO_DEPRECATE /Ic:/fltk-1.1.7 /I. /DFLTK1 /Tp simple-app.cxx /c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. simple-app.cxx cl /MD /EHsc /DVS2000 /DVISUAL_STUDIO /Zi /D_CRT_SECURE_NO_DEPRECATE simple-app.obj Fl_Native_File_Chooser.obj /link c:/fltk-1.1.7/lib/fltk.lib wsock32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib winmm.lib ole32.lib oleaut32.lib uuid.lib imm32.lib /nologo /machine:I386 /subsystem:console del simple-app.obj 2> nul gmake[1]: Leaving directory `Z:/erco/src/Fl_Native_File_Chooser' *** *** FLTK2 BUILD *** *** gmake -f Makefile-fltk2.MICROSOFT FLTK2DIR=c:/fltk-2.x-svn gmake[1]: Entering directory `Z:/erco/src/Fl_Native_File_Chooser' cl /EHsc /ZI /DVS2000 /DVISUAL_STUDIO /D_CRT_SECURE_NO_DEPRECATE /Ic:/fltk-2.x-svn /I. /DFLTK2 /Tp test-browser-fltk2.cxx /c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. test-browser-fltk2.cxx cl /EHsc /ZI /DVS2000 /DVISUAL_STUDIO /D_CRT_SECURE_NO_DEPRECATE test-browser-fltk2.obj NativeFileChooser.obj /link c:/fltk-2.x-svn/lib/fltk2.lib ws2_32.lib msimg32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /subsystem:console Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. Microsoft (R) Incremental Linker Version 8.00.50727.42 Copyright (C) Microsoft Corporation. All rights reserved. /out:test-browser-fltk2.exe /debug c:/fltk-2.x-svn/lib/fltk2.lib ws2_32.lib msimg32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /subsystem:console test-browser-fltk2.obj NativeFileChooser.obj cl /EHsc /ZI /DVS2000 /DVISUAL_STUDIO /D_CRT_SECURE_NO_DEPRECATE /Ic:/fltk-2.x-svn /I. /DFLTK2 /Tp simple-app-fltk2.cxx /c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. simple-app-fltk2.cxx cl /EHsc /ZI /DVS2000 /DVISUAL_STUDIO /D_CRT_SECURE_NO_DEPRECATE simple-app-fltk2.obj NativeFileChooser.obj /link c:/fltk-2.x-svn/lib/fltk2.lib ws2_32.lib msimg32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /subsystem:console Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. Microsoft (R) Incremental Linker Version 8.00.50727.42 Copyright (C) Microsoft Corporation. All rights reserved. /out:simple-app-fltk2.exe /debug c:/fltk-2.x-svn/lib/fltk2.lib ws2_32.lib msimg32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /subsystem:console simple-app-fltk2.obj NativeFileChooser.obj gmake[1]: Leaving directory `Z:/erco/src/Fl_Native_File_Chooser' itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/CVS/0000755000076500000240000000000011560342171024053 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/CVS/Entries0000644000076500000240000000056511560342171025415 0ustar paulystaff/Fl_Native_File_Chooser.html/1.2/Sat Sep 5 16:10:29 2009// /NativeFileChooser.html/1.2/Sat Sep 5 16:10:29 2009// /build-output-FEDORA3.txt/1.1/Mon Jul 13 17:23:06 2009// /build-output-MAC.txt/1.1/Mon Jul 13 17:23:06 2009// /build-output-WIN32.txt/1.1/Mon Jul 13 17:23:06 2009// /how-to-use.html/1.1/Mon Jul 13 17:23:06 2009// /index.html/1.1/Mon Jul 13 17:23:06 2009// D itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/CVS/Entries.Log0000644000076500000240000000001711560342171026125 0ustar paulystaffA D/images//// itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/CVS/Repository0000644000076500000240000000007411560342171026156 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/CVS/Root0000644000076500000240000000010011560342171024710 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/Fl_Native_File_Chooser.html0000644000076500000240000002774611250506565030623 0ustar paulystaff [Fltk-1.x] Fl_Native_File_Chooser

    [Fltk-1.x] Fl_Native_File_Chooser

    class Fl_Native_File_Chooser

    Include Files

        #include <FL/Fl_Native_File_Chooser.H>
        
    Dowload latest source from here.

    Description

    This class lets an FLTK application easily and consistently access the local operating system's native file chooser. Some operating systems have very complex and specific file choosers that many users want access to specifically, instead of FLTK's default file chooser(s).

    Methods

     
     

    Fl_Native_File_Chooser::Fl_Native_File_Chooser()
    Fl_Native_File_Chooser::Fl_Native_File_Chooser(type)

    Fl_Native_File_Chooser::~Fl_Native_File_Chooser()

      The destructor; destroys any resources allocated to this widget.

    int Fl_Native_File_Chooser::count() const

    void Fl_Native_File_Chooser::directory(const char*)
    const char* Fl_Native_File_Chooser::directory() const;

      Sets the directory with which to start the chooser. If NULL is passed, or if no directory is specified, the chooser will attempt to use the last non-cancelled folder.

    const char *Fl_Native_File_Chooser::errmsg() const

      Returns a system dependent error message for the last method that failed. This message should at least be flagged to the user in a dialog box, or to some kind of error log.

    const char *Fl_Native_File_Chooser::filename() const
    const char *Fl_Native_File_Chooser::filename(int) const

    const char *Fl_Native_File_Chooser::filter() const
    void Fl_Native_File_Chooser::filter(const char*)

      Gets or sets the filename filters used for browsing. The default is NULL, which browses all files.

      The filter string can be any of:

      • A single wildcard (eg. "*.txt")
      • Multiple wildcards (eg. "*.{cxx,h,H})
      • A descriptive name followed by a '\t' and a wildcard (eg. "Text Files\t*.txt")
      • A list of separate wildcards with a \n between each (eg. "*.{cxx,H}\n*.txt")
      • A list of descriptive names and wildcards (eg. "C++ Files\t*.{cxx,H}\nTxt Files\t*.txt")

      The format of each filter is a wildcard, or an optional user description followed by '\t' and the wildcard.

      On most platforms, each filter is available to the user via a pulldown menu in the file chooser. The 'All Files' option is always available to the user.

    void Fl_Native_File_Chooser::filter_value(int)
    int Fl_Native_File_Chooser::filter_value()

      The first form sets which filter will be initially selected.
      The second returns which filter was chosen. This is only valid if the chooser returns success.

      The first filter is indexed as 0. If filter_value()==filters(), then "All Files" was chosen. If filter_value() > filters(), then a custom filter was set.

    int Fl_Native_File_Chooser::filters()

      Gets how many filters were available, not including "All Files"

    void Fl_Native_File_Chooser::options(int)
    int Fl_Native_File_Chooser::options()

      Sets/gets the platform specific chooser options.

      Some platforms have file choosers with specific functions that can be enabled/disabled via this method.

      Flags can be ORed together to enable several features. Flags currently supported:

      Flag Description WinMacOther
      Fl_Native_File_Chooser::SAVEAS_CONFIRMWith a BROWSE_SAVEAS chooser, prompts a confirmation if file existsIgnored Used Ignored
      Fl_Native_File_Chooser::NEW_FOLDER Shows the 'New Folder' button Ignored UsedUsed
      Fl_Native_File_Chooser::PREVIEW Enables the 'Preview' mode by default Ignored IgnoredUsed

    void Fl_Native_File_Chooser::preset_file(const char*)
    const char* Fl_Native_File_Chooser::preset_file()

      Sets a default filename for the chooser. (Use directory() to set the default directory)
      Mainly used to preset the filename for save dialogs, and on most platforms can be used for opening files as well.

    int Fl_Native_File_Chooser::show() const

      Opens the current file chooser on the screen. This method will block until the user has chosen something, or cancelled.

      Return value:

      • 0 - user picked a file, filename() will have the result
      • 1 - user hit 'cancel'
      • -1 - failed; errmsg() has reason

    const char *Fl_Native_File_Chooser::title() const
    void Fl_Native_File_Chooser::title(const char*)

      Gets or sets the title of the file chooser's window.

      The default title varies according to the platform, so you are advised to set the title explicitly.

    int Fl_Native_File_Chooser::type() const
    Fl_Native_File_Chooser::type(int)

      Gets or sets the current type of browser. Possible choices are:

      Flag Description
      Fl_Native_File_Chooser::BROWSE_FILE Browse for a single file
      Fl_Native_File_Chooser::BROWSE_DIRECTORY Browse for a single directory
      Fl_Native_File_Chooser::BROWSE_MULTI_FILE Browse for multiple files
      Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY Browse for multiple directories (implementation varies)
      Fl_Native_File_Chooser::BROWSE_SAVE_FILE Browse to save a single file
      Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY Browse for a directory, allowing creation

      These may be changed in future versions of Fl_Native_File_Chooser.

    Typical Usage

      #include <Fl_Native_File_Chooser.H>
      :
      Fl_Native_File_Chooser *chooser = new Fl_Native_File_Chooser();
      chooser->type(Fl_Native_File_Chooser::BROWSE_FILE);   // let user browse a single file
      chooser->title("Open a file");                        // optional title
      chooser->preset_file("/var/tmp/somefile.txt");        // optional filename preset
      chooser->filter("Text Files\t*.txt");                 // optional filter
      switch ( chooser->show() ) {
          case -1:    // ERROR
      	fprintf(stderr, "*** ERROR show() failed:%s\n", chooser->errmsg());
      	break;
          case 1:     // CANCEL
      	fprintf(stderr, "*** CANCEL\n");
      	break;
          default:    // USER PICKED A FILE
              fprintf(stderr, "Filename was '%s'\n", chooser->filename());
      	break;
      }
      


      // EXAMPLE 'SAVEAS' FILE BROWSER #include <Fl_Native_File_Chooser.H> : Fl_Native_File_Chooser *chooser = new Fl_Native_File_Chooser(); chooser->type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); // 'saveas' browser chooser->title("Save As.."); // optional title for chooser window chooser->directory("/var/tmp"); // optional starting directory chooser->preset_file("untitled.txt"); // optional default filename chooser->filter("Text Files\t*.txt"); // optional filter switch ( chooser->show() ) { case -1: // ERROR fprintf(stderr, "*** ERROR show() failed:%s\n", chooser->errmsg()); break; case 1: // CANCEL fprintf(stderr, "*** CANCEL\n"); break; default: // USER PICKED A FILE fprintf(stderr, "Filename was '%s'\n", chooser->filename()); break; }























































    itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/how-to-use.html0000644000076500000240000000543611226666572026342 0ustar paulystaff

    How To Install Fl_Native_File_Chooser Into Your Own Project

    The Fl_Native_File_Chooser project can either be added to FLTK, or installed as a subdirectory either inside your own project, or in a separate directory that all your projects can refer to.

    Just be sure to compile with the -I (include) flag pointing to where the Fl_Native_File_Chooser directory is, and to link in the Fl_Native_File_Chooser.o file left in that directory after doing a 'make'.

    Installing Fl_Native_File_Chooser Into Your Project's Directory

    Here's an example of how to install Fl_Native_File_Chooser into your project's own directory:
            ../src/YourApp/
    	        |
    	        |-- YourApp.C
    		|-- YourApp.H		     -- Contains "#include <FL/Fl_Native_File_Chooser.H>"
    		|-- Makefile		     -- Compiles with "-I./Fl_Native_File_Chooser", 
    		|                               links with ./Fl_Native_File_Chooser/Fl_Native_File_Chooser.o
    		|-- Fl_Native_File_Chooser   -- Fl_Native_File_Chooser installed as a subdirectory
    		:       |
    		        |-- FL
    			|   |
    			|   |-- Fl_Native_File_Chooser.H
    			|   :
    			|
    			|-- Fl_Native_File_Chooser.o
    			|-- Makefile
    			:
    
    This way you can still just have "#include <FL/Fl_Native_File_Chooser.H>" in your C++ files.. just use "-I./Fl_Native_File_Chooser" as one of the compile flags, and the #include will resolve correctly.

    During linking, be sure to link in "./Fl_Native_File_Chooser/Fl_Native_File_Chooser.o" as part of the link instructions for your app.

    Recommendations

    It is advised you locate Fl_Native_File_Chooser in the same source directory your FLTK version(s) are installed, eg:
        /usr/local/src/fltk-1.1.x-svn/		     <-- some rev of fltk
        /usr/local/src/fltk-2.0-svn/		     <-- some other rev of fltk
        /usr/local/src/Fl_Native_File_Chooser-0.84/  <-- put Fl_Native_File_Chooser here
        
    In your Makefile, just add the Fl_Native_File_Chooser directory to your include path, and add the Fl_Native_File_Chooser.o to your link line, eg:
        # FLTK1
        INCLUDES = -I/usr/local/src/Fl_Native_File_Chooser-0.84/
        LIBS     = /usr/local/src/Fl_Native_File_Chooser-0.84/Fl_Native_File_Chooser.o
    
        # FLTK2
        INCLUDES = -I/usr/local/src/Fl_Native_File_Chooser-0.84/
        LIBS     = /usr/local/src/Fl_Native_File_Chooser-0.84/NativeFileChooser.o
    
        foo: foo.cxx
    	    g++ $(INCLUDES) foo.cxx -c
    	    g++ foo.o $(LIBS) ..
        
    itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/images/0000755000076500000240000000000011560342171024665 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/images/CVS/0000755000076500000240000000000011560342171025320 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/images/CVS/Entries0000644000076500000240000000006611560342171026656 0ustar paulystaff/native-small.png/1.1/Mon Jul 13 17:23:06 2009/-kb/ D itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/images/CVS/Repository0000644000076500000240000000010311560342171027414 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/images itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/images/CVS/Root0000644000076500000240000000010011560342171026155 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/images/native-small.png0000644000076500000240000014263411226666572030016 0ustar paulystaffPNG  IHDR5aPLTE:~҂&2FvV6r@M_ZRRR⚚bbfrƎ֒Ƌ"Bfn\l~~RZbn###n~BBB&^򮮮:Zqqqt***r552y6bKGDH pHYs PtIME 9$/ IDATx} {L&mر 4!$Hs6UI/v-gv#\ī.*jG/j/j)M0|d:P>t~C/NCOWFà+%_SozwFw19X?)j~&ڤ}j_[?r|ȯvb {FWZ*eޡ`ϽOt=%/TZk~tEߟ3rabEGB(#ma\Kn2}Wvݫ/3{u0*' ?Nz#-~p]!_dʟSvtĂ_^9?938W/O|Q{;[ ! >vA{~4WeUq$p hB;Od$d5hЪ >˔{W6ldI]d?*+ֈl0XJ| ?Xf6"V{IzXepnVsf)4gvfNjpe5V$fHTQLBXΐe͍u wjb{ !$qZ_IhSm$>X  R=Txɏ}F%xž +F--ߤl(2zPv6mm %ĉΉxXDv<EuXceRs#ގߒmQQ5鍩[hTi&`8 JJ|YO(K9A,<@-񞂔kl"=T5t]kX6~{ ^VUkAoO/^re`]C֖7 uRNv uܮ1$ٻ.C Hv Mߌx3JZxMWPs n.nm-ɯ*wo@yAP r\܎We y|N2ta1o+w!z αA<-\.:mI.!iqKY%? *0 Ø$Fim1( (S580_PAIa 12XCAh |%&YIVzmjQ~&z>iidl\%vmBKLD@Uxw/l6 !`VX(H#-EFW#PJZ ;/dIYWW2oƐ w&8x**8-jz#h/GBaqj  1)B[ |;s a#(jJ cZL'(v`Eb9Hf4olMH`'6xa(H@RYD>mluES!yŻ'Y>#ѮLm`BCQޖ }Y`#\aݒe~{#UNݕޚgpq{q6s#1pnvnbBon_5U#fCǂb;$0|$>ӨD̫x{jVLAwER}3m$rQD oE`6%XS#eFT˜ Q " wwQQ,>2U{c4[}\*N5Gӑ9;:eDޔ:kmې+@&،@nĺuÓ7$*tt>ܚ7v_hΑ*rp1f&H눂FHl$W&X x}i .BVչAeP* 7%Sa>b"ym0qСdQf:$M|6c O>kҁ5#cV") 4t 'H,+gG%/J fet E]hpiuGg>DJ_Ϝ, &<_@ i7U\ %GHkGN%2%5Uf(Uո* $n5XP/E0!VU'QI{K{dhd=1@1}%;=Qhg9slmo%lvBlz-oO?PՕ]&pXsZ޶Eյگj7tr7F3\g>1wvɘY363SKbңuϘ'm6#<×Ƹ͋Rhپo<#nnXBMZ]7wN+tyٶ^[ =}w׾%op*PjYbmX!g MHwGYvgRzz3'LʄE{Ol3SiUD;?xwgi~&~QN]'a3<4sNN=6Nm?{{=șga/ l+~V9]ntl[Nm3'Zl4̷ ρE(6Ú?h=v)wf?u3=}w.9/Al[U4kjNC3<bchZiy}pNH07h_;;ѻDzVIwr?WIçU2'ʸwnoh?ձ:Ikbr +/v8z^U֒8~G7kk"mBɜ?2_=>t ax-~B[`mu\2:퉄y$lrˀ_>xNt n9Q?^̀%:D9H`+`7'zD%̅AQ˕',+ܘձ' [_}?sĮy~rQpd~%<9jgVJxRCNmMtE*euW{Fv,q\WY_W;xG* o";PxpHv[ }q{|g6ލx2~;^v|wO]=kˇv{zuZs@|&zP?M9B,f.gʽa,_VgNڋi% %$~v߷j^ۼ O$/xYXt1RcR<~87n~.ӮAݴގxX{q}ݬm_gNkWr]b·NSM:VlZy&UNLN/|sgwBClq|nlz{k/ 7Lֻ/nK; ۖ5A$sS< Zl0K_j r_ZynsIuM%ƙzLrVvY-;A93";Uh/}vxSp4#6>G[be-?>^5} v۷- otnW<|&_%RKizLiI{ ٥ …[ |$(eفP@-ujd] F%*Ũ{ٱ*K( W:I)OZEfF AoPEꋛlVv6-gU_W?llF;y0}>/^?(8۴wKej3~.׽p`h9w~ m%[B"p8FI(h' O;84o,c~tu+OslY /_|oc{ p>BmۇvC8wD owWzE],R6ǂ_%ƨwyd6d0 ~ iBc" 1(OiJ*\ dW¯dT}]fXRi  m ~f>}۷W˄V9Q)h*HIP OdTqVD(i~%rΆbRy\P櫑%R!œ0_YI}"XIKi : )n$tqUHfTEu,52^x /뿥}~?}Kn}x8<>lu׍lR~$* VYG=p,e@~Hb)ۻo:^)Z!tAoxlHj2PAG2=j`8-6dݼrTW?Z7:D\A?I=k wv!۳eVU_?Sȧ__˹Eaoݏ{ o?q/..$cY-yWv-GI5fIZeeN.1&魷vlr]ʭ{>]SGx}C&%1|$h$6G_<1Z t<\Gca{cUͯ`⓯_* Apo~Aey ?nvhq<&x IDATv4B{O};߇ǂezB=bBLl`ᮖAcL3SeQlJ8K 8ޤɊ(-aޓyfN=&]gꑍU@Aw o]-FVݿm;< 6kk>YǭxW ^W;yQ4n,Lh'p']-5ZTf+}9,<ܧIƆ/'-9 N|1ܑ.Ki}lCY{sDZ$z;a<ֻˋqa{%]͹,k8/z,(o"z|oϣ/jFf\ۺo,)}-cZL&{OH&Bi]ߐ@ 1m^|9=K>Nd;F8io]YvLFfD=; z8 ;{ HqѴu ݈ 8θ1)ͷ)8{ov!L-CA0 t7=M}'(${r6>՟s{4ߥNZj\!{PwsYy&@z= 6[SQ /I?ڑ1`plTw 7?p9֭3A2u-x@2,l%O&kxuȅx9հ'R KLŽǦm F%zqxoWe=`  ܈WtluC.0͸I7VguWJxJffvӱt,e(ɏiR-QJ v+m)[#ra B G}S1ǣ8"G{Á&Y͙>E hHʝSoiFɁ$s9+Qm "qlg~=]ٗ!җ-5RL 0J:gܡe$^&u:V^0 Umxx*׎9]A+@-ϙ] =vv(9@aZvQo)Q(\&=T<^rV=!9yiUYd4 ~) Ac*tf AbmDob-0sfr³M<چf*72AaNLN`O 8<lL,1*w4f.;{/39e ֩6}^7B|60Thd"tnJP\-Ы~Zokiϓ:GRBR2J*FO w%AE7{@,R Z&QwjTVvڥhv܀h&]fXw٦$Icc$)B[΁Kۋ;Ж+_E"v b\./@ܯc=wo}s|OoypW7m<)rp:*IyLS!%%Q/]FpI D>! #" Rw_˞)RKE LPpwdYkZuf(˔n1]ɟo߾~| ࿘UuY7? 4atm\QM ¸y}!_^x7gwG8q4W.#hAMJuNg!Q6G{.u U@}`z#^rixh,T>̅j3Ew4LtAu-⥡bQ f"e\9]g,MXxgUyZsi2 ϟ>qyin1+Ln%:^b|bY\MɟrDEbe1 ^h1USZbz2 Y(A~-@Պ\^uyٌ[Eԩ])Yl?OW0L2&΢!kXj@֫%_~_WOޮI(Hލk3u-WZEAs¬JZ OaNy'[6\hs{9Xۢu=&%ph$׮"*yʁe+ Ǘ`QQf1Bf,u,TT-ՃkR-jPSD0W|NWu]'PThvoQvkw,ۻuJ IR6"q٨[xqẐ@28e,=55_=~\djt:h] Tӷyn(_SP;[[6 ec@Gu`vh~!<4c*S58\F֓=zStŤr\V e:YKo>=0fϦ uC΃z 7گj%:G5^}@|ޝ'W_/$-6"/,Yz23 B޺_)lWrK$0p#tڻ>ȯlI]yTz%WlvR$ 6bq˴-k_F*msDҮ,#88NIo(?ssL$H\E^iGUp>(ctI )GfӸT˹ClS_>RZ u鞙 ]lNac##n}L5Y'/_:3(lrN\z{`[m/? |Z] ~oNDȞ(9wǿ᳛`k^#JJK|2& nuh%G4P39;gyy8e {`_6C.*7o/) }]lR+ML'`lqdm':[uw wB`hHT]ND&?^)w,؋zijZ+fS:x<{gWE`'xl-\ ˅F569Arpp=\Xmږ>̙XIoSqgiO'GZfwguA,xg?j8p3oB `H+1eXX7ͤ'Vc7Xǒ6M7x7 Q#im]u\`g{u}zʣ_> &"fj.v=sUvw'`wvgYr1{lS 1`,pk=bϚ/%>y'sho's:4X9'iNc͔ :oj^љդ A m;ؐղl4\v^2]i ռNeEZvT_NĶT/Fxyy F,zg8oiŹ7x=)ti[wy֛1txlĜfh?Z8hY%'.C+JHUU*J*)nBo:$tkĪKeҴ{AGhHX+Ҩ]%lx+sPl#GyYS5ƇO?M:w&Z`"->w/ʋ2I$_RE IaK>X,0+9'gw9hh ~L3"w7·|`vU,kP浒~f7-6n=.jo`& ^=T vʌik9{]u/?|?^6吾nVO{WmueGWο5e#{7~Q-[._BQbPErځ8^x8R8-j+e6|L1UNQ"ew2e LzQXJZǩP%oR7E2JXҥP>:]Q'i=J~p+q׼NNt}Zv>\X?su/:XRX ȗcX8Cr}sj|Z7s[?}_^:px\(f&UvcШJ(LYű %%OWd二"@y)k47r*՘UL=ۊ*VA)MyhO[u)w/,x"sn>x~ó9\ XGӼϚ`ZZ[:H׼Y@_^mC'M`, Ǽ)IAB4"fu{_h,檑1k"<UQhʸa;<*`C7CN*Piّ;^ߡzSL4kĄ4[T``{wǏ>=kÓsGPb- Js[hPuBf+(,SXaok?\^/e͇b ~I QC&Y  @ $,chq@ d@@D%8 #O`CFQ1 x*FFk(GHCPCץGgC`{|O?XͿ~\K&Qũ%S1ܿ(~ D`bw]f0`s8ay]>Ҍm;GcFP(bMaeSS,ɛ* *C,0$$,,HadBt T'j t| cP(S~n𧧃?H Vi/xhM0KBVwow>q?>՗Z?\kh2aJ5L&iR䦛EGH!%fm°WY?v2sI3V$2w]R|$Χs* i@Tfd`q Ks7eGJ^pkc\s3x.μ>(\~0otyؤjMIWPI6DRR=~31\s`:67jO1hz)vԒf UnSx|tnY~}[9M Z e#(,Cp ~"ck0x6]p?9o>~z4>6nslnl[y*ǒ}!L"L)bzwh O4xP,nh\t[W5Iq'*mƦd2J.Llu2kv$Q7`cI[lVL}W>D)G`}[SIJjV9 D ߝGsu~o𰄿w>wtwv9&6QFk6kچ(!>i-~첤P}Ssu.ᓆ*Kxi#=uMzF?2;Chؽ.0Ƕ͕KҸY/ Cׁ8**h\ T0 a7T\[o'n ub#;ܛRe/OlXkc2:˴x,4IlQr'i){< m4lٿg` BCa>M>F& K~,secEEG?s TiaH@1I!\yB Q>\QJ!Ыhm73ORvf(o'81ksp\)xQn ΫpQ2Vuf- H;ݻn{{1urv GgUm_/6h)4^YYl#y;:=(HeB|~{R,4U%*BՔWՀC.AK6H-ٓX7L}dQm:][O{:cr  Z Ry(75L& ~`{1*djS,B*!q*/Y8O)w檬k,Phrޞ{_2n##&0 YLb\1;JS TŸ i0ck 8t UޥDdir?F 7 !e`H6xǩѕm!FD0 Lzx{DWIZm[x{ w/ Ƶar烰#pZ3WOCO>L6kc6È^=Adl8 LUDЫNG@BEWנ5-BRep CtG aeD0HJ6 `ۅ0,yhѪ;K Ld6kp?1/gͩپZlƬv" QjY. ?qٳӘŋKHbD+L,jгP5"=\l|m҇!bw~. 0%dΖt.9 y3"ۦ Csq_^:kфy\/$B FeU;N=WS Ѡx ?N G9 fuJjsSI.@ZAecTuQwp^LM~" SxN59 =) _ x7v 55i4vuiO;5gjqu/pi,BnTx{n$ @$EZD<($\covzp{lxyn<~B~膕Ѽ0FO"g?TZ׊hT7?~ -%!ȷkhG\WІMiF\X?p(PZq`qSFjzߜT3ҢxuùTyb9pjYdʰL蟦R@6*Pxf΂ .\/Mɜ0Tx^1ȅW|eM:ƨT&@((`=Rq'"[c+;,'0:Zg?:n~ñNu[WCO~D)XG1M[dYYgٔ;YO[HY?:Opz \Koy#SQ3,Cb.U]c1 HT(3M x;cr҇LbL\VCn4J R2wLߥyYNЦ^X'lv(lOg,K_u͗Q~1-L Pψ|wHŽ 8{5j?bRPŘ҂)=Οuu<X3~& ё=ŸFE?06T-tޙ@HFi4@֚V`NpA878`UaG,VF NiNW_ut~DM )ȔQ}QY hBVl,˶;Í* ~N{Ezq&30lalEo|Fe #f'WLhu[@{~#@p`x!R$A14A0>"\CӾ0sfGQ{CC19"V!PI<H3˘2V'w)jGebi|!^/DŽҙx፱+WAG}):U;;S{'(+7Ytm,4Y3C\=KV%Ύ𸰊࿷FOA<%Lrt7uevVy$s^MHۄ>G7fqe ò:a]0bf%T.2}A'J1E2v9 ީmM59=hLtFx#8v.(J!!#99b͂!UU,_cC>ݔsyaaqԾOrɷ|kco&J>Eo\q)zMs=?p}>Oy¢v/o3m\j Զ1{A.a$8 h jZVW@DTnDyMo`?<0\_VXȀlϾ/H;BNݼr?{kR *îkIHl\';R1簯>/qS2ulRḎTm3OJdz ~] MsZQjxqYa)1)"n~ƅ31Jp=,/P`=}Y㇏ ]6'^+ez0qRTD4qMI>{w9Wk| EAې GLm5O ot6쒿4Ι9_yglsEa3DlJr-/+tL YVC( ;Ny~J B[!Ʉ_,uoA_a$i_f85eFN.`e,i0d81 1h^DrtQ28q+)UYHkRq*:zfM Dh>\u3o[?RE-=IEX; pWC֓>ifߖǀMQUvPE*Jk:HB jVlE;p&_tPsI7+ek*`>{m:oeTtwZ2ji Dq|͕f emnֶ'_)?W_3:l"qSߥ7L6dd8 -[5r:e ۍX4m숨hS +Km")gbe}\G^zr,1S()4TpiFaEn9:Ǫ;Ǚ^msxlge`} wҺyP~V~]ڟ~\~Rc%:/"I |hFxuڍ\K9Ue"ˬfw&`4q&J**K6`V$jobV@z ,YwYҞG9d噔h/T\8*Xhmi0`RNH$e^[8T}u^,yy>lI[}}bRIoES^DDwfrK"\N;lU\jІNTՄ+{!2qI5epfS9tޝ/~U/ԘD+mXS(MKEf5dXMljgs 9%Us8Y)H\970Q\boRbSoJ\34ɏw8 /`vCK@erj}Wg76""9RWsQ`CRC 3bnFmiZQvwZ\s,wмSPh#2&IMpJc2S#ہ Vf yW\ ~b^$gyr;Lk擊yK.Rjaܪry_\6T|fwm~(6 74I=M{YWO*D)ߘ*((MgjS,lvk;os*jWs,_N@(}B>?&3)G 4L+Xnu#qN~ BwљT?0pq}_SM[WT@(ꆼ®-x1#j@|Ūus*~wރ*F653gº4>U2v \PJ}K5;liʠRvT`"GUK 6̱$` dHMsXLY*l@t?ݖ+4} 'Ų-[9+NO&Ds:d Ϩ7_1jE 9f q6XY"LU%"\VK{#buMXX,M NАR]ڃ* P X%mo_wyH>˅ИJ+l:1^`RISnE.DՐٲhЛm]:ٳMq9=,+eF9f ߙ䲾:(A",0cP.|!M*WUtԴC4M˄+?C,%30,2Ty:B$u(c&sP,h$LW?Nm3cԇ  Ք Xs%{ٰzaĄA.0רJ`x5U.u-gYu+p1"NtE07KycH)’6Z($u245]QtSÊNgfjʆ߯.Ut&>{/;٨: Y/*LUԨ aGm^ɀ2#!UN4 eeb#IWvTS7v2yG$CM@Ţ tI9E1euG=ɴGgnnRfn&_<_sJ8+#+J~a~~cqv}uQ mND Z =ڥ (PEqLBϵZDԘjO5cr%*K[i [$L^su.42N[0O֣_ԭٻ6q` MIqpHd!- "U {՝vNmh43kîȼ^J0ǭKND6mJ~_| ec#zP5yWzb-ja6?GYeYf!^Vf4APe+Fꡪ[ -csU"#Y_,Ֆ/[٪tM\>I%[#^)"[7 Qdi8c39*(<" tv1Ԓb%EޗhI1 uGwכO.pb,]F ~1&/GH_byVpWVˣ%i W5#maTK^$(ci}Ҡ/BɡT>&D 9!$p9™M9 `aAlgHJ` D#cJY* [)VO⓳ RߡP&WRȑhe:=X<^iLc$OJvzc Kx骜 :R%x#OL *dq@/JJ*EB5{= @ '^tQɥv>z׋:)k?I:I )Tq/~QKᤶk]tv8=|v6Ek۾$U}٪μ5kǜ{J S&7uKy3, IH'i  \'UA.NG6}ՔU)Hn|~d٫g2b BY  5 nWQsبn!G6_TPufqXx#V% Quu:=?F bUkdJH6̑| SQ8gse+]ۆϤhj?5wf %P\,/GeRUA}_QqT3_*H>idTGi'wFæDeƥiMPyOD1lK$ˍ+|/~| wTpP+C2VpNf͛ٻv2J^2OՓzZ=S}OL+FxEyBf0iЁLUJ̔0n"q'i@VT"U,0md_Cg{rjþiJ-n{'^G篿v:yu骳d|Qwײ]!8oA1]O70G7G9ۜ1Fxz(Ȇ]!iA#T>5?I>Nֲ4GSB~yAQ~]zuꑌ\3c$*2P\^~zmXx6Ӌ44 Z9 IDAT rZԄ\uO0BXmjc}'-TL*0x[7N/_+ t-_4M+'ua)7kUA5V53j E=mO>3^oernT퉾L-@ǫ"qyjM[jJVc֋"%LUޓ8r{&h.F^d-6.r ;,0V9'O,C;MWFENfy~ ?~iʂ'TZT eF҂K師uCxb.|Y(~\jOw߅ԝWuk}V=C'X܈ .f&,Ujb?Sƃ剜(ňm*]S{&``1Obq̼^cz:YNvfd?X-VC{391!y{z9dG%u2 iaXAwQMc̙;x(T7mBx&\;B_K).Nzv`&mSqx{6S,DWvLPǾs)!sJ%6UӏSoz{߿k9Fx_jkBbh4YŴZ qX})*gGMS{dUp< bäK`Jvjࢡ}I#%"nzBH\G4R̕/H A E'yZYUgqV7AVk/PuLʀK`ߟxϮNoR|>u2>/MЛ1mbGh—-xl1sl_Y/adO; TT*K̽X,-A(8< Y(ÎK 2-Z奦M` $IڧlZo:W)5*9"zs_wyWEk^ʃ媿x1v}e X)} iꐥuݦM Zkm IRf`C _WM"Ps{V|ۍ ^|1:e :j\bsC˫ymJU^ ls*m̛=߈ް רaqibs?*T(KdYF+#rke8E!"Mb4iXɟW~-bRԤ킘"Iu."yYKCd~UGDNUJY0T+7)N~#Nu"NQfxbP?uBem_w h)>Pį0e;E݁I",-B$j|GmLt7tW*]ѯ`z큍m5\"JVꌴN{]) mABu?Os5kH(nML&EQ  lcYFUBYbbRilHnM%d@Dhoox:$/%aEq{i|C/Otʋ)frM d%=wo Q έDebL,[ŵ!aIf:껻ɌOgITg`DeFE2:se:#G60ÐxC<͗M&QDҖP"VpqKob*)D%-DU2JsbrupyMAU3Ug5y:XJ.uٙ%q)XR'`Bq,eHwb>YP7{t Y0%nC,h }k pkGth u Sxi.kmөu䫷3q3$w͡v6m$ I/ڰ.f|];:-L@xR ڢ'A>oG-lq TX@ 2 UzV!jU=%aA ) (I~au)2RxgLwpi(#DmTبXR< < ~Iu96a.bfz+TL,r S8y7<[?N.S t½TZR5O:DS/n Q](aSDb"i!,cp&KPuXЏ\NDJɌRKUkiKlV'1yr,Z p$㛴X7/}| G$mжKr(s`Q3WX~&iHߔ P+ꙮ)*fte? Ft%z{Z'qZc4pLo؜%Xğ]p״ %踊 FŊ:0a /1H<dj,T:JBmjb|p5ˌst;Y(M*V&&/Y~]bۚ#Ҫ@=>+ ~f :j]^4R.8uީtss"s 2u gx&=kcZgԋ)ٜ נּp^\|>8I\ǂYDŽNx n3f#;SL&NS;_ ))NN>UJi9TrxE*nOԜ*0UvC]ƴUT4^rKzm_`Ҍx>SFh?crZHULTV%2JJ:I=S`3l04Kۊ2kU9F/ ~5?8%R}F*'E0?Gm5\*3WHg W> 'NtfPxE= J|K2^cf Mo6MS )_Lj .d` zlpVx HMsHEJZu}T?o2 6ѫD/gonΏH+}W5p\#. y:%3oð*眷h_mJ~fVU^ Z{d o:%SxH&f=1UZ*LUT: KlzJwӒ完dR= ?mYH7MmbIj` 1vR-PAaXVqZ*\D]EhJ,CDL(c*\mtB`&9u$:=YV[&=4<,f$L6P&s*/FҶPO2F|/%ك='m  e@p='1]4 BWקphj ĤD*2g~ g^M&\}:>>8>>_Cl']շ]ǰ7~ޮ@?GV<$lWg\4*Rϟ3$i0eT׉|xl1K+T) ^PZx4O5 <;z{ zr׶uq&D>O,`*ezcWЉ"j"􁚄WR np.`'p\vy{ÇƁ /01 +1隷GFA` ґlںY]>)7"bd*gI*{UMP2wcRH Ϫq=oJݫ@kz(%];~| js|[^p݇wGi'`8>Ľr)00x>NI'g!ICޖHK߸8)L@1;)Fή>SoYЉm?'J ϐ-QEU򫮣d`.:ǞJj“"R:T?:~|j6 O=ǻÓGq<:XگHjG_MBPop)o;x.siJ oÆbB3evR{ҽ9 `{7`Ac]D"l(qK7{_ fW_>˨xdD|:xqvt~q;:zj'2ӟY7m[檕+F6ЄkbͰ%ْy6k.ZP _.G'J43ty1uW-\\}qjdxëg?`)́:<^}ܿ8?秇=:Ň_|Pɏ2:dG 1](oK WwbY1-3l7yFdȕCqy٭݇#I7EfdT㛣㽛?ox=|9psaoo*? upS | imQ,J`⸤pF#tX2.Kye3l#" r7''HgG~G Ab>=~;99;9;w7{O! .?1`X#I3/EO,\ kKD]ioj3GfGFba߀aHACpxdf_͖`g]}toRͻ2ƑM 8O.o`6kO"G1 ?jgLmN˜jdN#\5kK `l?ΎN{7g1)l9|v :gOW:|z/SXxӣ?LOqGV;fm7RIJ՞lP[: %|̿u WJ,E /mZSupmԱ:@?ԝ. WM]c߆?E:0uՖ O g@aڱծV.c%&u0pe1i$XxRn\>BZFMQ͹Ž'/_gm iqƍ*[̊m7#GB7 KǏg09 !]V>xS6mLfUl<醊ާ?q?TA&ɟ]]:Qi֦$H"Zwy'Cw.et1G~g\h-r~y ;+(ɓ4fȌib~Q\̈IhgY5\dٝW.y4V, tSdOOݏ6-B{[w9M@eFGYǔLHYu/Xrk IDATP9ϾAH׬u03B:TA3:Yu/GE֗:l% a/SbJu|v8|ħSjv"^eXi3fFw hIT'HU1l߁_meFKs&(5B¡s ?6K9ZҋxR!!v$5 {T, h++|BM%?Jת ڻlzxxz/*Ij"UoD(*UM> >͞JxB:ޣM/DGm;U}ɖ*%h.ex +J*A$P4a;{nO~thNO(s ` RNu\XlEşk8q$CoFӝsU/ iԒ]Sr114,1:籊nf;atT.󳳳/ܺia2A`C,SoKΕsž?hQ)2@˰ܵ؇b?Y${rg'3Jõ4>mgAPVoiHg gFpuA3-3n?ڤTx%QcR#hx*B 6>ylr]W .bXlCHht5M)ƴlR{fl\7 v%u6 ؓT~296oHwsEO_ ~:?+gu?ܼpzzv@צLݜ{64 vYAA8eu[Il.xUs?N5__,'{xfo`Y_ZI7WAd- C~^n*eVZ!u|XHak mΰe4f6㛶"5 @d2uQ5 _.6%lȷғ \e+W)aMdM+yZzr~zʲ< W' #*(%NC(**~~uYVɚFqY~"{wƜoV " *6jA,jdz/'U0Kbe~Qw (3u(Yp1Kɤ:&5$ApTr]tj|mjfIї24j@oE*]n; g|.b+\W/Bp*B'|Ͻ7\~nwfQ NQߪy^$3@ZvoXksz<صY`hyEp"kI9k=rx'N-b+VjiF_)]E30;_{yn~p~6s c,)FY@ZA'2A8-O|B  g5[.HʴD`J*`P9Aa*Mm\co'ov>ly&UJ5燠?t6?% &([Q(Mgyp R! 9~7,۰?0I JT$5M4H]AR*K;`"?H)dyE3twb\l_)$LY3?m>O'֢?oD뵍W^ҧG[7׳}(Wo8zr^J5߬6K*2z\‘vRK=m (=I׺0r9Q *ieBw:$TLމw?{/KrCN-. S}WE$THÒNf:'UoC#Uı7'u-20F Py>@<zxF["kTXJk 'D~ĀfƱq]gj~( {d&$a)CTgY:L> a@j4D|SB"7mWustPe.ސY+ڻIWD2^}+)'0[ܞoHǔ *;?(Km1Oge]Ģ&eG=*UTV/~k\Ffu6]%I`L4Y"K/Lhde!**jxelvE)jOSho;2Rj;= _,t_Q2Y*s9E:;%\ OeXkY [5x)%) ƤMR"労N$pT`9hY;kXd& [ \P\-=5; PFIǚu6tU [L %?t]^>ӶONRЁ@YEq$kiS0-횤[&)JUkGc'su=I`GiQuTIg1%I [قWSL(߂-8}mWcnɪ7\~FM97* , 4 jyjv6!(] (1Ϡ8D;)=Eq@ZMWU3$Aoy ePah8f:ǰ/-b}55dvrQ3k?lcyU@TDE&`qf`T = %(G7.ĸ:L~K`ryMq9&70[ uy3XG56.ZI'E"-;*ug^vؠLŚM>Z=~jDH'$o6{Sɣ}8m[~ɻo"5T:˚(&iیxb 6ads@6Ʀ܇?jIH*_ju][^g932Zs$-e  S=nbj ͌Nc;y|ӫ/:C*؝e֢ `u:K+[s h[>EV[?({:/eQ3sՍC cJmcq\??}({C % fPkEpC`~h3DBaX78Y㪲|=Ǡڌ w*52isI8Sh%.{G RwOZ[d\ .}^?71%Iϖޞ_t7yj "ԝ!&: Q!ڸ.!bT6,"5Ht K2uE;WL-"Y+Z;?(0R^QzE؈Ԟק#<?f<ݵbXB/]z'82 Y_Wy:jyJ}Z3xb7 <4`7..X]I&I@|QiX ,qtϷW{|5L{Mws}y|Q! X2N:{`a: A7AļuPʭQLLE+B)ѐܛc+L(Iɽ0Úx&jV$v`<:ڗׯ/7\%gYEҷ huПP$a,: ´tTm@IP4Ȇ?.^[> 5xYCV Q4.H(klq6 ^lY?Yh=%/yqYyq>{cѵ \9' 3h>˭G_Zbs lGӴhc[[oU52Ww6ӏ-+ uHMo؋{zv2\Q#ynmM0a&yVU;|oX>_J,,MԫD"EI= _7K- KAUy{{+-04D~0X*Ngś=pαn|RJ˂/{Nw&wwX7J~VgzR*qӪa?%M@)Y 0e-܃M Ű>[ yf<&-s;=ž Ё&QҼy]vwMT4b[4Rw"e=F"HqRg- lHjӷL$t3!\dxPZImV/sw׭X}<3ž^hkm vFEp:lOt@E{;s,/#jEx$$yÃ84Er71evY*`(LeyR7m0lJ SdfTIL+0OcZH&R]cL؆~mÇE PXŢݠeyamLujIV7`V }bNBl9ńԇ*.^7!} 82բnJܭ}[ S4E$?t0U*Wa,?:hpbf5 )ưA'5p|Fa4ꏑXߧgvlϧYMf&p?g6|0"זT$M͠ʓ.myƣ[d1:n8aør<Yݼ$NIBH8&b,caxOy+cBtlZ!7 * PkY(Ɵ:{ ,_"oϘs[W;g}z\=, ;{adѬ-@zP+x'|8REu'$C)Tۥ~ܨH z6D`Lpn9l& kK&^h#TcC*~/ןΎ}ӳӣ/Ͼ|9=|==AMw}seNS]9 ^cRH d!օB>ywt4;9><4?_y\\^~89yw0ޞ7ͱށMAO?hMҜ N<JA).^ԾjIF'BKVuu &4"ha]x= Ԟ*i Ao?98p2?N./N` d% NM˛=N͕+$_iY'uT7iifS;0& %}YLT S/,I\,/nɇ./?\]^'on0~mW[!g ؇/{N~M#J$hOǃoh :˦&V+X cXfsޟ{\-vPs :~ɿ3x?Qcjcb-I{MyGQќu$MhXz{qf)"-ӳ7x` ->~~lkV1 Ae( .Ilt7LٮrRU]ִǙ +!Ej'a\_^GGBJ4ٍ-uR:FqF:!QV>Y a:پʏ7w#DI LqMbx#f-,O˗/:bQS`ZKW.l| ]xoeA8e6(T~;r+S(g0)1X+ЄG>bfpMAse0t8@KwLg`E/1~v! StLpcԻ &uroq缃 Vb=1vRc&p0eaLv Vr*tI$C $F~%&|JT5p;-qfY ?Wpݎqڸҡ™$vk 5h:4kE^7;'n;nn;nn;nn;nn;;nn;nśg7/0;۟'?PltQ?~d급x7~$OLy7v79݁Qy&Nj1`_ )ߛ'%7A kl-4Ro-naLBw_ z j~U>{==?-\>)y%PJ7IW1ww|; {H3cl]ͷn;՞۹{C[8T}L$ː;> Sfs.}f1eǷ }PnwfmDGwQ>6L~)yLf֔Jψb)$OC7),T#4Qa ibx)v.q-!Fnq-hn_lßϔ̽[RVâZ%}0B=xdkX+%d}@}ϋ IDAT1#^."lx6 mr.7? K9L!j䯼yURy?co*Ho %`pa@QÚ㗥'q oRIPFK;<(TVkHSpfG;5ӀWz$ -.EO I\ ^7srᄷUN$b `}DcM0 d UARPJw};~BV4?ߗ,Zw'}[,{s|EGЙB(h6 >jNGW( YN6 lSop^R+Sx:`\6J |y?,2O=.ö®%P~WU/@@ 4?CP]XKX/ܧ,^= ]>xaHtsϷ뇇h0`{C UǨ=9{?W`U66˼b4)*W=4xPλoˬl'[D1s5to?1ŭ?oʒBp%< ` Niu;Өs kh^[)'Kw~`q1fD}(c:oӭ; jde@9WL&W8L Z|PPomdN"PQ4~(XO\)@#)bQq2ށ_xN<'j3%71K:m^2w {̀W +2TOD6^q]]ͳ }U`T  wl0C` h{zh՝ꃧuGН_?(U AV7 6H.~~ ^P!AqgV! "&]36Q䣣^+Ki䟃R$iI,SM=n^06`? C d!bKID2wN+IFRNqYk v :BZ':r<{ ^rD;:؅Sz֝$&0b}f/Q/XX % CUpA`Q SY{+3h֨Bw]JO{,r6@ՄT@[mul[^)+} I/lӏa_w~O >tExY߳yX=N{s_]`R Ѫ:uT`:o]َ͂^f_uNr[mO\Ej^|s h_-"d)5_Kv@F)%lq!d}l#'k0jjدIX F dag)_60 BFL͗'KmU J*KD>zzR*IV8w=xn&VuKcgy]9V>vճǽz??ȧ'/Ÿ=x'Xxk50 t=Ɵwmܔ$غ߯,sQ!gt)2y(?8*GUo|Mof8F?lx[ !X}&pcf?~c|Z'^^:vrn \ՖV5jӒQU3z}>s^GYfn0ݞσZW,&owQd: @uܲ4AqyJj|yeeL?{19㏕`_.??vSd*^K\EbhTؿݶ0=ڡxE;vwp1iͳmx;3;|bo]|6M{{P(BQ(BQ(BQ(B/Wz8"/Ӑ|T^ Oj+(GqOgysJI5 |Qc V"Yؖ*QCE*V~J*Y=}D<l??(*{ ^A[|ʋ( W ٸm(S4⿦aD}W+zjӡ=?"]S@)71|acC8 rnJ;bMJ h}3PȪۏV [crncTb7fɹ c&ft{J*~CR6IjoݾҾ֋n! +'pG g *`(b,R(M@lU%7[<= 8 3]ު_ ܦ^<0UWъw{ɝZtJ!i߶\ xQh?#z"'6o؎TҗT{|,m{_؋*7ym+tݾ뻿<2_w wꇖ(c7ۇzB^%xyTW< a?m?8 _Do*폯0`J=%L7ſmtWۛ[o>um4_ PZŭZܯ$7_%57O +[Z^(#GP&xScХ*LOMimGfntWL,2IMn rOVn`_P]ܭx?,NʾY۽2?K^ޮmޮN jcZcO?jh>?t`9?]Act5f#ź=!ǃɦ]c* m,>5"ǃKɌcxG (2 z-lm_C-63V< p>ˆBhxߏ[_,Yqቘ0?i ެ|{5xzk{{?*;SizcsgBgz(GP##;?<toy >?_st~\z3J@]-he]o2H> ~䲘XrPG7?`Y:PJwqlBZnB8i~3I5UGm1jcğQHOו?-J,uiRפBʼg_S=U:%ă h:s˓ 'KOSnOi 'd&I~c< /]T e e>b~{ bik~ͮOyD?f@S< :~6rDM1fű"{/_{'*g#4)>ZV.m^ۊǟR^h>Y;m OZOkZX?iqv\W~w{=nϩw,? #Ix&} ~b])vu]|ʙAm{2)I<>2ֈ*.m H%xM!/A\1ͤGx1uwtGMs`\.ZSiXY n#8GG~?;9a\$ǎahgGO?K {0a45~Z},e,d>de9z=}aq%]62KP8A.q#ld$X2WqG?~k=.E] R9jKQGk=J[Xa+PR#n&<* .1K[YH".VVr4Ho~s,?D_#/G-ьw7PأR,Lj%e=bK~iC>C);'ED7v5!%qz?N?whHFH8u6md^{'i~0-c4]K;,c qMlFG.dKYIjX~^.!AJ55A"A(#n N)QoWDɆ#/r2~4{(9 b rKZ. Z)^,O4 ?kJZuv="mxlGx値9-vK= ">4yg?'iY4t ~e҄7F_{e({vWkkOȫ\\GÿIS@nl~-Zq ,sA!mTmm**dJCDLIDNQBd9ƺJ'}Yutպ(ӁCҜN=il:W۽~#~!k!0I:LcVDrBӚhh>_mӊ.8KZGo/ + ~~'?6x/]!϶';I튎[OCs_MM#bQ>8}: n V\hq[AoVa*sg39ڼ # A 9_SτkFh Gf\o`f1e i#e 2^yƷ2ni- bFޗ)9ZqM.v;v~N1312b 1BR~fsMs0e7l+`"bQ}!o_2חFVmÀZo"Tcbzt{28g)~mejp{  r/p`lk ^W\7xuYrz ^!O%KK~#E~G~ܻB͋P?vG0H?2dwY{R&䭂BͽB,S g9?{ۋ#"0Xm;ȥ4oݏD<J8 lCg %" _!1QR+$ﶨo`1>NIN^ __,䚯v r5|L/[Ex(-HOkSB'2R<?IQNWkM5\qxC'şD?/Z_6v&..ou)=]-I#?-݂KƕE'[m%칪oOÉ4?.3u AQ2 :>n KgXNwX.Aˇ?>O [Ӷ"15Sik"Y*ztzw]-g,6Fˏ_g"[KPP+!42o+#Wt;z.;MU7e *+}NM>z[+Βmk~JK ?'t_ ѓ'1)%2b_k~B1fL#W)G15wǶkl\G?SZDRU,.8%Fv2E4F_fT;[Hr?^CWnCk-q-LÈfv朓"?X؃3/#+"zIbX {`$kN~:~GA.-??3w>0Fk!LoIM,RaUlgU0&2$eqyj~-&%iZZCi(ɘ95 nz_0e MGe^&7f[&1g,-y@8F]>9 ۦ$'ldж,4u\ʡmdkρFܼUCf8)Ë}UA.nڇ-??ASfM?s:~e:HPq0~AVXviQXj-?0U!`" 7qw:#WNvLG0Ud&S"~]ePDä9=M[ rp'X _&P/B[$aіw :E;Q`1‹6K<%/iހR,E| ql¿#L^78 ~-ӔTq#s+-?y4  N0^#~ۃ#?#_32# 8= ~>8(K[IDE"~_,Dsaʄ2\·[?07 tIDATbc ?~~(,1,R+~GY3/[v`???;Q?#?#?RfNIYe?!5.?{*]vMzp7"o9X7 GG~m (c-{^3P#__ HsB'/O3~8c,b*']3='~L_8F fGQȆ~G~f'/jm+2%XᏥ_RZ~aڃdR6bYȷ_7=+1ȅooTd$\0NE I}FmNΓTlɨmق Z~mи[%"u?{ojXFB$okKHoKWWs^{\y W"="^k͕-Ђ9X~ OJL[/$-n6d%_,ʈH"[%LO`5 ˏƗ~K2ž|.V6GUl_?)صB~v~JLW0#~ހ#?#?#?#'Q>=*$ƢDs?|*zLKP?) eYxP@ӳ //ijND$?-wo,D)t{P+p^7M zm݃.Gjۨ/^9,"Dv@O5W1ȅB ~ r!g%Eߧ~RK2 C~G~G~G?BMAg j?3Z?+}zB!(B!(B!(B!(B!(B!(B= }H,0IENDB`itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/index.html0000644000076500000240000000201411226666572025427 0ustar paulystaff Fl_Native_File_Chooser Documentation

    Fl_Native_File_Chooser Documentation


    Examples of Native_File_Chooser on different platforms.
    itksnap/Utilities/FLTK/Fl_Native_File_Chooser/documentation/NativeFileChooser.html0000644000076500000240000002775011250506565027677 0ustar paulystaff [Fltk-2.x] NativeFileChooser

    [Fltk-2.x] NativeFileChooser

    class fltk::NativeFileChooser

    Include Files

        #include <fltk/NativeFileChooser.h>
        
    Dowload latest source from here.

    Description

    This class lets an FLTK2 application easily and consistently access the local operating system's native file chooser. Some operating systems have very complex and specific file choosers that many users want access to specifically, instead of FLTK2's default file chooser(s).

    Methods

     
     

    fltk::NativeFileChooser::NativeFileChooser()
    fltk::NativeFileChooser::NativeFileChooser(type)

    fltk::NativeFileChooser::~NativeFileChooser()

      The destructor; destroys any resources allocated to this widget.

    int fltk::NativeFileChooser::count() const

    void fltk::NativeFileChooser::directory(const char*)
    const char* fltk::NativeFileChooser::directory() const;

      Sets the directory with which to start the chooser. If NULL is passed, or if no directory is specified, the chooser will attempt to use the last non-cancelled folder.

    const char *fltk::NativeFileChooser::errmsg() const

      Returns a system dependent error message for the last method that failed. This message should at least be flagged to the user in a dialog box, or to some kind of error log.

    const char *fltk::NativeFileChooser::filename() const
    const char *fltk::NativeFileChooser::filename(int) const

    const char *fltk::NativeFileChooser::filter() const
    void fltk::NativeFileChooser::filter(const char*)

      Gets or sets the filename filters used for browsing. The default is NULL, which browses all files.

      The filter string can be any of:

      • A single wildcard (eg. "*.txt")
      • Multiple wildcards (eg. "*.{cxx,h,H})
      • A descriptive name followed by a '\t' and a wildcard (eg. "Text Files\t*.txt")
      • A list of separate wildcards with a \n between each (eg. "*.{cxx,H}\n*.txt")
      • A list of descriptive names and wildcards (eg. "C++ Files\t*.{cxx,H}\nTxt Files\t*.txt")

      The format of each filter is a wildcard, or an optional user description followed by '\t' and the wildcard.

      On most platforms, each filter is available to the user via a pulldown menu in the file chooser. The 'All Files' option is always available to the user.

    void fltk::NativeFileChooser::filter_value(int)
    int fltk::NativeFileChooser::filter_value()

      The first form sets which filter will be initially selected.
      The second returns which filter was chosen. This is only valid if the chooser returns success.

      The first filter is indexed as 0. If filter_value()==filters(), then "All Files" was chosen. If filter_value() > filters(), then a custom filter was set.

    int fltk::NativeFileChooser::filters()

      Gets how many filters were available, not including "All Files"

    void fltk::NativeFileChooser::options(int)
    int fltk::NativeFileChooser::options()

      Sets/gets the platform specific chooser options.

      Some platforms have file choosers with specific functions that can be enabled/disabled via this method.

      Flags can be ORed together to enable several features. Flags currently supported:

      Flag Description WinMacOther
      fltk::NativeFileChooser::SAVEAS_CONFIRMWith a BROWSE_SAVEAS chooser, prompts a confirmation if file existsIgnored Used Ignored
      fltk::NativeFileChooser::NEW_FOLDER Shows the 'New Folder' button Ignored UsedUsed
      fltk::NativeFileChooser::PREVIEW Enables the 'Preview' mode by default Ignored IgnoredUsed

    void fltk::NativeFileChooser::preset_file(const char*)
    const char* fltk::NativeFileChooser::preset_file()

      Sets a default filename for the chooser. (Use directory() to set the default directory)
      Mainly used to preset the filename for save dialogs, and on most platforms can be used for opening files as well.

    int fltk::NativeFileChooser::show() const

      Opens the current file chooser on the screen. This method will block until the user has chosen something, or cancelled.

      Return value:

      • 0 - user picked a file, filename() will have the result
      • 1 - user hit 'cancel'
      • -1 - failed; errmsg() has reason

    const char *fltk::NativeFileChooser::title() const
    void fltk::NativeFileChooser::title(const char*)

      Gets or sets the title of the file chooser's window.

      The default title varies according to the platform, so you are advised to set the title explicitly.

    int fltk::NativeFileChooser::type() const
    fltk::NativeFileChooser::type(int)

      Gets or sets the current type of browser. Possible choices are:

      Flag Description
      fltk::NativeFileChooser::BROWSE_FILE Browse for a single file
      fltk::NativeFileChooser::BROWSE_DIRECTORY Browse for a single directory
      fltk::NativeFileChooser::BROWSE_MULTI_FILE Browse for multiple files
      fltk::NativeFileChooser::BROWSE_MULTI_DIRECTORY Browse for multiple directories (implementation varies)
      fltk::NativeFileChooser::BROWSE_SAVE_FILE Browse to save a single file
      fltk::NativeFileChooser::BROWSE_SAVE_DIRECTORY Browse for a directory, allowing creation

      These may be changed in future versions of fltk::NativeFileChooser.

    Typical Usage

      #include <fltk/NativeFileChooser.h>
      :
      fltk::NativeFileChooser *chooser = new fltk::NativeFileChooser();
      chooser->type(fltk::NativeFileChooser::BROWSE_FILE);   // let user browse a single file
      chooser->title("Open a file");                           // optional title
      chooser->preset_file("/var/tmp/somefile.txt");           // optional filename preset
      chooser->filter("Text Files\t*.txt");                    // optional filter
      switch ( chooser->show() ) {
          case -1:    // ERROR
      	fprintf(stderr, "*** ERROR show() failed:%s\n", chooser->errmsg());
      	break;
          case 1:     // CANCEL
      	fprintf(stderr, "*** CANCEL\n");
      	break;
          default:    // USER PICKED A FILE
              fprintf(stderr, "Filename was '%s'\n", chooser->filename());
      	break;
      }
      


      // EXAMPLE 'SAVEAS' FILE BROWSER #include <fltk/NativeFileChooser.h> : fltk::NativeFileChooser *chooser = new fltk::NativeFileChooser(); chooser->type(fltk::NativeFileChooser::BROWSE_SAVE_FILE); // 'saveas' browser chooser->title("Save As.."); // optional title for chooser window chooser->directory("/var/tmp"); // optional starting directory chooser->preset_file("untitled.txt"); // optional default filename chooser->filter("Text Files\t*.txt"); // optional filter switch ( chooser->show() ) { case -1: // ERROR fprintf(stderr, "*** ERROR show() failed:%s\n", chooser->errmsg()); break; case 1: // CANCEL fprintf(stderr, "*** CANCEL\n"); break; default: // USER PICKED A FILE fprintf(stderr, "Filename was '%s'\n", chooser->filename()); break; }























































    itksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/0000755000076500000240000000000011560342171021050 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/CVS/0000755000076500000240000000000011560342171021503 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/CVS/Entries0000644000076500000240000000036511560342171023043 0ustar paulystaff/Fl_Native_File_Chooser.H/1.1/Mon Jul 13 17:23:06 2009// /Fl_Native_File_Chooser_FLTK.H/1.2/Sat Sep 5 16:10:29 2009// /Fl_Native_File_Chooser_MAC.H/1.2/Sat Sep 5 16:10:29 2009// /Fl_Native_File_Chooser_WIN32.H/1.1/Mon Jul 13 17:23:06 2009// D itksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/CVS/Repository0000644000076500000240000000006111560342171023602 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL itksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/CVS/Root0000644000076500000240000000010011560342171022340 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/Fl_Native_File_Chooser.H0000644000076500000240000000240111226666572025463 0ustar paulystaff// // Fl_Native_File_Chooser.H -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // #ifndef FL_NATIVE_FILE_CHOOSER_H #define FL_NATIVE_FILE_CHOOSER_H // Use Windows' chooser #ifdef _WIN32 #include #endif // Use Apple's chooser #ifdef __APPLE__ #include #endif // All else falls back to FLTK's own chooser #if ! defined(__APPLE__) && !defined(_WIN32) #include #endif #endif /*FL_NATIVE_FILE_CHOOSER_H*/ itksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/Fl_Native_File_Chooser_FLTK.H0000644000076500000240000000562711250506565026310 0ustar paulystaff// // Fl_Native_File_Chooser_DEFAULT.H -- FLTK native OS file chooser widget // // Copyright 2005 by Nathan Vander Wilt. // March 2005 - wrapper around Fl_File_Chooser // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // #include #include #include class Fl_Native_File_Chooser { public: enum Type { BROWSE_FILE = 0, BROWSE_DIRECTORY, BROWSE_MULTI_FILE, BROWSE_MULTI_DIRECTORY, BROWSE_SAVE_FILE, BROWSE_SAVE_DIRECTORY }; enum Option { NO_OPTIONS = 0x0000, // no options enabled SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite // confirm dialog (if supported) NEW_FOLDER = 0x0002, // Show 'New Folder' icon // (if supported) PREVIEW = 0x0004 // enable preview mode }; private: int _btype; // kind-of browser to show() int _options; // general options char *_filter; // user supplied filter char *_parsedfilt; // parsed filter int _filtvalue; // selected filter char *_preset_file; char *_prevvalue; // Returned filename char *_directory; char *_errmsg; // error message Fl_File_Chooser *file_chooser; int exist_dialog() { return(fl_choice("File exists. Are you sure you want to overwrite?", "Cancel", " OK ", NULL)); } void load_system_icons() { Fl_File_Icon::load_system_icons(); } int _nfilters; // Private methods void errmsg(const char *msg); int type_fl_file(int); void parse_filter(); void keeplocation(); public: Fl_Native_File_Chooser(int val=BROWSE_FILE); ~Fl_Native_File_Chooser(); // Public methods void type(int); int type() const; void options(int); int options() const; int count() const; const char *filename() const; const char *filename(int i) const; void directory(const char *val); const char *directory() const; void title(const char *); const char* title() const; const char *filter() const; void filter(const char *); int filters() const { return(_nfilters); } void filter_value(int i); int filter_value() const; void preset_file(const char*); const char* preset_file() const; const char *errmsg() const; int show(); }; itksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/Fl_Native_File_Chooser_MAC.H0000644000076500000240000001122211250506565026134 0ustar paulystaff// // Fl_Native_File_Chooser_MAC.H -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // OSX-SPECIFIC NATIVE BROWSER #ifdef __APPLE_CC__ #include #else #include #endif #include #define MAXFILTERS 80 class Fl_Native_File_Chooser { public: enum Type { BROWSE_FILE = 0, BROWSE_DIRECTORY, BROWSE_MULTI_FILE, BROWSE_MULTI_DIRECTORY, BROWSE_SAVE_FILE, BROWSE_SAVE_DIRECTORY }; enum Option { NO_OPTIONS = 0x0000, // no options enabled SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite // confirm dialog (if supported) NEW_FOLDER = 0x0002, // Show 'New Folder' icon // (if supported) PREVIEW = 0x0004, // enable preview mode }; protected: NavDialogCreationOptions _opts; // file navigation options private: int _btype; // kind-of browser to show() int _options; // general options NavDialogRef _ref; // file navigation reference NavActionState _keepstate; // various chooser options NavMenuItemSpec _tempitem; // Popup menu selection char **_pathnames; // array of pathnames int _tpathnames; // total pathnames char *_directory; // default pathname to use char *_title; // title for window char *_preset_file; // the 'save as' filename char *_filter; // user-side search filter, eg: // C Files\t*.[ch]\nText Files\t*.txt" char *_filt_names; // filter names (tab delimited) // eg. "C Files\tText Files" char *_filt_patt[MAXFILTERS]; // array of filter patterns, eg: // _filt_patt[0]="*.{cxx,h}" // _filt_patt[1]="*.txt" int _filt_total; // parse_filter() # of filters loaded int _filt_value; // index of the selected filter char *_errmsg; // error message // PRIVATE CLASS TO HANDLE NAVIGATION DIALOG REPLY STRUCT // Class-ified, mainly to ensure proper cleanup. // class NavReply { int _valid_reply; NavReplyRecord _reply; public: NavReply(); ~NavReply(); int get_reply(NavDialogRef& ref); int get_saveas_basename(char *s, int slen); int get_dirname(char *s, int slen); int get_pathnames(char **&pathnames, int& tpathnames); }; // Private methods void errmsg(const char *msg); void clear_pathnames(); void set_single_pathname(const char *s); int get_saveas_basename(NavDialogRef& ref); int get_pathnames(NavDialogRef& ref); static void event_handler(NavEventCallbackMessage callBackSelector, NavCBRecPtr cbparm, void *data); void clear_filters(); void add_filter(const char *, const char *); void parse_filter(const char *from); static Boolean filter_proc_cb(AEDesc *, void *, void *, NavFilterModes); Boolean filter_proc_cb2(AEDesc*, void*, void*, NavFilterModes); int post(); public: Fl_Native_File_Chooser(int val = BROWSE_FILE); ~Fl_Native_File_Chooser(); // Public methods void type(int); int type() const; void options(int); int options() const; int count() const; const char *filename() const; const char *filename(int i) const; void directory(const char *); const char *directory() const; void title(const char *); const char *title() const; const char *filter() const; void filter(const char *); void filter_value(int i) { _filt_value = i; } int filter_value() { return(_filt_value); } int filters() { return(_filt_total); } void preset_file(const char *); const char *preset_file(); const char *errmsg() const; int show(); }; itksnap/Utilities/FLTK/Fl_Native_File_Chooser/FL/Fl_Native_File_Chooser_WIN32.H0000644000076500000240000000706511226666572026360 0ustar paulystaff// // Fl_Native_File_Chooser_WINDOWS.H -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // April 2005 - API changes, improved filter processing by Nathan Vander Wilt // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // #define _WIN32_WINNT 0x0501 // needed for OPENFILENAME's 'FlagsEx' #include #include // malloc #include #include // OPENFILENAME, GetOpenFileName() #include // BROWSEINFO, SHBrowseForFolder() class Fl_Native_File_Chooser { public: enum Type { BROWSE_FILE = 0, BROWSE_DIRECTORY, BROWSE_MULTI_FILE, BROWSE_MULTI_DIRECTORY, BROWSE_SAVE_FILE, BROWSE_SAVE_DIRECTORY }; enum Option { NO_OPTIONS = 0x0000, // no options enabled SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite // confirm dialog (if supported) NEW_FOLDER = 0x0002, // Show 'New Folder' icon // (if supported) PREVIEW = 0x0004, // enable preview mode }; private: int _btype; // kind-of browser to show() int _options; // general options OPENFILENAME _ofn; // GetOpenFileName() & GetSaveFileName() struct BROWSEINFO _binf; // SHBrowseForFolder() struct char **_pathnames; // array of pathnames int _tpathnames; // total pathnames char *_directory; // default pathname to use char *_title; // title for window char *_filter; // user-side search filter char *_parsedfilt; // filter parsed for Windows dialog int _nfilters; // number of filters parse_filter counted char *_preset_file; // the file to preselect char *_errmsg; // error message // Private methods void errmsg(const char *msg); void clear_pathnames(); void set_single_pathname(const char *s); void add_pathname(const char *s); void FreePIDL(ITEMIDLIST *pidl); void ClearOFN(); void ClearBINF(); void Win2Unix(char *s); void Unix2Win(char *s); int showfile(); static int CALLBACK Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data); int showdir(); void parse_filter(const char *); void clear_filters(); void add_filter(const char *, const char *); public: Fl_Native_File_Chooser(int val = BROWSE_FILE); ~Fl_Native_File_Chooser(); // Public methods void type(int val); int type() const; void options(int); int options() const; int count() const; const char *filename() const; const char *filename(int i) const; void directory(const char *val); const char *directory() const; void title(const char *val); const char *title() const; const char *filter() const; void filter(const char *val); int filters() const { return _nfilters; } void filter_value(int i); int filter_value() const; void preset_file(const char *); const char *preset_file() const; const char *errmsg() const; int show(); }; itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Fl_Native_File_Chooser.cxx0000644000076500000240000000223111226666572025576 0ustar paulystaff// // Fl_Native_File_Chooser.cxx -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Use Windows' chooser #ifdef _WIN32 #include "Fl_Native_File_Chooser_WIN32.cxx" #endif // Use Apple's chooser #ifdef __APPLE__ #include "Fl_Native_File_Chooser_MAC.cxx" #endif // All else falls back to FLTK's own chooser #if ! defined(__APPLE__) && !defined(_WIN32) #include "Fl_Native_File_Chooser_FLTK.cxx" #endif itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Fl_Native_File_Chooser_FLTK.cxx0000644000076500000240000002341711226666572026427 0ustar paulystaff// // Fl_Native_File_Chooser_FLTK.cxx -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // API changes + filter improvements by Nathan Vander Wilt 2005 // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please keep code 80 column compliant. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // #ifdef FLTK1 // // FLTK1 // #include #define FNFC_CLASS Fl_Native_File_Chooser #define FNFC_CTOR Fl_Native_File_Chooser #define FLTK_CHOOSER_SINGLE Fl_File_Chooser::SINGLE #define FLTK_CHOOSER_DIRECTORY Fl_File_Chooser::DIRECTORY #define FLTK_CHOOSER_MULTI Fl_File_Chooser::MULTI #define FLTK_CHOOSER_CREATE Fl_File_Chooser::CREATE #else // // FLTK2 // #include #include #define FNFC_CTOR NativeFileChooser #define FNFC_CLASS fltk::FNFC_CTOR #define FLTK_CHOOSER_SINGLE fltk::FileChooser::SINGLE #define FLTK_CHOOSER_DIRECTORY fltk::FileChooser::DIRECTORY #define FLTK_CHOOSER_MULTI fltk::FileChooser::MULTI #define FLTK_CHOOSER_CREATE fltk::FileChooser::CREATE #endif #include "common.cxx" #include // CTOR FNFC_CLASS::FNFC_CTOR(int val) { static int init = 0; // 'first time' initialize flag if ( init == 0 ) { // Initialize when instanced for first time load_system_icons(); init = 1; } _btype = val; _options = NO_OPTIONS; _filter = NULL; _filtvalue = 0; _parsedfilt = NULL; _preset_file = NULL; _prevvalue = NULL; _directory = NULL; _errmsg = NULL; #ifdef FLTK1 file_chooser = new Fl_File_Chooser(NULL, NULL, 0, NULL); #else file_chooser = new fltk::FileChooser(NULL, NULL, 0, NULL); #endif type(val); // do this after file_chooser created _nfilters = 0; } // DTOR FNFC_CLASS::~FNFC_CTOR() { delete file_chooser; _filter = strfree(_filter); _parsedfilt = strfree(_parsedfilt); _preset_file = strfree(_preset_file); _prevvalue = strfree(_prevvalue); _directory = strfree(_directory); _errmsg = strfree(_errmsg); } // PRIVATE: SET ERROR MESSAGE void FNFC_CLASS::errmsg(const char *msg) { _errmsg = strfree(_errmsg); _errmsg = strnew(msg); } // PRIVATE: translate Native types to Fl_File_Chooser types int FNFC_CLASS::type_fl_file(int val) { switch (val) { case BROWSE_FILE: return(FLTK_CHOOSER_SINGLE); case BROWSE_DIRECTORY: return(FLTK_CHOOSER_SINGLE | FLTK_CHOOSER_DIRECTORY); case BROWSE_MULTI_FILE: return(FLTK_CHOOSER_MULTI); case BROWSE_MULTI_DIRECTORY: return(FLTK_CHOOSER_DIRECTORY | FLTK_CHOOSER_MULTI); case BROWSE_SAVE_FILE: return(FLTK_CHOOSER_SINGLE | FLTK_CHOOSER_CREATE); case BROWSE_SAVE_DIRECTORY: return(FLTK_CHOOSER_DIRECTORY | FLTK_CHOOSER_MULTI | FLTK_CHOOSER_CREATE); default: return(FLTK_CHOOSER_SINGLE); } } void FNFC_CLASS::type(int val) { _btype = val; file_chooser->type(type_fl_file(val)); } int FNFC_CLASS::type() const { return(_btype); } // SET OPTIONS void FNFC_CLASS::options(int val) { _options = val; } // GET OPTIONS int FNFC_CLASS::options() const { return(_options); } // Show chooser, blocks until done. // RETURNS: // 0 - user picked a file // 1 - user cancelled // -1 - failed; errmsg() has reason // int FNFC_CLASS::show() { // FILTER if ( _parsedfilt ) { file_chooser->filter(_parsedfilt); } // FILTER VALUE // Set this /after/ setting the filter // file_chooser->filter_value(_filtvalue); // DIRECTORY if ( _directory && _directory[0] ) { file_chooser->directory(_directory); } else { file_chooser->directory(_prevvalue); } // PRESET FILE if ( _preset_file ) { file_chooser->value(_preset_file); } // OPTIONS: PREVIEW file_chooser->preview( (options() & PREVIEW) ? 1 : 0); // OPTIONS: NEW FOLDER if ( options() & NEW_FOLDER ) file_chooser->type(file_chooser->type() | FLTK_CHOOSER_CREATE); // on // SHOW file_chooser->show(); #ifdef FLTK1 // FLTK1: BLOCK WHILE BROWSER SHOWN while ( file_chooser->shown() ) { Fl::wait(); } #else // FLTK2: BLOCK WHILE BROWSER SHOWN while ( file_chooser->visible() ) { fltk::wait(); } #endif if ( file_chooser->value() && file_chooser->value()[0] ) { _prevvalue = strfree(_prevvalue); _prevvalue = strnew(file_chooser->value()); _filtvalue = file_chooser->filter_value(); // update filter value // HANDLE SHOWING 'SaveAs' CONFIRM if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) { struct stat buf; if ( stat(file_chooser->value(), &buf) != -1 ) { if ( buf.st_mode & S_IFREG ) { // Regular file + exists? if ( exist_dialog() == 0 ) { return(1); } } } } } if ( file_chooser->count() ) return(0); else return(1); } // RETURN ERROR MESSAGE const char *FNFC_CLASS::errmsg() const { return(_errmsg ? _errmsg : "No error"); } // GET FILENAME const char* FNFC_CLASS::filename() const { if ( file_chooser->count() > 0 ) return(file_chooser->value()); return(""); } // GET FILENAME FROM LIST OF FILENAMES const char* FNFC_CLASS::filename(int i) const { if ( i < file_chooser->count() ) return(file_chooser->value(i+1)); // convert fltk 1 based to our 0 based return(""); } // SET TITLE // Can be NULL if no title desired. // void FNFC_CLASS::title(const char *val) { file_chooser->label(val); } // GET TITLE // Can return NULL if none set. // const char *FNFC_CLASS::title() const { return(file_chooser->label()); } // SET FILTER // Can be NULL if no filter needed // void FNFC_CLASS::filter(const char *val) { _filter = strfree(_filter); _filter = strnew(val); parse_filter(); } // GET FILTER const char *FNFC_CLASS::filter() const { return(_filter); } // SET SELECTED FILTER void FNFC_CLASS::filter_value(int val) { _filtvalue = val; } // RETURN SELECTED FILTER int FNFC_CLASS::filter_value() const { return(_filtvalue); } // GET TOTAL FILENAMES CHOSEN int FNFC_CLASS::count() const { return(file_chooser->count()); } // PRESET PATHNAME // Can be NULL if no preset is desired. // void FNFC_CLASS::directory(const char *val) { _directory = strfree(_directory); _directory = strnew(val); } // GET PRESET PATHNAME // Can return NULL if none set. // const char *FNFC_CLASS::directory() const { return(_directory); } // Convert our filter format to fltk's chooser format // FROM TO (FLTK) // ------------------------- -------------------------- // "*.cxx" "*.cxx Files(*.cxx)" // "C Files\t*.{cxx,h}" "C Files(*.{cxx,h})" // "C Files\t*.{cxx,h}\nText Files\t*.txt" "C Files(*.{cxx,h})\tText Files(*.txt)" // // Returns a modified version of the filter that the caller is responsible // for freeing with strfree(). // void FNFC_CLASS::parse_filter() { _parsedfilt = strfree(_parsedfilt); // clear previous parsed filter (if any) _nfilters = 0; char *in = _filter; if ( !in ) return; int has_name = strchr(in, '\t') ? 1 : 0; char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard char wildcard[1024] = ""; // parsed wildcard char name[1024] = ""; // Parse filter user specified for ( ; 1; in++ ) { /*** DEBUG printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n", *in, mode, name, wildcard); ***/ switch (*in) { // FINISHED PARSING NAME? case '\t': if ( mode != 'n' ) goto regchar; mode = 'w'; break; // ESCAPE NEXT CHAR case '\\': ++in; goto regchar; // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? case '\r': case '\n': case '\0': // APPEND NEW FILTER TO LIST if ( wildcard[0] ) { // OUT: "name(wild)\tname(wild)" char comp[2048]; sprintf(comp, "%s%.511s(%.511s)", ((_parsedfilt)?"\t":""), name, wildcard); _parsedfilt = strapp(_parsedfilt, comp); _nfilters++; //DEBUG printf("DEBUG: PARSED FILT NOW <%s>\n", _parsedfilt); } // RESET wildcard[0] = name[0] = '\0'; mode = strchr(in, '\t') ? 'n' : 'w'; // DONE? if ( *in == '\0' ) return; // done else continue; // not done yet, more filters // Parse all other chars default: // handle all non-special chars regchar: // handle regular char switch ( mode ) { case 'n': chrcat(name, *in); continue; case 'w': chrcat(wildcard, *in); continue; } break; } } //NOTREACHED } // SET PRESET FILENAME void FNFC_CLASS::preset_file(const char* val) { _preset_file = strfree(_preset_file); _preset_file = strnew(val); } // GET PRESET FILENAME const char* FNFC_CLASS::preset_file() const { return(_preset_file); } itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Fl_Native_File_Chooser_MAC.cxx0000644000076500000240000005612411255773107026263 0ustar paulystaff// // Fl_Native_File_Chooser_MAC.cxx -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // FLTK2/MAC port by Greg Ercolano 2007. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please keep code 80 column compliant. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // // TODO: // o When doing 'open file', only dir is preset, not filename. // Possibly 'preset_file' could be used to select the filename. // #include "common.cxx" // strnew/strfree/strapp/chrcat #include // dirname(3) #include // stat(2) #include // stat(2) #ifdef FLTK1 // // FLTK1 // #include #include #include #define FNFC_CLASS Fl_Native_File_Chooser #define FNFC_CTOR Fl_Native_File_Chooser #else // // FLTK2 // #include #include #include #define FNFC_CTOR NativeFileChooser #define FNFC_CLASS fltk::FNFC_CTOR #define fl_filename_match filename_match // fltk1 name -> fltk2 name #define fl_filename_isdir filename_isdir // fltk1 name -> fltk2 name #endif // TRY TO CONVERT AN AEDesc TO AN FSRef // As per Apple Technical Q&A QA1274 // eg: http://developer.apple.com/qa/qa2001/qa1274.html // Returns 'noErr' if OK, or an 'OSX result code' on error. // static int AEDescToFSRef(const AEDesc* desc, FSRef* fsref) { OSStatus err = noErr; AEDesc coerceDesc; // If AEDesc isn't already an FSRef, convert it to one if ( desc->descriptorType != typeFSRef ) { if ( ( err = AECoerceDesc(desc, typeFSRef, &coerceDesc) ) == noErr ) { // Get FSRef out of AEDesc err = AEGetDescData(&coerceDesc, fsref, sizeof(FSRef)); AEDisposeDesc(&coerceDesc); } } else { err = AEGetDescData(desc, fsref, sizeof(FSRef)); } return( err ); } // NAVREPLY: CTOR FNFC_CLASS::NavReply::NavReply() { _valid_reply = 0; } // NAVREPLY: DTOR FNFC_CLASS::NavReply::~NavReply() { if ( _valid_reply ) { NavDisposeReply(&_reply); } } // GET REPLY FROM THE NAV* DIALOG int FNFC_CLASS::NavReply::get_reply(NavDialogRef& ref) { if ( _valid_reply ) { NavDisposeReply(&_reply); // dispose of previous _valid_reply = 0; } if ( ref == NULL || NavDialogGetReply(ref, &_reply) != noErr ) { return(-1); } _valid_reply = 1; return(0); } // RETURN THE BASENAME USER WANTS TO 'Save As' int FNFC_CLASS::NavReply::get_saveas_basename(char *s, int slen) { if (CFStringGetCString(_reply.saveFileName, s, slen-1, kCFStringEncodingUTF8) == false) { s[0] = '\0'; return(-1); } return(0); } // RETURN THE DIRECTORY NAME // Returns 0 on success, -1 on error. // int FNFC_CLASS::NavReply::get_dirname(char *s, int slen) { FSRef fsref; if ( AEDescToFSRef(&_reply.selection, &fsref) != noErr ) { // Conversion failed? Return empty name s[0] = 0; return(-1); } FSRefMakePath(&fsref, (UInt8 *)s, slen); return(0); } // RETURN MULTIPLE DIRECTORIES // Returns: 0 on success with pathnames[] containing pathnames selected, // -1 on error // int FNFC_CLASS::NavReply::get_pathnames(char **&pathnames, int& tpathnames) { // How many items selected? long count = 0; if ( AECountItems(&_reply.selection, &count) != noErr ) { return(-1); } // Allocate space for that many pathnames pathnames = new char*[count]; memset((void*)pathnames, 0, count*sizeof(char*)); tpathnames = count; // Walk list of pathnames selected for (short index=1; index<=count; index++) { AEKeyword keyWord; AEDesc desc; if (AEGetNthDesc(&_reply.selection, index, typeFSRef, &keyWord, &desc) != noErr) { pathnames[index-1] = strnew(""); continue; } FSRef fsref; if (AEGetDescData(&desc, &fsref, sizeof(FSRef)) != noErr ) { pathnames[index-1] = strnew(""); continue; } char s[4096]; FSRefMakePath(&fsref, (UInt8 *)s, sizeof(s)-1); pathnames[index-1] = strnew(s); AEDisposeDesc(&desc); } return(0); } // FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS void FNFC_CLASS::clear_pathnames() { if ( _pathnames ) { while ( --_tpathnames >= 0 ) { _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]); } delete [] _pathnames; _pathnames = NULL; } _tpathnames = 0; } // SET A SINGLE PATHNAME void FNFC_CLASS::set_single_pathname(const char *s) { clear_pathnames(); _pathnames = new char*[1]; _pathnames[0] = strnew(s); _tpathnames = 1; } // GET THE 'Save As' FILENAME // Returns -1 on error, errmsg() has reason, filename == "". // 0 if OK, filename() has filename chosen. // int FNFC_CLASS::get_saveas_basename(NavDialogRef& ref) { if ( ref == NULL ) { errmsg("get_saveas_basename: ref is NULL"); return(-1); } NavReply reply; OSStatus err; if ((err = reply.get_reply(ref)) != noErr ) { errmsg("NavReply::get_reply() failed"); clear_pathnames(); return(-1); } char pathname[4096] = ""; // Directory name.. // -2 leaves room to append '/' // if ( reply.get_dirname(pathname, sizeof(pathname)-2) < 0 ) { clear_pathnames(); errmsg("NavReply::get_dirname() failed"); return(-1); } // Append '/' int len = strlen(pathname); pathname[len++] = '/'; pathname[len] = '\0'; // Basename.. if ( reply.get_saveas_basename(pathname+len, sizeof(pathname)-len) < 0 ) { clear_pathnames(); errmsg("NavReply::get_saveas_basename() failed"); return(-1); } set_single_pathname(pathname); return(0); } // GET (POTENTIALLY) MULTIPLE FILENAMES // Returns: // -1 -- error, errmsg() has reason, filename == "" // 0 -- OK, pathnames()/filename() has pathname(s) chosen // int FNFC_CLASS::get_pathnames(NavDialogRef& ref) { if ( ref == NULL ) { errmsg("get_saveas_basename: ref is NULL"); return(-1); } NavReply reply; OSStatus err; if ((err = reply.get_reply(ref)) != noErr ) { errmsg("NavReply::get_reply() failed"); clear_pathnames(); return(-1); } // First, clear pathnames array of any previous contents clear_pathnames(); if ( reply.get_pathnames(_pathnames, _tpathnames) < 0 ) { clear_pathnames(); errmsg("NavReply::get_dirname() failed"); return(-1); } return(0); } // IS PATHNAME A DIRECTORY? // 1 - path is a dir // 0 - path not a dir or error // static int IsDir(const char *pathname) { struct stat buf; if ( stat(pathname, &buf) != -1 ) { if ( buf.st_mode & S_IFDIR ) return(1); } return(0); } // PRESELECT PATHNAME IN BROWSER static void PreselectPathname(NavCBRecPtr cbparm, const char *path) { // XXX: path must be a dir, or kNavCtlSetLocation fails with -50. // Why, I don't know. Let me know with a bug report. -erco // if ( ! IsDir(path) ) { path = dirname(const_cast(path)); } OSStatus err; FSRef fsref; err = FSPathMakeRef((const UInt8*)path, &fsref, NULL); if ( err != noErr) { fprintf(stderr, "FSPathMakeRef(%s) failed: err=%d\n", path, (int)err); return; } AEDesc desc; err = AECreateDesc(typeFSRef, &fsref, sizeof(FSRef), &desc); if ( err != noErr) { fprintf(stderr, "AECreateDesc() failed: err=%d\n", (int)err); } err = NavCustomControl(cbparm->context, kNavCtlSetLocation, &desc); if ( err != noErr) { fprintf(stderr, "NavCustomControl() failed: err=%d\n", (int)err); } AEDisposeDesc(&desc); } // NAV CALLBACK EVENT HANDLER void FNFC_CLASS::event_handler(NavEventCallbackMessage callBackSelector, NavCBRecPtr cbparm, void *data) { FNFC_CLASS *nfb = (FNFC_CLASS*)data; switch (callBackSelector) { case kNavCBStart: { if ( nfb->directory() ) { // dir specified? PreselectPathname(cbparm, nfb->directory()); // use it first } else if ( nfb->preset_file() ) { // file specified? PreselectPathname(cbparm, nfb->preset_file()); // use if no dir } if ( nfb->_btype == BROWSE_SAVE_FILE && nfb->preset_file() ) { CFStringRef namestr = CFStringCreateWithCString(NULL, nfb->preset_file(), kCFStringEncodingUTF8); NavDialogSetSaveFileName(cbparm->context, namestr); CFRelease(namestr); } NavCustomControl(cbparm->context, kNavCtlSetActionState, &nfb->_keepstate); // Select the right filter in pop-up menu if ( nfb->_filt_value == nfb->_filt_total ) { // Select All Documents NavPopupMenuItem kAll = kNavAllFiles; NavCustomControl(cbparm->context, kNavCtlSelectAllType, &kAll); } else if (nfb->_filt_value < nfb->_filt_total) { // Select custom filter nfb->_tempitem.version = kNavMenuItemSpecVersion; nfb->_tempitem.menuCreator = 'extn'; nfb->_tempitem.menuType = nfb->_filt_value; *nfb->_tempitem.menuItemName = '\0'; // needed on 10.3+ NavCustomControl(cbparm->context, kNavCtlSelectCustomType, &(nfb->_tempitem)); } break; } case kNavCBPopupMenuSelect: { NavMenuItemSpecPtr ptr; // they really buried this one! ptr = (NavMenuItemSpecPtr)cbparm->eventData.eventDataParms.param; if ( ptr->menuCreator ) { // Gets index to filter ( menuCreator = 'extn' ) nfb->_filt_value = ptr->menuType; } else { // All docs filter selected ( menuCreator = '\0\0\0\0' ) nfb->_filt_value = nfb->_filt_total; } break; } case kNavCBSelectEntry: { NavActionState astate; switch ( nfb->_btype ) { // These don't need selection override case BROWSE_MULTI_FILE: case BROWSE_MULTI_DIRECTORY: case BROWSE_SAVE_FILE: { break; } // These need to allow only one item, so disable // Open button if user tries to select multiple files case BROWSE_SAVE_DIRECTORY: case BROWSE_DIRECTORY: case BROWSE_FILE: { long selectcount; AECountItems((AEDescList*)cbparm-> eventData.eventDataParms.param, &selectcount); if ( selectcount > 1 ) { NavCustomControl(cbparm->context, kNavCtlSetSelection, NULL); astate = nfb->_keepstate | kNavDontOpenState | kNavDontChooseState; NavCustomControl(cbparm->context, kNavCtlSetActionState, &astate ); } else { astate= nfb->_keepstate | kNavNormalState; NavCustomControl(cbparm->context, kNavCtlSetActionState, &astate ); } break; } } } break; } } // CONSTRUCTOR FNFC_CLASS::FNFC_CTOR(int val) { _btype = val; NavGetDefaultDialogCreationOptions(&_opts); _opts.optionFlags |= kNavDontConfirmReplacement; // no confirms for "save as" _options = NO_OPTIONS; _ref = NULL; memset(&_tempitem, 0, sizeof(_tempitem)); _pathnames = NULL; _tpathnames = 0; _title = NULL; _filter = NULL; _filt_names = NULL; memset(_filt_patt, 0, sizeof(char*) * MAXFILTERS); _filt_total = 0; _filt_value = 0; _directory = NULL; _preset_file = NULL; _errmsg = NULL; _keepstate = kNavNormalState; } // DESTRUCTOR FNFC_CLASS::~FNFC_CTOR() { // _opts // nothing to manage if (_ref) { NavDialogDispose(_ref); _ref = NULL; } // _options // nothing to manage // _keepstate // nothing to manage // _tempitem // nothing to manage clear_pathnames(); _directory = strfree(_directory); _title = strfree(_title); _preset_file = strfree(_preset_file); _filter = strfree(_filter); //_filt_names // managed by clear_filters() //_filt_patt[i] // managed by clear_filters() //_filt_total // managed by clear_filters() clear_filters(); //_filt_value // nothing to manage _errmsg = strfree(_errmsg); } // SET THE TYPE OF BROWSER void FNFC_CLASS::type(int val) { _btype = val; } // GET TYPE OF BROWSER int FNFC_CLASS::type() const { return(_btype); } // SET OPTIONS void FNFC_CLASS::options(int val) { _options = val; } // GET OPTIONS int FNFC_CLASS::options() const { return(_options); } // SHOW THE BROWSER WINDOW // Returns: // 0 - user picked a file // 1 - user cancelled // -1 - failed; errmsg() has reason // int FNFC_CLASS::show() { // Make sure fltk interface updates before posting our dialog #ifdef FLTK1 Fl::flush(); #else fltk::flush(); #endif // BROWSER TITLE CFStringRef cfs_title; cfs_title = CFStringCreateWithCString(NULL, _title ? _title : "No Title", kCFStringEncodingUTF8); _opts.windowTitle = cfs_title; _keepstate = kNavNormalState; // BROWSER FILTERS CFArrayRef filter_array = NULL; // One or more filters specified? if ( _filt_total ) { // NAMES -> CFArrayRef CFStringRef tab = CFSTR("\t"); CFStringRef tmp_cfs; tmp_cfs = CFStringCreateWithCString(NULL, _filt_names, kCFStringEncodingUTF8); filter_array = CFStringCreateArrayBySeparatingStrings(NULL, tmp_cfs, tab); CFRelease(tmp_cfs); CFRelease(tab); _opts.popupExtension = filter_array; _opts.optionFlags |= kNavAllFilesInPopup; } else { filter_array = NULL; _opts.popupExtension = NULL; _opts.optionFlags |= kNavAllFilesInPopup; } // HANDLE OPTIONS WE SUPPORT if ( _options & SAVEAS_CONFIRM ) { _opts.optionFlags &= ~kNavDontConfirmReplacement; // enables confirm } else { _opts.optionFlags |= kNavDontConfirmReplacement; // disables confirm } // POST BROWSER int err = post(); // RELEASE _FILT_ARR if ( filter_array ) CFRelease(filter_array); filter_array = NULL; _opts.popupExtension = NULL; _filt_total = 0; // RELEASE TITLE if ( cfs_title ) CFRelease(cfs_title); cfs_title = NULL; return(err); } // POST BROWSER // Internal use only. // Assumes '_opts' has been initialized. // // Returns: // 0 - user picked a file // 1 - user cancelled // -1 - failed; errmsg() has reason // int FNFC_CLASS::post() { // INITIALIZE BROWSER OSStatus err; if ( _filt_total == 0 ) { // Make sure they match _filt_value = 0; // TBD: move to someplace more logical? } if ( ! ( _options & NEW_FOLDER ) ) { _keepstate |= kNavDontNewFolderState; } switch (_btype) { case BROWSE_FILE: case BROWSE_MULTI_FILE: // Prompt user for one or more files if ((err = NavCreateGetFileDialog( &_opts, // options 0, // file types event_handler, // event handler 0, // preview callback filter_proc_cb, // filter callback (void*)this, // callback data &_ref)) != noErr ) { // dialog ref errmsg("NavCreateGetFileDialog: failed"); return(-1); } break; case BROWSE_DIRECTORY: case BROWSE_MULTI_DIRECTORY: case BROWSE_SAVE_DIRECTORY: // Prompts user for one or more files or folders if ((err = NavCreateChooseFolderDialog( &_opts, // options event_handler, // event callback 0, // filter callback (void*)this, // callback data &_ref)) != noErr ) { // dialog ref errmsg("NavCreateChooseFolderDialog: failed"); return(-1); } break; case BROWSE_SAVE_FILE: // Prompt user for filename to 'save as' if ((err = NavCreatePutFileDialog( &_opts, // options kNavGenericSignature, // file types 0, // file creator event_handler, // event handler (void*)this, // callback data &_ref)) != noErr ) { // dialog ref errmsg("NavCreatePutFileDialog: failed"); return(-1); } break; } // SHOW THE DIALOG if ( ( err = NavDialogRun(_ref) ) != 0 ) { char msg[80]; sprintf(msg, "NavDialogRun: failed (err=%d)", (int)err); errmsg(msg); return(-1); } // WHAT ACTION DID USER CHOOSE? NavUserAction act = NavDialogGetUserAction(_ref); if ( act == kNavUserActionNone ) { errmsg("Nothing happened yet (dialog still open)"); return(-1); } else if ( act == kNavUserActionCancel ) { // user chose 'cancel' return(1); } else if ( act == kNavUserActionSaveAs ) { // user chose 'save as' return(get_saveas_basename(_ref)); } // TOO MANY FILES CHOSEN? int ret = get_pathnames(_ref); if ( _btype == BROWSE_FILE && ret == 0 && _tpathnames != 1 ) { char msg[80]; sprintf(msg, "Expected only one file to be chosen.. you chose %d.", (int)_tpathnames); errmsg(msg); return(-1); } return(err); } // SET ERROR MESSAGE // Internal use only. // void FNFC_CLASS::errmsg(const char *msg) { _errmsg = strfree(_errmsg); _errmsg = strnew(msg); } // RETURN ERROR MESSAGE const char *FNFC_CLASS::errmsg() const { return(_errmsg ? _errmsg : "No error"); } // GET FILENAME const char* FNFC_CLASS::filename() const { if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]); return(""); } // GET FILENAME FROM LIST OF FILENAMES const char* FNFC_CLASS::filename(int i) const { if ( _pathnames && i < _tpathnames ) return(_pathnames[i]); return(""); } // GET TOTAL FILENAMES CHOSEN int FNFC_CLASS::count() const { return(_tpathnames); } // PRESET PATHNAME // Value can be NULL for none. // void FNFC_CLASS::directory(const char *val) { _directory = strfree(_directory); _directory = strnew(val); } // GET PRESET PATHNAME // Returned value can be NULL if none set. // const char* FNFC_CLASS::directory() const { return(_directory); } // SET TITLE // Value can be NULL if no title desired. // void FNFC_CLASS::title(const char *val) { _title = strfree(_title); _title = strnew(val); } // GET TITLE // Returned value can be NULL if none set. // const char *FNFC_CLASS::title() const { return(_title); } // SET FILTER // Can be NULL if no filter needed // void FNFC_CLASS::filter(const char *val) { _filter = strfree(_filter); _filter = strnew(val); // Parse filter user specified // IN: _filter = "C Files\t*.{cxx,h}\nText Files\t*.txt" // OUT: _filt_names = "C Files\tText Files" // _filt_patt[0] = "*.{cxx,h}" // _filt_patt[1] = "*.txt" // _filt_total = 2 // parse_filter(_filter); } // GET FILTER // Returned value can be NULL if none set. // const char *FNFC_CLASS::filter() const { return(_filter); } // CLEAR ALL FILTERS // Internal use only. // void FNFC_CLASS::clear_filters() { _filt_names = strfree(_filt_names); for (int i=0; i<_filt_total; i++) { _filt_patt[i] = strfree(_filt_patt[i]); } _filt_total = 0; } // PARSE USER'S FILTER SPEC // Parses user specified filter ('in'), // breaks out into _filt_patt[], _filt_names, and _filt_total. // // Handles: // IN: OUT:_filt_names OUT: _filt_patt // ------------------------------------ ------------------ --------------- // "*.{ma,mb}" "*.{ma,mb} Files" "*.{ma,mb}" // "*.[abc]" "*.[abc] Files" "*.[abc]" // "*.txt" "*.txt Files" "*.c" // "C Files\t*.[ch]" "C Files" "*.[ch]" // "C Files\t*.[ch]\nText Files\t*.cxx" "C Files" "*.[ch]" // // Parsing Mode: // IN:"C Files\t*.{cxx,h}" // ||||||| ||||||||| // mode: nnnnnnn wwwwwwwww // \_____/ \_______/ // Name Wildcard // void FNFC_CLASS::parse_filter(const char *in) { clear_filters(); if ( ! in ) return; int has_name = strchr(in, '\t') ? 1 : 0; char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard char wildcard[1024] = ""; // parsed wildcard char name[1024] = ""; // Parse filter user specified for ( ; 1; in++ ) { //// DEBUG //// printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n", //// *in, mode, name, wildcard); switch (*in) { // FINISHED PARSING NAME? case '\t': if ( mode != 'n' ) goto regchar; mode = 'w'; break; // ESCAPE NEXT CHAR case '\\': ++in; goto regchar; // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? case '\r': case '\n': case '\0': // TITLE // If user didn't specify a name, make one // if ( name[0] == '\0' ) { sprintf(name, "%.*s Files", (int)sizeof(name)-10, wildcard); } // APPEND NEW FILTER TO LIST if ( wildcard[0] ) { // Add to filtername list // Tab delimit if more than one. We later break // tab delimited string into CFArray with // CFStringCreateArrayBySeparatingStrings() // if ( _filt_total ) { _filt_names = strapp(_filt_names, "\t"); } _filt_names = strapp(_filt_names, name); // Add filter to the pattern array _filt_patt[_filt_total++] = strnew(wildcard); } // RESET wildcard[0] = name[0] = '\0'; mode = strchr(in, '\t') ? 'n' : 'w'; // DONE? if ( *in == '\0' ) return; // done else continue; // not done yet, more filters // Parse all other chars default: // handle all non-special chars regchar: // handle regular char switch ( mode ) { case 'n': chrcat(name, *in); continue; case 'w': chrcat(wildcard, *in); continue; } break; } } //NOTREACHED } // STATIC: FILTER CALLBACK Boolean FNFC_CLASS::filter_proc_cb(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) { return((FNFC_CLASS*)callBackUD)->filter_proc_cb2(theItem, info, callBackUD, filterMode); } // FILTER CALLBACK // Return true if match, // false if no match. // Boolean FNFC_CLASS::filter_proc_cb2(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode) { // All files chosen or no filters if ( _filt_value == _filt_total ) return(true); FSRef fsref; char pathname[4096]; // On fail, filter should return true by default if ( AEDescToFSRef(theItem, &fsref) != noErr ) { return(true); } FSRefMakePath(&fsref, (UInt8 *)pathname, sizeof(pathname)-1); if ( fl_filename_isdir(pathname) ) return(true); if ( fl_filename_match(pathname, _filt_patt[_filt_value]) ) return(true); else return(false); } // SET PRESET FILE // Value can be NULL for none. // void FNFC_CLASS::preset_file(const char* val) { _preset_file = strfree(_preset_file); _preset_file = strnew(val); } // PRESET FILE // Returned value can be NULL if none set. // const char* FNFC_CLASS::preset_file() { return(_preset_file); } itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Fl_Native_File_Chooser_WIN32.cxx0000644000076500000240000005414311250505506026453 0ustar paulystaff// // Fl_Native_File_Chooser_WIN32.cxx -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // API changes + filter improvements by Nathan Vander Wilt 2005 // FLTK2/WIN32 port by Greg Ercolano // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please keep code 80 column compliant. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // // Any application to multi-folder implementation: // http://www.codeproject.com/dialog/selectfolder.asp // #include // debugging #include "common.cxx" // strnew/strfree/strapp/chrcat #ifdef FLTK1 // // FLTK1 // #include #define FNFC_CLASS Fl_Native_File_Chooser #define FNFC_CTOR Fl_Native_File_Chooser #else // // FLTK2 // #include #include #define FNFC_CTOR NativeFileChooser #define FNFC_CLASS fltk::FNFC_CTOR #endif #define LCURLY_CHR '{' #define RCURLY_CHR '}' #define LBRACKET_CHR '[' #define RBRACKET_CHR ']' #define MAXFILTERS 80 // STATIC: PRINT WINDOWS 'DOUBLE NULL' STRING (DEBUG) static void dnullprint(char *wp) { if ( ! wp ) return; for ( int t=0; true; t++ ) { if ( wp[t] == '\0' && wp[t+1] == '\0' ) { printf("\\0\\0"); fflush(stdout); return; } else if ( wp[t] == '\0' ) { printf("\\0"); } else { printf("%c",wp[t]); } } } // RETURN LENGTH OF DOUBLENULL STRING // Includes single nulls in count, excludes trailing doublenull. // // 1234 567 // |||/\||| // IN: "one\0two\0\0" // OUT: 7 // static int dnulllen(const char *wp) { int len = 0; while ( ! ( *(wp+0) == 0 && *(wp+1) == 0 ) ) { ++wp; ++len; } return(len); } // STATIC: Append a string to another, leaving terminated with DOUBLE NULL. // Automatically handles extending length of string. // wp can be NULL (a new wp will be allocated and initialized). // string must be NULL terminated. // The pointer wp may be modified on return. // static void dnullcat(char*&wp, const char *string, int n = -1 ) { //DEBUG printf("DEBUG: dnullcat IN: <"); dnullprint(wp); printf(">\n"); int inlen = ( n < 0 ) ? strlen(string) : n; if ( ! wp ) { wp = new char[inlen + 4]; *(wp+0) = '\0'; *(wp+1) = '\0'; } else { int wplen = dnulllen(wp); // Make copy of wp into larger buffer char *tmp = new char[wplen + inlen + 4]; memcpy(tmp, wp, wplen+2); // copy of wp plus doublenull delete [] wp; // delete old wp wp = tmp; // use new copy //DEBUG printf("DEBUG: dnullcat COPY: <"); dnullprint(wp); printf("> (wplen=%d)\n", wplen); } // Find end of double null string // *wp2 is left pointing at second null. // char *wp2 = wp; if ( *(wp2+0) != '\0' && *(wp2+1) != '\0' ) { for ( ; 1; wp2++ ) if ( *(wp2+0) == '\0' && *(wp2+1) == '\0' ) { wp2++; break; } } if ( n == -1 ) n = strlen(string); strncpy(wp2, string, n); // Leave string double-null terminated *(wp2+n+0) = '\0'; *(wp2+n+1) = '\0'; //DEBUG printf("DEBUG: dnullcat OUT: <"); dnullprint(wp); printf(">\n\n"); } // CTOR FNFC_CLASS::FNFC_CTOR(int val) { _btype = val; _options = NO_OPTIONS; memset((void*)&_ofn, 0, sizeof(OPENFILENAME)); _ofn.lStructSize = sizeof(OPENFILENAME); _ofn.hwndOwner = NULL; memset((void*)&_binf, 0, sizeof(BROWSEINFO)); _pathnames = NULL; _tpathnames = 0; _directory = NULL; _title = NULL; _filter = NULL; _parsedfilt = NULL; _nfilters = 0; _preset_file = NULL; _errmsg = NULL; } // DTOR FNFC_CLASS::~FNFC_CTOR() { //_pathnames // managed by clear_pathnames() //_tpathnames // managed by clear_pathnames() _directory = strfree(_directory); _title = strfree(_title); _filter = strfree(_filter); //_parsedfilt // managed by clear_filters() //_nfilters // managed by clear_filters() _preset_file = strfree(_preset_file); _errmsg = strfree(_errmsg); clear_filters(); clear_pathnames(); ClearOFN(); ClearBINF(); } // SET TYPE OF BROWSER void FNFC_CLASS::type(int val) { _btype = val; } // GET TYPE OF BROWSER int FNFC_CLASS::type() const { return( _btype ); } // SET OPTIONS void FNFC_CLASS::options(int val) { _options = val; } // GET OPTIONS int FNFC_CLASS::options() const { return(_options); } // PRIVATE: SET ERROR MESSAGE void FNFC_CLASS::errmsg(const char *val) { _errmsg = strfree(_errmsg); _errmsg = strnew(val); } // FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS void FNFC_CLASS::clear_pathnames() { if ( _pathnames ) { while ( --_tpathnames >= 0 ) { _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]); } delete [] _pathnames; _pathnames = NULL; } _tpathnames = 0; } // SET A SINGLE PATHNAME void FNFC_CLASS::set_single_pathname(const char *s) { clear_pathnames(); _pathnames = new char*[1]; _pathnames[0] = strnew(s); _tpathnames = 1; } // ADD PATHNAME TO EXISTING ARRAY void FNFC_CLASS::add_pathname(const char *s) { if ( ! _pathnames ) { // Create first element in array ++_tpathnames; _pathnames = new char*[_tpathnames]; } else { // Grow array by 1 char **tmp = new char*[_tpathnames+1]; // create new buffer memcpy((void*)tmp, (void*)_pathnames, sizeof(char*)*_tpathnames); // copy old delete [] _pathnames; // delete old _pathnames = tmp; // use new ++_tpathnames; } _pathnames[_tpathnames-1] = strnew(s); } // FREE A PIDL (Pointer to IDentity List) void FNFC_CLASS::FreePIDL(ITEMIDLIST *pidl) { IMalloc *imalloc = NULL; if ( SUCCEEDED(SHGetMalloc(&imalloc)) ) { imalloc->Free(pidl); imalloc->Release(); imalloc = NULL; } } // CLEAR MICROSOFT OFN (OPEN FILE NAME) CLASS void FNFC_CLASS::ClearOFN() { // Free any previously allocated lpstrFile before zeroing out _ofn if ( _ofn.lpstrFile ) { _ofn.lpstrFile = strfree((char*)_ofn.lpstrFile); } if ( _ofn.lpstrInitialDir ) { _ofn.lpstrInitialDir = (LPCSTR)strfree((char*)_ofn.lpstrInitialDir); } _ofn.lpstrFilter = NULL; // (deleted elsewhere) int temp = _ofn.nFilterIndex; // keep the filter_value memset((void*)&_ofn, 0, sizeof(_ofn)); _ofn.lStructSize = sizeof(OPENFILENAME); _ofn.nFilterIndex = temp; } // CLEAR MICROSOFT BINF (BROWSER INFO) CLASS void FNFC_CLASS::ClearBINF() { if ( _binf.pidlRoot ) { FreePIDL((ITEMIDLIST*)_binf.pidlRoot); _binf.pidlRoot = NULL; } memset((void*)&_binf, 0, sizeof(_binf)); } // CONVERT WINDOWS BACKSLASHES TO UNIX FRONTSLASHES void FNFC_CLASS::Win2Unix(char *s) { for ( ; *s; s++ ) if ( *s == '\\' ) *s = '/'; } // CONVERT UNIX FRONTSLASHES TO WINDOWS BACKSLASHES void FNFC_CLASS::Unix2Win(char *s) { for ( ; *s; s++ ) if ( *s == '/' ) *s = '\\'; } // SHOW FILE BROWSER int FNFC_CLASS::showfile() { ClearOFN(); clear_pathnames(); size_t fsize = 2048; _ofn.Flags |= OFN_NOVALIDATE; // prevent disabling of front slashes _ofn.Flags |= OFN_HIDEREADONLY; // hide goofy readonly flag // USE NEW BROWSER _ofn.Flags |= OFN_EXPLORER; // use newer explorer windows _ofn.Flags |= OFN_ENABLESIZING; // allow window to be resized (hey, why not?) // XXX: The docs for OFN_NOCHANGEDIR says the flag is 'ineffective' on XP/2K/NT! // But let's set it anyway.. // _ofn.Flags |= OFN_NOCHANGEDIR; // prevent dialog for messing up the cwd switch ( _btype ) { case BROWSE_DIRECTORY: case BROWSE_MULTI_DIRECTORY: case BROWSE_SAVE_DIRECTORY: abort(); // never happens: handled by showdir() case BROWSE_FILE: fsize = 65536; // XXX: there must be a better way break; case BROWSE_MULTI_FILE: _ofn.Flags |= OFN_ALLOWMULTISELECT; fsize = 65536; // XXX: there must be a better way break; case BROWSE_SAVE_FILE: if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) { _ofn.Flags |= OFN_OVERWRITEPROMPT; } break; } // SPACE FOR RETURNED FILENAME _ofn.lpstrFile = new char[fsize]; _ofn.nMaxFile = fsize-1; _ofn.lpstrFile[0] = '\0'; _ofn.lpstrFile[1] = '\0'; // dnull // PARENT WINDOW _ofn.hwndOwner = GetForegroundWindow(); // DIALOG TITLE _ofn.lpstrTitle = _title ? _title : NULL; // FILTER _ofn.lpstrFilter = _parsedfilt ? _parsedfilt : NULL; // PRESET FILE // If set, supercedes _directory. See KB Q86920 for details // if ( _preset_file ) { size_t len = strlen(_preset_file); if ( len >= _ofn.nMaxFile ) { char msg[80]; sprintf(msg, "preset_file() filename is too long: %ld is >=%ld", (long)len, (long)fsize); return(-1); } strncpy(_ofn.lpstrFile, _preset_file, _ofn.nMaxFile); Unix2Win(_ofn.lpstrFile); _ofn.lpstrFile[len+0] = 0; // multiselect needs dnull _ofn.lpstrFile[len+1] = 0; } if ( _directory ) { // PRESET DIR // XXX: See KB Q86920 for doc bug: // http://support.microsoft.com/default.aspx?scid=kb;en-us;86920 // _ofn.lpstrInitialDir = strnew(_directory); Unix2Win((char*)_ofn.lpstrInitialDir); } // SAVE THE CURRENT DIRECTORY // XXX: Save the cwd because GetOpenFileName() is probably going to // change it, in spite of the OFN_NOCHANGEDIR flag, due to its docs // saying the flag is 'ineffective'. %^( // char oldcwd[MAX_PATH]; GetCurrentDirectory(MAX_PATH, oldcwd); oldcwd[MAX_PATH-1] = '\0'; // OPEN THE DIALOG WINDOW int err; if ( _btype == BROWSE_SAVE_FILE ) { err = GetSaveFileName(&_ofn); } else { err = GetOpenFileName(&_ofn); } if ( err == 0 ) { // EXTENDED ERROR CHECK int err = CommDlgExtendedError(); // CANCEL? if ( err == 0 ) return(1); // user hit 'cancel' // AN ERROR OCCURRED char msg[80]; sprintf(msg, "CommDlgExtendedError() code=%d", err); errmsg(msg); // XXX: RESTORE CWD if ( oldcwd[0] ) SetCurrentDirectory(oldcwd); return(-1); } // XXX: RESTORE CWD if ( oldcwd[0] ) { SetCurrentDirectory(oldcwd); } // PREPARE PATHNAMES FOR RETURN switch ( _btype ) { case BROWSE_FILE: case BROWSE_SAVE_FILE: set_single_pathname(_ofn.lpstrFile); Win2Unix(_pathnames[_tpathnames-1]); break; case BROWSE_MULTI_FILE: { // EXTRACT MULTIPLE FILENAMES const char *dirname = _ofn.lpstrFile; int dirlen = strlen(dirname); if ( dirlen > 0 ) { // WALK STRING SEARCHING FOR 'DOUBLE-NULL' // eg. "/dir/name\0foo1\0foo2\0foo3\0\0" // char pathname[2048]; for ( const char *s = _ofn.lpstrFile + dirlen + 1; *s; s+= (strlen(s)+1)) { strcpy(pathname, dirname); strcat(pathname, "\\"); strcat(pathname, s); add_pathname(pathname); Win2Unix(_pathnames[_tpathnames-1]); } } // XXX // Work around problem where pasted forward-slash pathname // into the file browser causes new "Explorer" interface // not to grok forward slashes, passing back as a 'filename'..! // if ( _tpathnames == 0 ) { add_pathname(dirname); Win2Unix(_pathnames[_tpathnames-1]); } break; } case BROWSE_DIRECTORY: case BROWSE_MULTI_DIRECTORY: case BROWSE_SAVE_DIRECTORY: abort(); // never happens: handled by showdir() } return(0); } // Used by SHBrowseForFolder(), sets initial selected dir. // Ref: Usenet: microsoft.public.vc.mfc, Dec 8 2000, 1:38p David Lowndes // Subject: How to specify to select an initial folder .." // int CALLBACK FNFC_CLASS::Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data) { switch (msg) { case BFFM_INITIALIZED: if (data) ::SendMessage(win, BFFM_SETSELECTION, TRUE, data); break; case BFFM_SELCHANGED: TCHAR path[MAX_PATH]; if ( SHGetPathFromIDList((ITEMIDLIST*)param, path) ) { ::SendMessage(win, BFFM_ENABLEOK, 0, 1); } else { //disable ok button if not a path ::SendMessage(win, BFFM_ENABLEOK, 0, 0); } break; case BFFM_VALIDATEFAILED: // we could pop up an annoying message here. // also needs set ulFlags |= BIF_VALIDATE break; default: break; } return(0); } // SHOW DIRECTORY BROWSER int FNFC_CLASS::showdir() { OleInitialize(NULL); // init needed by BIF_USENEWUI ClearBINF(); clear_pathnames(); // PARENT WINDOW _binf.hwndOwner = GetForegroundWindow(); // DIALOG TITLE _binf.lpszTitle = _title ? _title : NULL; // FLAGS _binf.ulFlags = 0; // initialize // TBD: make sure matches to runtime system, if need be. //( what if _WIN32_IE doesn't match system? does the program not run? ) // // TBD: match all 3 types of directories // // NOTE: *Don't* use BIF_SHAREABLE. It /disables/ mapped network shares // from being visible in BROWSE_DIRECTORY mode. // See Walter Garm's comments in ./TODO. #if defined(BIF_NONEWFOLDERBUTTON) // Version 6.0 if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_NONEWFOLDERBUTTON; _binf.ulFlags |= BIF_USENEWUI | BIF_RETURNONLYFSDIRS; #elif defined(BIF_USENEWUI) // Version 5.0 if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_EDITBOX; else if ( _btype == BROWSE_SAVE_DIRECTORY ) _binf.ulFlags |= BIF_USENEWUI; _binf.ulFlags |= BIF_RETURNONLYFSDIRS; #elif defined(BIF_EDITBOX) // Version 4.71 _binf.ulFlags |= BIF_RETURNONLYFSDIRS | BIF_EDITBOX; #else // Version Old _binf.ulFlags |= BIF_RETURNONLYFSDIRS; #endif // BUFFER char displayname[MAX_PATH]; _binf.pszDisplayName = displayname; // PRESET DIR char presetname[MAX_PATH]; if ( _directory ) { strcpy(presetname, _directory); Unix2Win(presetname); _binf.lParam = (LPARAM)presetname; } else _binf.lParam = 0; _binf.lpfn = Dir_CB; // OPEN BROWSER ITEMIDLIST *pidl = SHBrowseForFolder(&_binf); // CANCEL? if ( pidl == NULL ) return(1); // GET THE PATHNAME(S) THE USER SELECTED // TBD: expand NetHood shortcuts from this PIDL?? // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shbrowseforfolder.asp TCHAR path[MAX_PATH]; if ( SHGetPathFromIDList(pidl, path) ) { Win2Unix(path); add_pathname(path); } FreePIDL(pidl); if ( !strlen(path) ) return(1); // don't return empty pathnames return(0); } // RETURNS: // 0 - user picked a file // 1 - user cancelled // -1 - failed; errmsg() has reason // int FNFC_CLASS::show() { if ( _btype == BROWSE_DIRECTORY || _btype == BROWSE_MULTI_DIRECTORY || _btype == BROWSE_SAVE_DIRECTORY ) { return(showdir()); } else { return(showfile()); } } // RETURN ERROR MESSAGE const char *FNFC_CLASS::errmsg() const { return(_errmsg ? _errmsg : "No error"); } // GET FILENAME const char* FNFC_CLASS::filename() const { if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]); return(""); } // GET FILENAME FROM LIST OF FILENAMES const char* FNFC_CLASS::filename(int i) const { if ( _pathnames && i < _tpathnames ) return(_pathnames[i]); return(""); } // GET TOTAL FILENAMES CHOSEN int FNFC_CLASS::count() const { return(_tpathnames); } // PRESET PATHNAME // Can be NULL if no preset is desired. // void FNFC_CLASS::directory(const char *val) { _directory = strfree(_directory); _directory = strnew(val); } // GET PRESET PATHNAME // Can return NULL if none set. // const char *FNFC_CLASS::directory() const { return(_directory); } // SET TITLE // Can be NULL if no title desired. // void FNFC_CLASS::title(const char *val) { _title = strfree(_title); _title = strnew(val); } // GET TITLE // Can return NULL if none set. // const char *FNFC_CLASS::title() const { return(_title); } // SET FILTER // Can be NULL if no filter needed // void FNFC_CLASS::filter(const char *val) { _filter = strfree(_filter); clear_filters(); if ( val ) { _filter = strnew(val); parse_filter(_filter); } add_filter("All Files", "*.*"); // always include 'all files' option #ifdef DEBUG nullprint(_parsedfilt); #endif /*DEBUG*/ } // GET FILTER // Can return NULL if none set. // const char *FNFC_CLASS::filter() const { return(_filter); } // CLEAR FILTERS void FNFC_CLASS::clear_filters() { _nfilters = 0; _parsedfilt = strfree(_parsedfilt); } // ADD A FILTER void FNFC_CLASS::add_filter( const char *name_in, // name of filter (optional: can be null) const char *winfilter // windows style filter (eg. "*.cxx;*.h") ) { // No name? Make one.. char name[1024]; if ( !name_in || name_in[0] == '\0' ) { sprintf(name, "%.*s Files", sizeof(name)-10, winfilter); } else { sprintf(name, "%.*s", sizeof(name)-10, name_in); } dnullcat(_parsedfilt, name); dnullcat(_parsedfilt, winfilter); _nfilters++; //DEBUG printf("DEBUG: ADD FILTER name=<%s> winfilter=<%s>\n", name, winfilter); } // CONVERT FLTK STYLE PATTERN MATCHES TO WINDOWS 'DOUBLENULL' PATTERN // Handles: // IN OUT // ----------- ----------------------------- // *.{ma,mb} "*.{ma,mb} Files\0*.ma;*.mb\0\0" // *.[abc] "*.[abc] Files\0*.a;*.b;*.c\0\0" // *.txt "*.txt Files\0*.txt\0\0" // C Files\t*.[ch] "C Files\0*.c;*.h\0\0" // // Example: // IN: "*.{ma,mb}" // OUT: "*.ma;*.mb Files\0*.ma;*.mb\0All Files\0*.*\0\0" // --------------- --------- --------- --- // | | | | // Title Wildcards Title Wildcards // // Parsing Mode: // IN:"C Files\t*.{cxx,h}" // ||||||| ||||||||| // mode: nnnnnnn ww{{{{{{{ // \_____/ \_______/ // Name Wildcard // void FNFC_CLASS::parse_filter(const char *in) { clear_filters(); if ( ! in ) return; int has_name = strchr(in, '\t') ? 1 : 0; char mode = has_name ? 'n' : 'w'; // parse mode: n=name, w=wildcard int nwildcards = 0; char wildcards[MAXFILTERS][1024]; // parsed wildcards (can be several) char wildprefix[512] = ""; char name[512] = ""; // Init int t; for ( t=0; t name=<%s> wildprefix=<%s> nwildcards=%d wildcards[n]=<%s>\n", //// *in, mode, name, wildprefix, nwildcards, wildcards[nwildcards]); switch (*in) { case ',': case '|': if ( mode == LCURLY_CHR ) { // create new wildcard, copy in prefix strcat(wildcards[nwildcards++], wildprefix); continue; } else { goto regchar; } continue; // FINISHED PARSING A NAME? case '\t': if ( mode != 'n' ) goto regchar; // finish parsing name? switch to wildcard mode mode = 'w'; break; // ESCAPE NEXT CHAR case '\\': ++in; goto regchar; // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? case '\r': case '\n': case '\0': { if ( mode == 'w' ) { // finished parsing wildcard? if ( nwildcards == 0 ) { strcpy(wildcards[nwildcards++], wildprefix); } // Append wildcards in Microsoft's "*.one;*.two" format char comp[4096] = ""; for ( t=0; t 0 ) { chrcat(wildcards[nwildcards-1], *in); } continue; case 'n': chrcat(name, *in); continue; case 'w': chrcat(wildprefix, *in); for ( t=0; t #endif // Use Apple's chooser #ifdef __APPLE__ #include #endif // All else falls back to FLTK's own chooser #if ! defined(__APPLE__) && !defined(_WIN32) #include #endif #endif /*FLTK_NATIVE_FILE_CHOOSER_H*/ itksnap/Utilities/FLTK/Fl_Native_File_Chooser/fltk/NativeFileChooser_FLTK.h0000644000076500000240000000563511226666572026077 0ustar paulystaff// // NativeFileChooser_FLTK.h -- FLTK native OS file chooser widget // // Copyright 2004 by Nathan Vander Wilt // Port to FLTK2 by Frederic Hoerni and Greg Ercolano 2007 // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // #include #include namespace fltk { class NativeFileChooser { public: enum Type { BROWSE_FILE = 0, BROWSE_DIRECTORY, BROWSE_MULTI_FILE, BROWSE_MULTI_DIRECTORY, BROWSE_SAVE_FILE, BROWSE_SAVE_DIRECTORY }; enum Option { NO_OPTIONS = 0x0000, // no options enabled SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite // confirm dialog (if supported) NEW_FOLDER = 0x0002, // Show 'New Folder' icon // (if supported) PREVIEW = 0x0004 // enable preview mode }; private: int _btype; // kind-of browser to show() int _options; // general options char *_filter; // user supplied filter char *_parsedfilt; // parsed filter int _filtvalue; // selected filter char *_preset_file; char *_prevvalue; // Returned filename char *_directory; char *_errmsg; // error message fltk::FileChooser *file_chooser; int exist_dialog() { return(fltk::choice("File exists. Are you sure you want to overwrite?", "Cancel", " OK ", NULL)); } void load_system_icons() { fltk::FileIcon::load_system_icons(); } int _nfilters; // Private methods void errmsg(const char *msg); int type_fl_file(int); void parse_filter(); void keeplocation(); public: NativeFileChooser(int val=BROWSE_FILE); ~NativeFileChooser(); // Public methods void type(int); int type() const; void options(int); int options() const; int count() const; const char *filename() const; const char *filename(int i) const; void directory(const char *val); const char *directory() const; void title(const char *); const char* title() const; const char *filter() const; void filter(const char *); int filters() const { return(_nfilters); } void filter_value(int i); int filter_value() const; void preset_file(const char*); const char* preset_file() const; const char *errmsg() const; int show(); }; } // namespace fltk itksnap/Utilities/FLTK/Fl_Native_File_Chooser/fltk/NativeFileChooser_MAC.h0000644000076500000240000001123711226666572025732 0ustar paulystaff// // NativeFileChooser_MAC.H -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // FLTK2 port by Greg Ercolano 2007 // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // OSX-SPECIFIC NATIVE BROWSER #ifdef __APPLE_CC__ #include #else #include #endif #define MAXFILTERS 80 namespace fltk { class NativeFileChooser { public: enum Type { BROWSE_FILE = 0, BROWSE_DIRECTORY, BROWSE_MULTI_FILE, BROWSE_MULTI_DIRECTORY, BROWSE_SAVE_FILE, BROWSE_SAVE_DIRECTORY }; enum Option { NO_OPTIONS = 0x0000, // no options enabled SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite // confirm dialog (if supported) NEW_FOLDER = 0x0002, // Show 'New Folder' icon // (if supported) PREVIEW = 0x0004, // enable preview mode }; protected: NavDialogCreationOptions _opts; // file navigation options private: int _btype; // kind-of browser to show() int _options; // general options NavDialogRef _ref; // file navigation reference NavActionState _keepstate; // holds button permissions NavMenuItemSpec _tempitem; // Popup menu selection char **_pathnames; // array of pathnames int _tpathnames; // total pathnames char *_directory; // default pathname to use char *_title; // title for window char *_preset_file; // the 'save as' filename char *_filter; // user-side search filter, eg: // C Files\t*.[ch]\nText Files\t*.txt" char *_filt_names; // filter names (tab delimited) // eg. "C Files\tText Files" char *_filt_patt[MAXFILTERS]; // array of filter patterns, eg: // _filt_patt[0]="*.{cxx,h}" // _filt_patt[1]="*.txt" int _filt_total; // parse_filter() # of filters loaded int _filt_value; // index of the selected filter char *_errmsg; // error message // PRIVATE CLASS TO HANDLE NAVIGATION DIALOG REPLY STRUCT // Class-ified, mainly to ensure proper cleanup. // class NavReply { int _valid_reply; NavReplyRecord _reply; public: NavReply(); ~NavReply(); int get_reply(NavDialogRef& ref); int get_saveas_basename(char *s, int slen); int get_dirname(char *s, int slen); int get_pathnames(char **&pathnames, int& tpathnames); }; // Private methods void errmsg(const char *msg); void clear_pathnames(); void set_single_pathname(const char *s); int get_saveas_basename(NavDialogRef& ref); int get_pathnames(NavDialogRef& ref); static void event_handler(NavEventCallbackMessage callBackSelector, NavCBRecPtr cbparm, void *data); void clear_filters(); void add_filter(const char *, const char *); void parse_filter(const char *from); static Boolean filter_proc_cb(AEDesc *, void *, void *, NavFilterModes); Boolean filter_proc_cb2(AEDesc*, void*, void*, NavFilterModes); int post(); public: NativeFileChooser(int val = BROWSE_FILE); ~NativeFileChooser(); // Public methods void type(int); int type() const; void options(int); int options() const; int count() const; const char *filename() const; const char *filename(int i) const; void directory(const char *); const char *directory() const; void title(const char *); const char *title() const; const char *filter() const; void filter(const char *); void filter_value(int i) { _filt_value = i; } int filter_value() { return(_filt_value); } int filters() { return(_filt_total); } void preset_file(const char *); const char *preset_file(); const char *errmsg() const; int show(); }; } itksnap/Utilities/FLTK/Fl_Native_File_Chooser/fltk/NativeFileChooser_WIN32.h0000644000076500000240000000713711226666572026140 0ustar paulystaff// // NativeFileChooser_WINDOWS.h -- FLTK native OS file chooser widget // // Copyright 2004 by Greg Ercolano. // API changes + filter improvements by Nathan Vander Wilt 2005 // FLTK2 port by Greg Ercolano 2007 // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // #define _WIN32_WINNT 0x0501 // needed for OPENFILENAME's 'FlagsEx' #include #include // malloc #include #include // OPENFILENAME, GetOpenFileName() #include // BROWSEINFO, SHBrowseForFolder() namespace fltk { class NativeFileChooser { public: enum Type { BROWSE_FILE = 0, BROWSE_DIRECTORY, BROWSE_MULTI_FILE, BROWSE_MULTI_DIRECTORY, BROWSE_SAVE_FILE, BROWSE_SAVE_DIRECTORY }; enum Option { NO_OPTIONS = 0x0000, // no options enabled SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite // confirm dialog (if supported) NEW_FOLDER = 0x0002, // Show 'New Folder' icon // (if supported) PREVIEW = 0x0004, // enable preview mode }; private: int _btype; // kind-of browser to show() int _options; // general options OPENFILENAME _ofn; // GetOpenFileName() & GetSaveFileName() struct BROWSEINFO _binf; // SHBrowseForFolder() struct char **_pathnames; // array of pathnames int _tpathnames; // total pathnames char *_directory; // default pathname to use char *_title; // title for window char *_filter; // user-side search filter char *_parsedfilt; // filter parsed for Windows dialog int _nfilters; // number of filters parse_filter counted char *_preset_file; // the file to preselect char *_errmsg; // error message // Private methods void errmsg(const char *msg); void clear_pathnames(); void set_single_pathname(const char *s); void add_pathname(const char *s); void FreePIDL(ITEMIDLIST *pidl); void ClearOFN(); void ClearBINF(); void Win2Unix(char *s); void Unix2Win(char *s); int showfile(); static int CALLBACK Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data); int showdir(); void parse_filter(const char *); void clear_filters(); void add_filter(const char *, const char *); public: NativeFileChooser(int val = BROWSE_FILE); ~NativeFileChooser(); // Public methods void type(int val); int type() const; void options(int); int options() const; int count() const; const char *filename() const; const char *filename(int i) const; void directory(const char *val); const char *directory() const; void title(const char *val); const char *title() const; const char *filter() const; void filter(const char *val); int filters() const { return _nfilters; } void filter_value(int i); int filter_value() const; void preset_file(const char *); const char *preset_file() const; const char *errmsg() const; int show(); }; } // namespace fltk itksnap/Utilities/FLTK/Fl_Native_File_Chooser/make.bat0000644000076500000240000000012011226666572022162 0ustar paulystaff@set PATH=C:\BIN;%PATH% @gmake -f Makefile.MICROSOFT %1 %2 %3 %4 %5 %6 %7 %8 %9 itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Makefile0000644000076500000240000000326111250505506022210 0ustar paulystaff# # Fl_Native_File_Chooser -- Makefile # # UNCOMMENT FOR FLTK1 BUILD FLTKCONFIG=fltk-config # UNCOMMENT FOR FLTK2 BUILD FLTK2CONFIG=fltk2-config ### REMOVE:END # Build Everything all: FORCE ifneq ($(FLTKCONFIG),) @echo "***"; echo "*** FLTK1 BUILD"; echo "***" ( make -f Makefile-fltk1 all "FLTKCONFIG=$(FLTKCONFIG)") endif ifneq ($(FLTK2CONFIG),) @echo "***"; echo "*** FLTK2 BUILD"; echo "***" ( make -f Makefile-fltk2 all "FLTK2CONFIG=$(FLTK2CONFIG)") endif # Libs Fl_Native_File_Chooser.o: FORCE ( make -f Makefile-fltk1 $@ "FLTKCONFIG=$(FLTKCONFIG)") NativeFileChooser.o: FORCE ( make -f Makefile-fltk2 $@ "FLTK2CONFIG=$(FLTK2CONFIG)") # Clean build clean: FORCE # BINARIES -rm -f *.o 2> /dev/null -rm -f *.obj 2> /dev/null -rm -f *.exe 2> /dev/null -rm -f test-browser 2> /dev/null -rm -f test-browser-fltk2 2> /dev/null -rm -f simple-app 2> /dev/null -rm -f simple-app-fltk2 2> /dev/null # EDITOR CRAP -rm -f *~ 2> /dev/null -rm -f .*.swp 2> /dev/null # WINDOWS CRAP -rm -rf ii_files 2> /dev/null -rm -f *.{ilk,pdb,sln,suo} 2> /dev/null -rm -f *.{manifest,idb} 2> /dev/null # UNIX CRAP -rm -f core 2> /dev/null -rm -f core.* 2> /dev/null -rm -f .nfs* 2> /dev/null -rm -f .gdb_history 2> /dev/null # MAC CRAP -rm -rf .DS_Store 2> /dev/null -rm -f ._test-browser* 2> /dev/null -rm -f ._simple-app* 2> /dev/null -rm -f *.app/Contents/MacOS/* # Create distribution tar file (for maintainer only) tar: clean if grep -q ERCODEBUG *.cxx FL/*.H ; then \ echo '### NO DEBUG CODE IN RELEASE'; \ exit 1; \ fi sh .TAR_RELEASE.sh # FORCE TARGET -- Do not remove FORCE: itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Makefile-fltk10000644000076500000240000000522311226666572023245 0ustar paulystaff# Makefile that uses fltkconfig # Submitted by Ian MacArthur. # NOTE: This makes use of new features in fltk-config 1.1.6: --cxx and --cc flags. ######################################################### # Assumes FLTKCONFIG has been set by main Makefile # flags for compiler: CFLAGS = -Wall -O3 -I. $(shell $(FLTKCONFIG) --cflags) -DFLTK1 CXXFLAGS = -Wall -O3 -I. $(shell $(FLTKCONFIG) --cxxflags) -DFLTK1 # Possible steps after linking... STRIP = strip POSTBUILD = $(FLTKCONFIG) --post # libraries to link with: LDLIBS = -lm LINKFLTK = $(shell $(FLTKCONFIG) --ldstaticflags) IMGLIB = $(shell $(FLTKCONFIG) --use-images --ldstaticflags) GL_LIB = $(shell $(FLTKCONFIG) --use-gl --ldstaticflags) # Other general settings CXX = $(shell $(FLTKCONFIG) --cxx) CC = $(shell $(FLTKCONFIG) --cc) # now we can make specific modifications based on the operating system and host UNAME := $(shell uname) NATIVESRCS=Fl_Native_File_Chooser.cxx \ Fl_Native_File_Chooser_WIN32.cxx \ Fl_Native_File_Chooser_MAC.cxx \ Fl_Native_File_Chooser_FLTK.cxx \ FL/Fl_Native_File_Chooser.H \ FL/Fl_Native_File_Chooser_WIN32.H \ FL/Fl_Native_File_Chooser_MAC.H \ FL/Fl_Native_File_Chooser_FLTK.H ifeq '$(OS)' "Windows_NT" EXE = .exe endif # end of WIN32 options ifeq ($(strip $(UNAME)),Linux) EXE = endif # end of linux options ifeq ($(strip $(UNAME)),Darwin) EXE = LDLIBS += -framework CoreFoundation endif # end of OSX options #.SILENT: # Make the build run quietly all: test-browser$(EXE) simple-app$(EXE) ######################################################### # make sure the basic rules are in place, just in case... # Build commands and filename extensions... .SUFFIXES: .c .cxx .cpp .cc .h .fl .o # Rule to build an object file from a C++ source file %.o : %.cxx @echo Compile $@... $(CXX) -c -o $@ $< $(CXXFLAGS) # Rule to build an object file from a C source file %.o : %.c @echo Compile $@... $(CC) -c -o $@ $< $(CFLAGS) ######################################################### # define rules for the known targets... Fl_Native_File_Chooser.o: $(NATIVESRCS) $(CXX) $(CXXFLAGS) $< -c test-browser.o: test-browser.cxx $(CXX) $(CXXFLAGS) $< -c test-browser$(EXE): test-browser.o Fl_Native_File_Chooser.o $(CXX) test-browser.o Fl_Native_File_Chooser.o $(IMGLIB) $(LDLIBS) -o $@ $(STRIP) $@ $(POSTBUILD) test-browser$(EXE) simple-app.o: simple-app.cxx $(CXX) $(CXXFLAGS) $< -c simple-app$(EXE): simple-app.o Fl_Native_File_Chooser.o $(CXX) simple-app.o Fl_Native_File_Chooser.o $(IMGLIB) $(LDLIBS) -o $@ $(STRIP) $@ $(POSTBUILD) simple-app$(EXE) # FORCE TARGET -- Do not remove FORCE: itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Makefile-fltk1.MICROSOFT0000644000076500000240000000401611250505506024572 0ustar paulystaff### ### FLTK1 MAKEFILE FOR MICROSOFT VS EXPRESS 8 ### This is Microsoft's native *free* compiler. ### # Assumes FLTKDIR has been set by main Makefile.MICROSOFT # SUBSYSTEM # "console" to see stdout/stderr, # "windows" to make regular windows app. # SUBSYSTEM=console # CHANGE THESE FLAGS AS NEEDED FOR YOUR COMPILER # If unsure what to set these to, compile one of the FLTK test programs, # and match these flags to those shown in the FLTK program's build log. # # The default settings shown here are for "Visual Studio Express 8" # # /MD = DYNAMIC: Multithreaded DLL; MSVCRT.DLL needed at runtime # /MT = STATIC: Multithreaded; MSVCRT.LIB is used; no DLL needed at runtime # /Wall = add for WAAAY too much info. Useful only if you grep out OS's errors! # /W3 = somewhat verbose # CC=cl /MD /EHsc /DVS2000 /DVISUAL_STUDIO /D_CRT_SECURE_NO_DEPRECATE /D_WIN32 CFLAGS= /I$(FLTKDIR) /I. /DFLTK1 LIBS=/link $(FLTKDIR)/lib/fltk.lib \ wsock32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib \ comdlg32.lib advapi32.lib shell32.lib winmm.lib ole32.lib oleaut32.lib \ uuid.lib imm32.lib /nologo /machine:I386 NATIVESRCS=Fl_Native_File_Chooser.cxx \ Fl_Native_File_Chooser_WIN32.cxx \ FL/Fl_Native_File_Chooser.H \ FL/Fl_Native_File_Chooser_WIN32.H default: test-browser simple-app Fl_Native_File_Chooser.obj: $(NATIVESRCS) $(CC) $(CFLAGS) /Tp Fl_Native_File_Chooser.cxx /c test-browser: test-browser.cxx Fl_Native_File_Chooser.obj $(CC) $(CFLAGS) /Tp test-browser.cxx /c $(CC) test-browser.obj Fl_Native_File_Chooser.obj $(LIBS) /subsystem:$(SUBSYSTEM) -del $@.obj 2> nul simple-app: simple-app.cxx Fl_Native_File_Chooser.obj $(CC) $(CFLAGS) /Tp simple-app.cxx /c $(CC) simple-app.obj Fl_Native_File_Chooser.obj $(LIBS) /subsystem:$(SUBSYSTEM) -del $@.obj 2> nul clean: FORCE -del *.obj > NUL -del *.pdb > NUL -del *.ilk > NUL -del *.idb > NUL -del *.sln > NUL -del *.suo > NUL -del test-browser.exe > NUL -del simple-app.exe > NUL # FORCE TARGET -- Do not remove FORCE: itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Makefile-fltk20000644000076500000240000000534111226666572023247 0ustar paulystaff# Makefile that uses fltkconfig # Submitted by Ian MacArthur. # NOTE: This makes use of new features in fltk-config 1.1.6: --cxx and --cc flags. ######################################################### # Assumes FLTK2CONFIG has been set by main Makefile # flags for compiler: CFLAGS = -Wall -O3 -I. $(shell $(FLTK2CONFIG) --cflags) -DFLTK2 CXXFLAGS = -Wall -O3 -I. $(shell $(FLTK2CONFIG) --cxxflags) -DFLTK2 # Possible steps after linking... STRIP = strip POSTBUILD = $(FLTK2CONFIG) --post # libraries to link with: LDLIBS = -lm LINKFLTK = $(shell $(FLTK2CONFIG) --ldstaticflags) IMGLIB = $(shell $(FLTK2CONFIG) --use-images --ldstaticflags) GL_LIB = $(shell $(FLTK2CONFIG) --use-gl --ldstaticflags) # Other general settings CXX = $(shell $(FLTK2CONFIG) --cxx) CC = $(shell $(FLTK2CONFIG) --cc) # now we can make specific modifications based on the operating system and host UNAME := $(shell uname) NATIVESRCS=Fl_Native_File_Chooser.cxx \ Fl_Native_File_Chooser_WIN32.cxx \ Fl_Native_File_Chooser_MAC.cxx \ Fl_Native_File_Chooser_FLTK.cxx \ fltk/NativeFileChooser.h \ fltk/NativeFileChooser_WIN32.h \ fltk/NativeFileChooser_MAC.h \ fltk/NativeFileChooser_FLTK.h ifeq '$(OS)' "Windows_NT" EXE = .exe endif # end of WIN32 options ifeq ($(strip $(UNAME)),Linux) EXE = endif # end of linux options ifeq ($(strip $(UNAME)),Darwin) EXE = LDLIBS += -framework CoreFoundation endif # end of OSX options #.SILENT: # Make the build run quietly all: test-browser-fltk2$(EXE) simple-app-fltk2$(EXE) ######################################################### # make sure the basic rules are in place, just in case... # Build commands and filename extensions... .SUFFIXES: .c .cxx .cpp .cc .h .fl .o # Rule to build an object file from a C++ source file %.o : %.cxx @echo Compile $@... $(CXX) -c -o $@ $< $(CXXFLAGS) # Rule to build an object file from a C source file %.o : %.c @echo Compile $@... $(CC) -c -o $@ $< $(CFLAGS) ######################################################### # define rules for the known targets... NativeFileChooser.o: $(NATIVESRCS) $(CXX) $(CXXFLAGS) $< -c -o NativeFileChooser.o test-browser-fltk2.o: test-browser-fltk2.cxx $(CXX) $(CXXFLAGS) $< -c test-browser-fltk2$(EXE): test-browser-fltk2.o NativeFileChooser.o $(CXX) test-browser-fltk2.o NativeFileChooser.o $(IMGLIB) $(LDLIBS) -o $@ $(STRIP) $@ $(POSTBUILD) test-browser-fltk2$(EXE) simple-app-fltk2.o: simple-app-fltk2.cxx $(CXX) $(CXXFLAGS) $< -c simple-app-fltk2$(EXE): simple-app-fltk2.o NativeFileChooser.o $(CXX) simple-app-fltk2.o NativeFileChooser.o $(IMGLIB) $(LDLIBS) -o $@ $(STRIP) $@ $(POSTBUILD) simple-app-fltk2$(EXE) # FORCE TARGET -- Do not remove FORCE: itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Makefile-fltk2.MICROSOFT0000644000076500000240000000374411226666572024620 0ustar paulystaff### ### FLTK2 MAKEFILE FOR MICROSOFT VS EXPRESS 8 ### This is Microsoft's native *free* compiler. ### # Assumes FLTK2DIR has been set by main Makefile.MICROSOFT # SUBSYSTEM # "console" to see stdout/stderr, # "windows" to make regular windows app. # SUBSYSTEM=console # CHANGE THESE FLAGS AS NEEDED FOR YOUR COMPILER # If unsure what to set these to, compile one of the FLTK test programs, # and match these flags to those shown in the FLTK program's build log. # # The default settings shown here are for "Visual Studio Express 8" # # /MD = DYNAMIC: Multithreaded DLL; MSVCRT.DLL needed at runtime # /MT = STATIC: Multithreaded; MSVCRT.LIB is used; no DLL needed at runtime # /Wall = add for WAAAY too much info. Useful only if you grep out OS's errors! # /W3 = somewhat verbose # CC=cl /EHsc /DVS2000 /DVISUAL_STUDIO /D_CRT_SECURE_NO_DEPRECATE CFLAGS= /I$(FLTK2DIR) /I. /DFLTK2 LIBS=/link $(FLTK2DIR)/lib/fltk2.lib \ ws2_32.lib msimg32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib \ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib NATIVESRCS=Fl_Native_File_Chooser.cxx \ Fl_Native_File_Chooser_WIN32.cxx \ fltk/NativeFileChooser.H \ fltk/NativeFileChooser_WIN32.H default: test-browser-fltk2 simple-app-fltk2 NativeFileChooser.obj: $(NATIVESRCS) $(CC) $(CFLAGS) /Tp Fl_Native_File_Chooser.cxx /c /FoNativeFileChooser.obj test-browser-fltk2: test-browser-fltk2.cxx NativeFileChooser.obj $(CC) $(CFLAGS) /Tp test-browser-fltk2.cxx /c $(CC) test-browser-fltk2.obj NativeFileChooser.obj $(LIBS) /subsystem:$(SUBSYSTEM) simple-app-fltk2: simple-app-fltk2.cxx NativeFileChooser.obj $(CC) $(CFLAGS) /Tp simple-app-fltk2.cxx /c $(CC) simple-app-fltk2.obj NativeFileChooser.obj $(LIBS) /subsystem:$(SUBSYSTEM) clean: FORCE -del *.obj > NUL -del *.pdb > NUL -del *.ilk > NUL -del *.idb > NUL -del *.sln > NUL -del *.suo > NUL -del test-browser.exe > NUL -del simple-app.exe > NUL # FORCE TARGET -- Do not remove FORCE: itksnap/Utilities/FLTK/Fl_Native_File_Chooser/Makefile.MICROSOFT0000644000076500000240000000163211250505506023554 0ustar paulystaff### ### Makefile for Windows - Fl_Native_File_Chooser ### # CHANGE THESE PATHS AS NEEDED. (Comment out to disable) # | # \|/ # v FLTKDIR=C:/fltk-1.3.x-r6148 FLTK2DIR=c:/fltk-2.x-svn all: FORCE ifneq ($(FLTKDIR),) @echo *** @echo *** FLTK1 BUILD @echo *** gmake -f Makefile-fltk1.MICROSOFT FLTKDIR=$(FLTKDIR) endif ifneq ($(FLTK2DIR),) @echo *** @echo *** FLTK2 BUILD *** @echo *** gmake -f Makefile-fltk2.MICROSOFT FLTK2DIR=$(FLTK2DIR) endif Fl_Native_File_Chooser.obj: FORCE gmake -f Makefile-fltk1.MICROSOFT FLTKDIR=$(FLTKDIR) Fl_Native_File_Chooser.obj NativeFileChooser.obj: FORCE gmake -f Makefile-fltk2.MICROSOFT FLTK2DIR=$(FLTK2DIR) NativeFileChooser.obj clean: FORCE -del *.obj > NUL -del *.pdb > NUL -del *.ilk > NUL -del *.idb > NUL -del *.sln > NUL -del *.suo > NUL -del test-browser.exe > NUL -del simple-app.exe > NUL # FORCE TARGET -- Do not remove FORCE: itksnap/Utilities/FLTK/Fl_Native_File_Chooser/README.txt0000644000076500000240000000700511226666572022264 0ustar paulystaffFl_Native_File_Chooser -- Access platform's native file choosers in FLTK ------------------------------------------------------------------------ WHAT IS "Fl_Native_File_Chooser"? ================================= Fl_Native_File_Chooser is a 'widget' wrapper to access the different platform's native file choosers. On platforms that don't have a 'native' file chooser (linux), we use FLTK's own. The purpose of this library is to make a consistent interface for accessing the different native file choosers. Tested under Linux, Mac OSX and Windows with fltk 1.1.6. Tested under Linux with fltk 2.x. LICENSING ========= Fl_Native_File_Chooser comes with complete free source code. Fl_Native_File_Chooser is available under the terms of the GNU Library General Public License. See COPYING for more info. Yes, it can be used in commercial software! Free! Imagine that. BUILD INSTRUCTIONS ================== 1. Which version of FLTK? Fl_Native_File_Chooser now supports fltk1 and fltk2 as of 0.83e. Edit the Makefile and uncomment/modify the FLTKCONFIG and FLTK2CONFIG variables as needed. If both variables are uncommented, both versions of Fl_Native_File_Chooser will be built. For instance, if you have both fltk1 and fltk2 installed on your system, the top of your Makefile settings might look like: FLTKCONFIG=/usr/local/src/fltk-1.1.x-svn/fltk-config FLTK2CONFIG=/usr/local/src/fltk-2.0-svn/fltk2-config Then you can build both with just: make These test programs are created, depending on if you have configured the above for fltk1, fltk2, or both: ./test-browser -- fltk1 exerciser demo ./simple-app -- fltk1 simple app ./test-browser-fltk2 -- fltk2 exerciser demo ./simple-app-fltk2 -- fltk2 simple app Originally Fl_Native_File_Chooser was designed for FLTK1, so there may be some left over FLTK1 specific references in the docs. Please report these as bugs (see below) PLATFORM SPECIFIC NOTES ======================= For linux and osx the default compilers are used. For Windows, tested with VS Express 8 + make.bat / Makefile.MICROSOFT. Ian confirmed it compiled OK under Windows with mingw using the default unix Makefile. WHERE'S THE DOCUMENTATION? ========================== ./documentation/index.html HOW DO I LINK Fl_Native_File_Chooser INTO MY OWN APPLICATION? ============================================================= ./documentation/how-to-use.html FILE LAYOUT =========== ./Makefile -- main Makefile for unix builds ./Makefile.MICROSOFT -- main Makefile for native Microsoft builds ./documentation/index.html -- public documentation for fltk1 + fltk2 ./FL \__ fltk1 include files and lib ./Fl_Native_File_Chooser.o / (for your app to include and link) ./fltk \__ fltk2 include files and lib ./NativeFileChooser.o / (for your app to include and link) ./reference -- project's reference docs (internal use) *_FLTK.{cxx,H,h} -- Platforms that don't have native choosers *_MAC.{cxx,H,h} -- Mac platform specific source code *_WIN32.{cxx,H,h} -- Windows platform specific source code RELEASE NOTES/VERSION INFORMATION ================================= See ./CHANGES. BUGS? FEATURE REQUESTS? ======================= Send bugs and RFE's to erco at seriss dot com itksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference/0000755000076500000240000000000011560342171022505 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference/CVS/0000755000076500000240000000000011560342171023140 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference/CVS/Entries0000644000076500000240000000023711560342171024476 0ustar paulystaff/README-natev-mods.txt/1.1/Mon Jul 13 17:23:06 2009// /REFERENCE-MAC.txt/1.1/Mon Jul 13 17:23:06 2009// /erco-merge-trick.cxx/1.1/Mon Jul 13 17:23:06 2009// D itksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference/CVS/Repository0000644000076500000240000000007011560342171025237 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference itksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference/CVS/Root0000644000076500000240000000010011560342171023775 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference/erco-merge-trick.cxx0000644000076500000240000000076511226666572026415 0ustar paulystaff#define FLTK1 #include #ifdef FLTK1 #define FOO_CLASS Fl_Foo #else #define FOO_CLASS fltk::Foo #endif #ifdef FLTK1 class Fl_Foo { #else namespace fltk { class Foo { #endif public: void aaa(); void bbb(); #ifdef FLTK1 }; #else }; } #endif void FOO_CLASS::aaa() { printf("aaa\n"); } void FOO_CLASS::bbb() { printf("bbb\n"); } #ifdef FLTK1 int main() { Fl_Foo foo; foo.aaa(); foo.bbb(); } #else int main() { fltk::Foo foo; foo.aaa(); foo.bbb(); } #endif itksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference/README-natev-mods.txt0000644000076500000240000000774311226666572026306 0ustar paulystaff===Fl_Native_File_Chooser=== A library to simplify creation of user-friendly file and directory choosers. // // This library 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 // Library General Public License for more details. // ===LOG=== 2/Apr/2005 took Greg Ercolano's version 0.62, added FLTK support, changed API and added multiple filter selection 4/Apr/2005 Win32 nullncat fix applied, improved input filter checking 9/Apr/2005 a lot of Mac filter work done, yet no setting initial filter 14/Apr/2005 Mac set initial filter, disallow multi-select by disabling 'Choose' button 15/Apr/2005 Win32 disable non-folder locations, got set of initial filter working 20/Apr/2005 Passed event handler to all Mac choosers to fix modal problems, minor Mac tidies 21/Apr/2005 Some minor fixes to Win32 code. Thanks Don! 3/May/2005 Added one line to enable setting selected filter on 10.3 14/May/2005 Added preset_file which enables setting a default filename**, added AWOL ctor on Mac, made FLTK browser (usually***) keep its place 20/July/2005 Made FLTK-side preset_file not apply to directories, since that was weird looking 20/Aug/2005 merged Ian MacArthur's fix and makefile with my current tree. much thanks a.rburgers and Ian for taking the time to create makefiles! 26/Aug/2005 fixed another warning, removed a CFString.h include, thanks again Ian! 30/Aug/2005 fixed another little bug in the Win32 code, thanks Alessandro! 10/Oct/2005 updated documentation 11/Oct/2005 Fixed bug that could return bad value from the FLTK ::filename() method 11/Oct/2005 Fixed nativefc.cxx to include proper file 26/Oct/2005 Modified makefile 27/Oct/2005 fixed missing return in FLTK filename(int) method 27/Oct/2005 shushed signedness warning in _FLTK chooser for loop TBD: ensure Windows code will both compile and run across all platforms ( tested a WinXP-compiled version on Win98 with no trouble ) TBD?: implement multi-folder selection on Win32 if possible TBD?: enable preset selection on Mac OS X open dialogs TBD: try working around ugly inactive filtered OS X items, again* TBD: tidy code if possible (there might be some debug/development leftovers) TBD: make sure errmsg is always set to suitable end-user messages TBD: add a fuller constructor TBD: make sure directory() reports the directory the dialog will display TBD: test, test, test. ===USE=== If you have compiled and installed the FLTK source, you should be able to compile this package using 'make' The package does not install any files, but will build an object file and tester for your platform. Your platform's header file will be copied to Fl_Native_File_Chooser.H To compile into your program, include the header file and link with Fl_Native_File_Chooser.o ===NOTES / KNOWN ISSUES=== OS X: *A glitch in Mac OS X (pre-Tiger) causes only the icons to activate/deactivate after the initial filter selection. The names of files keep their original higlights. A few attempts at a workaround failed, patches welcome! *On OS X, using preset_filename() only works for saving files now. *Some apparent bugs in Apple's Spotlight code cause trouble with the leftmost Spotlight results column: - even when the Native_Chooser is supposed to be allowing only single selection, it is unable to catch multiple selections there - (!) if a directory is picked from that column, the Native_Chooser seems to be told that there were NO PATHNAMES RETURNED FLTK: *If preset_filename() and a NULL directory() is used with the FLTK browser, the cwdir rather than the previous directory will be used This library is almost beta, see the TBD's above. This library may have undiscovered bugs, use at your own risk. I suppose one place for bug reports would be via the fltk.general newsgroup, although this library is not currently an official part of FLTK. Feel free to e-mail me. Enjoy! -natevw (at yahoo dot com) http://homepages.dordt.edu/~nthnlvnd/projects.html itksnap/Utilities/FLTK/Fl_Native_File_Chooser/reference/REFERENCE-MAC.txt0000644000076500000240000000344511226666572025165 0ustar paulystaffMac reference: Top Level: http://developer.apple.com/documentation/Carbon/Conceptual/ProvidingNavigationDialogs/nsx_concepts/chapter_2_section_2.html#//apple_ref/doc/uid/TP30001147-CH202-CHDEDCBD Functions: http://developer.apple.com/documentation/Carbon/Conceptual/ProvidingNavigationDialogs/nsx_tasks/chapter_3_section_3.html#//apple_ref/doc/uid/TP30001147-CH203-BEIGBDGD Nav Tasks: http://developer.apple.com/documentation/Carbon/Conceptual/ProvidingNavigationDialogs/nsx_tasks/chapter_3_section_1.html#//apple_ref/doc/uid/TP30001147-CH203-BABGIAGG Preload: http://groups.google.com/groups?q=NavCreateGetFileDialog+directory&hl=en&lr=&c2coff=1&selm=oster-A486E7.08390422102002%40newssvr21-ext.news.prodigy.com&rnum=1 http://developer.apple.com/qa/qa2001/qa1151.html CFString: file:///Developer/Documentation/CoreFoundation/Reference/CFStringRef/index.html#//apple_ref/doc/uid/20001211 // http://developer.apple.com/documentation/Carbon/Reference/Navigation_Services_Ref/index.html http://lists.apple.com/archives/carbon-dev/2005/Feb/msg01230.html http://www.mactech.com/macintosh-c/chap18-1.html http://developer.apple.com/documentation/Carbon/Reference/Navigation_Services_Ref/nav_serv_ref/chapter_1.4_section_2.html -block New Folder button http://developer.apple.com/documentation/Carbon/Reference/Navigation_Services_Ref/nav_serv_ref/chapter_1.4_section_9.html http://developer.apple.com/documentation/Carbon/Reference/Navigation_Services_Ref/nav_serv_ref/chapter_1.4_section_6.html http://lists.apple.com/archives/nav-serv-developers/2003/Apr/msg00003.html http://developer.apple.com/datatype/creatorcode.html http://developer.apple.com/documentation/Carbon/Conceptual/ProvidingNavigationDialogs/nsx_tasks/chapter_3_section_3.html // itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app-fltk2.cxx0000644000076500000240000000501511250505506024362 0ustar paulystaff// // simple-app.cxx -- simple example application // // Copyright 2004 by Greg Ercolano. // FLTK2 port by Frederic Hoerni 2007. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please keep code 80 column compliant. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // #include #include #include #include #include #include #include #include // GLOBALS fltk::Input *G_filename = NULL; void Butt_CB(fltk::Widget*, void*) { // Create native chooser fltk::NativeFileChooser native; native.title("Pick a file"); native.type(fltk::NativeFileChooser::BROWSE_FILE); native.filter("C Files\t*.{cxx,h,c}\n" "C Files\t*.{cxx,h,c}"); native.preset_file(G_filename->value()); // Show native chooser switch ( native.show() ) { case -1: fprintf(stderr, "ERROR: %s\n", native.errmsg()); break; // ERROR case 1: fprintf(stderr, "*** CANCEL\n"); fltk::beep(); break; // CANCEL default: // PICKED FILE if ( native.filename() ) G_filename->value(native.filename()); else G_filename->value("NULL"); break; } } int main() { fltk::Window *win = new fltk::Window(600, 100, "FLTK Window"); win->begin(); { int y = 10; G_filename = new fltk::Input(80, y, win->w()-80-10, 25, "Filename"); G_filename->value("."); G_filename->tooltip("Default filename"); y += G_filename->h() + 5; fltk::Button *but = new fltk::Button(win->w()-80-10, win->h()-25-10, 80, 25, "Pick File"); but->callback(Butt_CB); } win->end(); win->resizable(win); win->show(); return(fltk::run()); } itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/0000755000076500000240000000000011560342171023375 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/0000755000076500000240000000000011560342171025172 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/CVS/0000755000076500000240000000000011560342171025625 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/CVS/Entries0000644000076500000240000000005511560342171027161 0ustar paulystaff/Info.plist/1.1/Sat Sep 5 16:10:29 2009// D itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/CVS/Entries.Log0000644000076500000240000000001611560342171027676 0ustar paulystaffA D/MacOS//// itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/CVS/Repository0000644000076500000240000000010611560342171027724 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/CVS/Root0000644000076500000240000000010011560342171026462 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/Info.plist0000644000076500000240000000061311250506565027147 0ustar paulystaff CFBundleInfoDictionaryVersion 6.0 CFBundleExecutable simple-app CFBundleIdentifier org.fltk.simple-app CFBundleName simple-app CFBundlePackageType APPL itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/MacOS/0000755000076500000240000000000011560342171026134 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/MacOS/CVS/0000755000076500000240000000000011560342171026567 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/MacOS/CVS/Entries0000644000076500000240000000000211560342171030113 0ustar paulystaffD itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/MacOS/CVS/Repository0000644000076500000240000000011411560342171030665 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/MacOS itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/Contents/MacOS/CVS/Root0000644000076500000240000000010011560342171027424 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/CVS/0000755000076500000240000000000011560342171024030 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/CVS/Entries0000644000076500000240000000000211560342171025354 0ustar paulystaffD itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/CVS/Entries.Log0000644000076500000240000000002111560342171026075 0ustar paulystaffA D/Contents//// itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/CVS/Repository0000644000076500000240000000007511560342171026134 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.app/CVS/Root0000644000076500000240000000010011560342171024665 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/simple-app.cxx0000644000076500000240000000471311250505506023346 0ustar paulystaff// // simple-app.cxx -- simple example application // // Copyright 2004 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please keep code 80 column compliant. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // #include #include #include #include #include #include #include // GLOBALS Fl_Input *G_filename = NULL; void Butt_CB(Fl_Widget*, void*) { // Create native chooser Fl_Native_File_Chooser native; native.title("Pick a file"); native.type(Fl_Native_File_Chooser::BROWSE_FILE); native.filter("Text\t*.txt\n" "C Files\t*.{cxx,h,c}"); native.preset_file(G_filename->value()); // Show native chooser switch ( native.show() ) { case -1: fprintf(stderr, "ERROR: %s\n", native.errmsg()); break; // ERROR case 1: fprintf(stderr, "*** CANCEL\n"); fl_beep(); break; // CANCEL default: // PICKED FILE if ( native.filename() ) G_filename->value(native.filename()); else G_filename->value("NULL"); break; } } int main() { Fl_Window *win = new Fl_Window(600, 100, "FLTK Window"); win->begin(); // (for symmetry with fltk2) { int y = 10; G_filename = new Fl_Input(80, y, win->w()-80-10, 25, "Filename"); G_filename->value("."); G_filename->tooltip("Default filename"); y += G_filename->h() + 5; Fl_Button *but = new Fl_Button(win->w()-80-10, win->h()-25-10, 80, 25, "Pick File"); but->callback(Butt_CB); } win->end(); win->resizable(win); win->show(); return(Fl::run()); } itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser-fltk2.cxx0000644000076500000240000002212211250505506024751 0ustar paulystaff// // test-browser.cxx -- test the NativeFileChooser widget // // Copyright 2004 by Greg Ercolano. // FLTK2 port by Frederic Hoerni 2007. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please keep code 80 column compliant. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 // WINDOWS // #include #define getcwd _getcwd #define MAXPATHLEN MAX_PATH #else // UNIX // #include #include #include #endif // GLOBALS fltk::NativeFileChooser *G_choo = NULL; fltk::Browser *G_type = NULL; fltk::MultiBrowser *G_options = NULL; fltk::Input *G_directory = NULL; fltk::Input *G_title = NULL; fltk::Input *G_preset_file = NULL; fltk::MultiLineOutput *G_result = NULL; fltk::MultiLineInput *G_filter = NULL; fltk::ValueInput *G_filter_value = NULL; void Butt_CB(fltk::Widget*w, void*) { // TYPE OF CHOOSER switch ( G_type->value() ) { case 0: G_choo->type(fltk::NativeFileChooser::BROWSE_FILE); break; case 1: G_choo->type(fltk::NativeFileChooser::BROWSE_DIRECTORY); break; case 2: G_choo->type(fltk::NativeFileChooser::BROWSE_MULTI_FILE); break; case 3: G_choo->type(fltk::NativeFileChooser::BROWSE_MULTI_DIRECTORY); break; case 4: G_choo->type(fltk::NativeFileChooser::BROWSE_SAVE_FILE); break; } // OPTIONS { int flags = 0; if ( G_options->selected(0) ) flags |= fltk::NativeFileChooser::SAVEAS_CONFIRM; if ( G_options->selected(1) ) flags |= fltk::NativeFileChooser::NEW_FOLDER; if ( G_options->selected(2) ) flags |= fltk::NativeFileChooser::PREVIEW; G_choo->options(flags); } // DIRECTORY if ( G_directory->value()[0] ) { if ( strcmp(G_directory->value(), "NULL") == 0 ) { G_choo->directory(NULL); } else { G_choo->directory(G_directory->value()); } } else { G_choo->directory(NULL); } // PRESET FILE if ( G_preset_file->value()[0] ) { if ( strcmp(G_preset_file->value(), "NULL") == 0 ) { G_choo->preset_file(NULL); } else { G_choo->preset_file(G_preset_file->value()); } } else { G_choo->preset_file(NULL); } // TITLE if ( G_title->value()[0] ) { if ( strcmp(G_title->value(), "NULL") == 0 ) { G_choo->title(NULL); } else { G_choo->title(G_title->value()); } } else { G_choo->title(""); } // FILTERS if ( G_filter->value()[0] ) { if ( strcmp(G_filter->value(), "NULL") == 0 ) { G_choo->filter(NULL); } else { G_choo->filter(G_filter->value()); } } else { G_choo->filter(""); // all files } G_choo->filter_value((int)G_filter_value->value()); switch ( G_choo->show() ) { case -1: // ERROR fprintf(stderr, "*** ERROR show():%s\n", G_choo->errmsg()); break; case 1: // CANCEL fprintf(stderr, "*** CANCEL\n"); break; default: break; } // UPDATE SETTINGS RETURNED BY CHOOSER INTO BROWSER // RESULT { int count = G_choo->count(); int bytes = 0; if ( count > 0 ) { int t; for ( t=0; tfilename(t)) + 2; if ( count > 1 ) fprintf(stderr, "MULTI FILENAME RESULT %d) '%s'\n", t, G_choo->filename(t)); else fprintf(stderr, "FILENAME RESULT '%s'\n", G_choo->filename(t)); } char *s = new char[bytes]; s[0] = '\0'; for ( t=0; tfilename(t)); strcat(s, "\n"); } G_result->value(s); delete [] s; } } // FILTER VALUE G_filter_value->value(G_choo->filter_value()); fprintf(stderr, "FILTER VALUE USED: %d\n", G_choo->filter_value()); // PRESET FILE { const char *s = G_choo->preset_file(); G_preset_file->value(s ? s : "NULL"); fprintf(stderr, " PRESET_FILE(): %s\n", G_choo->preset_file()); } // DIRECTORY { const char *s = G_choo->directory(); G_directory->value(s ? s : "NULL"); fprintf(stderr, " DIRECTORY(): %s\n", G_choo->directory()); } fprintf(stderr, "\n"); } int main() { char *start_filter = "Source Code\t*.{cxx,h,H,cpp}\n"\ "Cxx Only\t*.cxx\n"\ "Text\t*.{txt}\n"\ "Makefiles\tMakefile*\n"\ "All image files \t*.{BMP,CUT,DDS,GIF,ICO,IFF,LBM,JNG,JPG,JPEG,JPE,JIF,KOA," "MNG,PBM,PCD,PCX,PGM,PNG,PPM,PSD,RAS,TGA,TIF,TIFF,WBMP," "XBM,XPM}\n"\ "Windows, OS/2 Bitmap (*.BMP)\n"\ "Dr. Halo (*.CUT)\n"\ "DirectDraw Surface (*.DDS)\t*.DDS\n"\ "Graphic Interchange Format (*.GIF)\t*.GIF\n"\ "Windows Icon (*.ICO)\t*.ICO\n"\ "Amiga IFF (*.IFF;*.LBM)\t*.{IFF,LBM}\n"\ "JBIG (*.JBIG)\t*.JBIG\n"\ "JPEG Network Graphics (*.JNG)\t*.JNG\n"\ "Independent JPEG Group (*.JPG;*.JPEG;*.JPE;*.JIF)\t*.{JPG,JIF,JPEG,JPE}\n"\ "Commodore 64 Koala (*.KOA)\t*.KOA\n"\ "Multiple Network Graphics (*.MNG)\t*.MNG\n"\ "Portable Bitmap (*.PBM)\t*.PBM\n"\ "Kodak PhotoCD (*.PCD)\t*.PCD\n"\ "PC Paintbrush Bitmap (*.PCX)\t*.PCX\n"\ "Portable Graymap (*.PGM)\t*.PGM\n"\ "Portable Network Graphics (*.PNG)\t*.PNG\n"\ "Portable Pixelmap (*.PPM)\t*.PPM\n"\ "Photoshop Document (*.PSD)\t*.PSD\n"\ "Sun Raster Graphic (*.RAS)\t*.RAS\n"\ "Targa (*.TGA)\t*.TGA\n"\ "Tagged Image File Format (*.TIF;*.TIFF)\t*.{TIF,TIFF}\n"\ "Wireless Bitmap (*.WBMP)\t*.WBMP\n"\ "X11 Bitmap (*.XBM)\t*.XBM\n"\ "X11 Pixmap (*.XPM)\t*.XPM"; char start_dir[MAXPATHLEN + 1]; getcwd(start_dir, MAXPATHLEN); char start_file[MAXPATHLEN + 1]; strcpy(start_file, "testfile"); fltk::Window *win = new fltk::Window(600, 520, "Test Browser (FLTK2)"); win->begin(); { int y = 20; G_type = new fltk::Browser(10,y,180,120,"Type"); G_type->add("Single File"); G_type->add("Single Directory"); G_type->add("Multi File"); G_type->add("Multi Directory"); G_type->add("Save File"); G_type->textsize(12); G_type->value(0); G_type->tooltip("Type of browser to use"); G_options = new fltk::MultiBrowser(win->w()-180-10,y,180,120,"Options"); G_options->add("Show SaveAs Confirm"); G_options->add("Show New Folder"); G_options->add("Show Previews"); G_options->tooltip("Platform specific options.\nSeveral can be selected."); G_options->textsize(12); y += G_type->h() + 10 + 20; G_title = new fltk::Input(80, y, win->w()-80-10, 25, "Title"); G_title->value("Title Of Window"); G_title->tooltip("Sets title of browser window\nSet this to 'NULL' for a NULL setting"); y += G_title->h() + 5; G_directory = new fltk::Input(80, y, win->w()-80-10, 25, "Directory"); G_directory->value(start_dir); G_directory->tooltip("Starting directory shown in browser\nSet this to 'NULL' for a NULL setting"); y += G_directory->h() + 5; G_preset_file = new fltk::Input(80, y, win->w()-80-10, 25, "Preset File"); G_preset_file->value(start_file); G_preset_file->tooltip("Default filename used for 'Save File' chooser\nSet this to 'NULL' for a NULL setting"); y += G_preset_file->h() + 5; G_filter = new fltk::MultiLineInput(80, y, win->w()-80-10, 75, "Filter"); G_filter->value(start_filter); G_filter->tooltip("Filter(s) to be available to user\n" "Multiple filters should be specified on separate lines\n" "Set this to 'NULL' for a NULL setting"); y += G_filter->h() + 5; G_filter_value = new fltk::ValueInput(80, y, win->w()-80-10, 25, "Filter Value"); G_filter_value->value(0); G_filter_value->step(1.0); G_filter_value->range(0.0,50.0); G_filter_value->tooltip("Index number of the filter to be applied when browser opens."); y += G_filter_value->h() + 5; G_result = new fltk::MultiLineOutput(80, y, win->w()-80-10, 100, "Result"); G_result->color(51); fltk::Button *but = new fltk::Button(win->w()-80-10, win->h()-25-10, 80, 25, "Browser"); but->callback(Butt_CB); y += but->h() + 10; G_choo = new fltk::NativeFileChooser(); G_choo->type(fltk::NativeFileChooser::BROWSE_FILE); } win->end(); win->resizable(win); win->show(); return(fltk::run()); } itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/0000755000076500000240000000000011560342171023766 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/0000755000076500000240000000000011560342171025563 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/CVS/0000755000076500000240000000000011560342171026216 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/CVS/Entries0000644000076500000240000000005511560342171027552 0ustar paulystaff/Info.plist/1.1/Sat Sep 5 16:10:29 2009// D itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/CVS/Entries.Log0000644000076500000240000000001611560342171030267 0ustar paulystaffA D/MacOS//// itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/CVS/Repository0000644000076500000240000000011011560342171030310 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/CVS/Root0000644000076500000240000000010011560342171027053 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/Info.plist0000644000076500000240000000062111250506565027537 0ustar paulystaff CFBundleInfoDictionaryVersion 6.0 CFBundleExecutable test-browser CFBundleIdentifier org.fltk.test-browser CFBundleName test-browser CFBundlePackageType APPL itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/MacOS/0000755000076500000240000000000011560342171026525 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/MacOS/CVS/0000755000076500000240000000000011560342171027160 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/MacOS/CVS/Entries0000644000076500000240000000000211560342171030504 0ustar paulystaffD itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/MacOS/CVS/Repository0000644000076500000240000000011611560342171031260 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/MacOS itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/Contents/MacOS/CVS/Root0000644000076500000240000000010011560342171030015 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/CVS/0000755000076500000240000000000011560342171024421 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/CVS/Entries0000644000076500000240000000000211560342171025745 0ustar paulystaffD itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/CVS/Entries.Log0000644000076500000240000000002111560342171026466 0ustar paulystaffA D/Contents//// itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/CVS/Repository0000644000076500000240000000007711560342171026527 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.app/CVS/Root0000644000076500000240000000010011560342171025256 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Native_File_Chooser/test-browser.cxx0000644000076500000240000002237111250505506023737 0ustar paulystaff// // test-browser.cxx -- test the Fl_Native_File_Chooser widget // // Copyright 2004 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please keep code 80 column compliant. // // 10 20 30 40 50 60 70 // | | | | | | | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 // #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 // WINDOWS // #include #define getcwd _getcwd #define MAXPATHLEN MAX_PATH #else // UNIX // #include #include #include #endif // GLOBALS Fl_Native_File_Chooser *G_choo = NULL; Fl_Hold_Browser *G_type = NULL; Fl_Multi_Browser *G_options = NULL; Fl_Input *G_directory = NULL; Fl_Input *G_title = NULL; Fl_Input *G_preset_file = NULL; Fl_Multiline_Output *G_result = NULL; Fl_Multiline_Input *G_filter = NULL; Fl_Value_Input *G_filter_value = NULL; void Butt_CB(Fl_Widget*w, void*) { // TYPE OF CHOOSER switch ( G_type->value() ) { case 1: G_choo->type(Fl_Native_File_Chooser::BROWSE_FILE); break; case 2: G_choo->type(Fl_Native_File_Chooser::BROWSE_DIRECTORY); break; case 3: G_choo->type(Fl_Native_File_Chooser::BROWSE_MULTI_FILE); break; case 4: G_choo->type(Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY); break; case 5: G_choo->type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); break; } // OPTIONS { int flags = 0; if ( G_options->selected(1) ) flags |= Fl_Native_File_Chooser::SAVEAS_CONFIRM; if ( G_options->selected(2) ) flags |= Fl_Native_File_Chooser::NEW_FOLDER; if ( G_options->selected(3) ) flags |= Fl_Native_File_Chooser::PREVIEW; G_choo->options(flags); } // DIRECTORY if ( G_directory->value()[0] ) { if ( strcmp(G_directory->value(), "NULL") == 0 ) { G_choo->directory(NULL); } else { G_choo->directory(G_directory->value()); } } else { G_choo->directory(NULL); } // PRESET FILE if ( G_preset_file->value()[0] ) { if ( strcmp(G_preset_file->value(), "NULL") == 0 ) { G_choo->preset_file(NULL); } else { G_choo->preset_file(G_preset_file->value()); } } else { G_choo->preset_file(NULL); } // TITLE if ( G_title->value()[0] ) { if ( strcmp(G_title->value(), "NULL") == 0 ) { G_choo->title(NULL); } else { G_choo->title(G_title->value()); } } else { G_choo->title(""); } // FILTERS if ( G_filter->value()[0] ) { if ( strcmp(G_filter->value(), "NULL") == 0 ) { G_choo->filter(NULL); } else { G_choo->filter(G_filter->value()); } } else { G_choo->filter(""); // all files } G_choo->filter_value((int)G_filter_value->value()); switch ( G_choo->show() ) { case -1: // ERROR fprintf(stderr, "*** ERROR show():%s\n", G_choo->errmsg()); break; case 1: // CANCEL fprintf(stderr, "*** CANCEL\n"); break; default: break; } // UPDATE SETTINGS RETURNED BY CHOOSER INTO BROWSER // RESULT { int count = G_choo->count(); int bytes = 0; if ( count > 0 ) { int t; for ( t=0; tfilename(t)) + 2; if ( count > 1 ) fprintf(stderr, "MULTI FILENAME RESULT %d) '%s'\n", t, G_choo->filename(t)); else fprintf(stderr, "FILENAME RESULT '%s'\n", G_choo->filename(t)); } char *s = new char[bytes]; s[0] = '\0'; for ( t=0; tfilename(t)); strcat(s, "\n"); } G_result->value(s); delete [] s; } } // FILTER VALUE G_filter_value->value(G_choo->filter_value()); fprintf(stderr, "FILTER VALUE USED: %d\n", G_choo->filter_value()); // PRESET FILE { const char *s = G_choo->preset_file(); G_preset_file->value(s ? s : "NULL"); fprintf(stderr, " PRESET_FILE(): %s\n", G_choo->preset_file()); } // DIRECTORY { const char *s = G_choo->directory(); G_directory->value(s ? s : "NULL"); fprintf(stderr, " DIRECTORY(): %s\n", G_choo->directory()); } fprintf(stderr, "\n"); } int main() { // char *start_filter = "Text\t*.txt\nC Files\t*.{cxx,h,c}"; char *start_filter = "Source Code\t*.{cxx,h,H,cpp}\n"\ "Cxx Only\t*.cxx\n"\ "Text\t*.{txt}\n"\ "Makefiles\tMakefile*\n"\ "All image files\t*.{BMP,CUT,DDS,GIF,ICO,IFF,LBM,JNG,JPG,JPEG,JPE,JIF,KOA," "MNG,PBM,PCD,PCX,PGM,PNG,PPM,PSD,RAS,TGA,TIF,TIFF,WBMP," "XBM,XPM}\n"\ "Windows, OS/2 Bitmap (*.BMP)\t*.bmp\n"\ "Dr. Halo (*.CUT)\t*.CUT\n"\ "DirectDraw Surface (*.DDS)\t*.DDS\n"\ "Graphic Interchange Format (*.GIF)\t*.GIF\n"\ "Windows Icon (*.ICO)\t*.ICO\n"\ "Amiga IFF (*.IFF;*.LBM)\t*.{IFF,LBM}\n"\ "JBIG (*.JBIG)\t*.JBIG\n"\ "JPEG Network Graphics (*.JNG)\t*.JNG\n"\ "Independent JPEG Group (*.JPG;*.JPEG;*.JPE;*.JIF)\t*.{JPG,JIF,JPEG,JPE}\n"\ "Commodore 64 Koala (*.KOA)\t*.KOA\n"\ "Multiple Network Graphics (*.MNG)\t*.MNG\n"\ "Portable Bitmap (*.PBM)\t*.PBM\n"\ "Kodak PhotoCD (*.PCD)\t*.PCD\n"\ "PC Paintbrush Bitmap (*.PCX)\t*.PCX\n"\ "Portable Graymap (*.PGM)\t*.PGM\n"\ "Portable Network Graphics (*.PNG)\t*.PNG\n"\ "Portable Pixelmap (*.PPM)\t*.PPM\n"\ "Photoshop Document (*.PSD)\t*.PSD\n"\ "Sun Raster Graphic (*.RAS)\t*.RAS\n"\ "Targa (*.TGA)\t*.TGA\n"\ "Tagged Image File Format (*.TIF;*.TIFF)\t*.{TIF,TIFF}\n"\ "Wireless Bitmap (*.WBMP)\t*.WBMP\n"\ "X11 Bitmap (*.XBM)\t*.XBM\n"\ "X11 Pixmap (*.XPM)\t*.XPM"; char start_dir[MAXPATHLEN + 1]; getcwd(start_dir, MAXPATHLEN); char start_file[MAXPATHLEN + 1]; strcpy(start_file, "testfile"); Fl_Window *win = new Fl_Window(600, 520, "Test Browser (FLTK1)"); int y = 20; G_type = new Fl_Hold_Browser(10,y,180,120,"Type"); G_type->add("Single File"); G_type->add("Single Directory"); G_type->add("Multi File"); G_type->add("Multi Directory"); G_type->add("Save File"); G_type->textsize(12); G_type->value(1); G_type->tooltip("Type of browser to use"); G_options = new Fl_Multi_Browser(win->w()-180-10,y,180,120,"Options"); G_options->add("Show SaveAs Confirm"); G_options->add("Show New Folder"); G_options->add("Show Previews"); G_options->tooltip("Platform specific options.\nSeveral can be selected."); G_options->textsize(12); y += G_type->h() + 10 + 20; G_title = new Fl_Input(80, y, win->w()-80-10, 25, "Title"); G_title->value("Title Of Window"); G_title->tooltip("Sets title of browser window\n" "Set this to 'NULL' for a NULL setting"); y += G_title->h() + 5; G_directory = new Fl_Input(80, y, win->w()-80-10, 25, "Directory"); G_directory->value(start_dir); G_directory->tooltip("Starting directory shown in browser\n" "Set this to 'NULL' for a NULL setting"); y += G_directory->h() + 5; G_preset_file = new Fl_Input(80, y, win->w()-80-10, 25, "Preset File"); G_preset_file->value(start_file); G_preset_file->tooltip("Default filename used for 'Save File' chooser\n" "Set this to 'NULL' for a NULL setting"); y += G_preset_file->h() + 5; G_filter = new Fl_Multiline_Input(80, y, win->w()-80-10, 75, "Filter"); G_filter->value(start_filter); G_filter->tooltip("Filter(s) to be available to user\n" "Multiple filters should be specified on separate lines\n" "Set this to 'NULL' for a NULL setting"); y += G_filter->h() + 5; G_filter_value = new Fl_Value_Input(80, y, win->w()-80-10, 25, "Filter Value"); G_filter_value->value(0); G_filter_value->tooltip("Index number of the filter to be applied " "when browser opens."); y += G_filter_value->h() + 5; G_result = new Fl_Multiline_Output(80, y, win->w()-80-10, 100, "Result"); G_result->color(51); Fl_Button *but = new Fl_Button(win->w()-80-10, win->h()-25-10, 80, 25, "Browser"); but->callback(Butt_CB); y += but->h() + 10; G_choo = new Fl_Native_File_Chooser(); G_choo->type(Fl_Native_File_Chooser::BROWSE_FILE); win->resizable(win); win->show(); return(Fl::run()); } itksnap/Utilities/FLTK/Fl_Native_File_Chooser/TODO0000644000076500000240000003247511250505506021251 0ustar paulystaff> Under OSX, the 'simple-app' program can't open up the file browser with the preset_file() selected in the browser. WHY?? It selects the dir ok, but not the file. > Christophe points out differences with FLTK's API: * type enums are different (Fl_File_Chooser::MULTI vs. Fl_Native_File_Chooser::BROWSE_MULTI_FILE) SOLUTION: mark old names legacy, implement FLTK's names as alternate, modify docs. * show() works differently (Native's show() blocks) SOLUTION: Meh, not sure there is one, native choosers have to block. * filters have slightly different syntax ("type (*.ext)\t" vs. "type\t*.ext\n") SOLUTION: Might be as simple as just ignoring parens around filter when specified. Document FLTK's syntax, and indicate old syntax is legacy. * filename() is indexed from 0 instead of 1 * filename() should be changed to value() SOLUTION: Kill above two birds with one stone: Mark filename() as obsolete/legacy in docs, keep method code. Implement value(int) and friends to replace filename(), making sure value(int) is 1 based. Make docs and for() loop example 1 based. > Christophe points out that adding but->shortcut(FL_META+'o'); to simple-app.cxx causes FLTK/MAC to theink the meta key is still down after invoking the short cut, then cancelling out of the chooser. (Just hitting 'o' after the chooser closes will re-open the browser) Probably the native chooser for MAC/Leopard is eating the keyup events. Had christophe repost question to fltk.general on 01/07/08. CHECK HISTORY TO SEE IF THIS WAS SOLVED. > Add an option to control enable/disable of 'New Folder' button on Mac. See: _keepstate = kNavDontNewFolderState; ..in the file Fl_Native_File_Chooser_MAC.cxx. Maybe change that to: if ( ! ( _options & NEW_FOLDER ) ) { _keepstate |= kNavDontNewFolderState; } > Determine why WIN32 and OSX don't open browser with preset file selected. 09/04/2009 MAC: It is years later, and I still can't figure this out. Several sources on the net say the pathname can only be a directory, not a filename for preselect to work. XX/XX/XXXX WIN: tried to make this work, but it just doesn't support it! Even calling GetOpenFileName(&_ofn); twice in a row. ### NOT SURE ### ### NOT SURE ### ### NOT SURE ### ### NOT SURE ### ### NOT SURE ### ### NOT SURE ### (?) > 0.83d: WINDOWS/FILE BROWSE: (?) If simple-app's preset filename doesn't end in a backslash, (?) browser shows contents of parent dir, not dir itself. (?) Maybe append a slash if stat() shows preset filename is a DIR? -- 0.82c -- (?) > 0.82c: Windows "Save As" is not setting correct directory in browser (?) > 0.82: Andreas reports problem with Mac chooser not showing thumbnails (?) that live on remote drives even though the Finder does. (?) Files located on the local file system show up OK. (?) (See Reference #1 below for Apple's SimpleText.c excerpts?) (?) Also, see Andreas' email 12/05/2005 (Reference #2 below) (?) > 0.82: Presetting value() under linux causes chooser to open with file (?) highlighted in the chooser, but not in windows or mac. (?) > 0.82: Fl_Native_File_Chooser: WINDOWS doesn't handle returning UTF8 from (?) native windows file chooser.. comes back as garbage, possibly as non-UTF8? (?) (Found problem on Japanese localized machine by browsing to a dir (?) with Japanese chars. Shows OK in windows browser, but when returned (?) to Fl_Input, comes up as garbage, even though the Fl_Input is capable (?) of showing text correctly if japanese text pasted as text into the (?) Fl_Input from Windows URL input) #### DONE #### #### DONE #### #### DONE #### #### DONE #### #### DONE #### #### DONE #### > 0.84: Ian MacArthur indicated mingw warns about strapp and strdump being "defined but not used" cause it's a static. Should probably make a lib, disable this warning, or reference the calls in an unused subroutine. > 0.83d: Namespace pollution: Yuri wants Carbon.h/win32.h to not be in the public .H files. Can probably make an 'underbar' version of the class to isolate them. See erco's post on fltk.general 06/24/07. > 0.83d: MAC / Save File: If: 1) /tmp/foo.xxx exists 2) Directory: set to /tmp 3) Preset File set to foo.xxx ..when browser opens, foo.xxx is GRAYED OUT, even though filter appears to be on. Can still click on grayed out items to pick them, and Save As: name changes. Weird! Looks OK when 'Single File' is selected. *** DONE *** (DONE) > 0.82c: "Save As" + "Show Confirm Dialog" is not showing the confirm dialog..! (DONE) TESTED ON LINUX AND OSX TO WORK OK. (DONE) > 0.83c: Shane Hill on 02/07/06 reports that under Windows, doing regular (DONE) file browsing results in the current working dir being changed. (DONE) Luckily there appears to be a WIN32 flag for this, but the docs (DONE) say it's ineffective..!? (DONE) OFN_NOCHANGEDIR (DONE) Restores the current directory to its original value if the user changed (DONE) the directory while searching for files. (DONE) Windows NT 4.0/2000/XP: This flag is ineffective for GetOpenFileName. (DONE) ^^^^^^^^^^^^^^^^^^^^^^^^ (DONE) > 0.82: Alessandro: uses very long wildcard strings.. (DONE) Replace fixed arrays (char wildcard[80]) with new strapp() stuff. ------------------------------------------------------------------------ Reference #1 ------------------------------------------------------------------------ // REFERENCE FROM SimpleText.c for erco.. short numTypes; OSType typeList[20]; OSType fileType = '\?\?\?\?'; NavDialogRef navDialog; DetermineWindowTypeOrOpen( nil, fileType, &typeList[0], &numTypes, nil ); // Open as many documents as the user wishes through Appleevents return OpenFileDialog( 'ttxt', numTypes, typeList, &navDialog ); OSType typeList[20]; OSType docList[20]; pFileTypes[*numTypes] = 'MooV'; pDocumentTypes[*numTypes] = kMovieWindow; (*numTypes)++; OSStatus OpenFileDialog( OSType applicationSignature, short numTypes, OSType typeList[], NavDialogRef *outDialog ) { OSStatus theErr = noErr; if ( gOpenFileDialog == NULL ) { NavDialogCreationOptions dialogOptions; NavTypeListHandle openList = NULL; NavGetDefaultDialogCreationOptions( &dialogOptions ); dialogOptions.modality = kWindowModalityNone; dialogOptions.clientName = CFStringCreateWithPascalString( NULL, LMGetCurApName(), GetApplicationTextEncoding()); openList = (NavTypeListHandle)NewOpenHandle( applicationSignature, numTypes, typeList ); theErr = NavCreateGetFileDialog( &dialogOptions, openList, GetPrivateEventUPP(), NULL, NULL, NULL, &gOpenFileDialog ); ------------------------------------------------------------------------ Reference #2 ------------------------------------------------------------------------ Andreas Schömann wrote: > Greg Ercolano wrote: > >> Andreas Schömann wrote: >> >>> I've found a problem: when loading images (jpg) via network, no thumbnail is shown in the file chooser. Actual loading works. The network device is NTFS formatted. I thought it might be an access rights problem, but with 'Finder' it works fine... BTW, Andreas is specifically referring to the MAC file browser; when you have it in 'column' mode, when you highlight eg. a jpg image, the preview that normally displays off to the right doesn't show up. I looked at the SimpleText.c code that comes with the OSX developer toolkit, ie: /Developer//Examples/Carbon/SimpleText/* ..and it looks like they use a few options to NavCreateGetFileDialog() that I don't use, namely arg2 and arg3 to set a list of files and a callback. I have to admit I don't fully understand the Mac stuff when it comes to this sort of thing. Apparently there's a global database somewhere of 4 character file types that dates back to the 1980's, and it appears one needs to know these names and hard code them into your app (as is done in SimpleText.cxx). I think I'll leave this unfinished, since it's not really a show stopper, and I'm not sure how to make it work correctly. > 4) Here on my machine Fl_Native_File_Chooser displays a preview for _local_ images (e.g. .jpeg, .tiff and .png) Really? I actually couldn't get that to work on my 10.4.3 box. When I single click on a jpeg, png, or other image format, it shows a generic image, but the correct text info about the image's file type. What does work is if I click on .c or .cxx files it shows correctly, and if I click on a .rm file (Real Media), it shows Real's icon logo.. (shrug) In the Finder, when you click on images, it shows a thumbnail of the actual image. > Hmmmh, somewhat confusing results... > Especially the different behaviour of Finder and File Chooser is a mystery to me. Why did Apple do that? Maybe this has changed with Tiger? > 1) and 2) tell me that one can disable file preview with 'Navigation Services', though probably not by intention. It might be I'm missing some args to NavCreateGetFileDialog() (arg2 and 3?) Unfortunately I'm not sure what is the correct code to make that work, if that's even the cause. Or maybe I need to link in some apple framework that's missing to make that stuff work. > I also had a look into the documentation ( http://developer.apple.com/documentation/Carbon/Conceptual/ProvidingNavigationDialogs/index.html ) and it says that 'Navigation Services' offers to specify a 'preview callback' with 'NavCreateGetFileDialog'. This is called when the user selects a file and offers a way to preview custom file types. But Fl_Native_File_Chooser does not use this feature and in that case 'Navigation Services' inspects the file. Right, as described above. However, I'd think there'd be defaults for things like jpeg/png/etc. I'd hate to think I have to manually provide callbacks for each image type..! ------------------------------------------------------------------------ Reference #3 ------------------------------------------------------------------------ -- 0.86 -- > (DONE -- 0.86) ADDITIONS REQUESTED BY MANOLO OCT 08 2008 Hi Greg, I have been using your Fl_Native_File_Chooser with general satisfaction but had to make 3 changes in Fl_Native_File_Chooser_MAC.cxx for it to suit my needs, and would like to present them to you. 1) using a BROWSE_SAVE_FILE displays the list of file formats as set by the filter member function plus a "Default" format that I wanted to get rid of. The solution to that is to put kNavGenericSignature as 3rg argument of the NavCreatePutFileDialog call in your <-- (DONE 0.86) post() function. Because the initially selected file format can be controlled by the filter_value member function, I don't see what can be the purpose of having a Default format in addition to a programmable set of formats. Furthermore, presence of the Default format renders the call to filter_value ignored by the navigation dialog window. Thus, I would think that using this kNavGenericSignature argument would benefit Fl_Native_Fie_Chooser_MAC without loss of functionality. Alternatively, an accessor function to the 3rd argument of the NavCreatePutFileDialog call would be helpful. 2) using a BROWSE_SAVE_FILE, with a call to the preset_file member function, often fails to correctly display the desired preset filename in the navigation dialog. This happens in your event_handler function, case kNavCBStart, that attempts to get the FSSpec of the preset file and fails because this file does not exist yet. I thus changed the 1st statement of the kNavCBStart clause as follows: if ( nfb->directory() /* || nfb->preset_file() */) { <-- (DONE 0.86) 3) the preset_file member function does not work correctly with non-ascii containing filenames. This is easily repaired, without side effect, using kCFStringEncodingUTF8 instead of <-- (DONE 0.86) kCFStringEncodingASCII in the statement just before the NavDialogSetSaveFileName call. I take this opportunity to mention to you my XCode plug-in that fully drives a Win32 C/C++ cross-compiler within Apple's Xcode development environment: http://pbil.univ-lyon1.fr/members/mgouy/XCodeWin32Plugin/ Many thanks for your Fl_Native_Fie_Chooser, and best wishes, > (DONE -- 0.86) Walter Garms points out mapped network shares (eg. Z:) don't show up in 'BROWSE_DIRECTORY' mode unless the code is modified. See emails dated 05/12/2008: --- Garms, Walter (GE EntSol, Security) wrote: With your help I was able to solve the problem of the network drives. For Version 6.0, at least, the BIF_SHAREABLE flag seems to have the opposite sense: With BIF_SHAREABLE not set I see the mapped network drives, and with BIF_SHAREABLE set I do not. --- ie. he removed BIF_SHAREABLE from this section: #if defined(BIF_NONEWFOLDERBUTTON) // Version 6.0 if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_NONEWFOLDERBUTTON; _binf.ulFlags |= BIF_USENEWUI | BIF_SHAREABLE | BIF_RETURNONLYFSDIRS; ^^^^^^^^^^^^^ #elif defined(BIF_USENEWUI) itksnap/Utilities/FLTK/Fl_Table/0000755000076500000240000000000011560342171015727 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Table/CHANGES0000644000076500000240000002261211401101412016706 0ustar paulystaff3.13 -- 04/08/09 -- Non-critical additions to example programs o Added simple-display-table.cxx example o Added cell fg/bg color control to exercisetablerow example o Added 'documentation/how-to-use.html' (snuck into tar file 05/07/09) o Converted Makefile's to use 'fltk-config' o Fix for Ivica Lazarevic's row_position() bug report on 03/16/09 o Converted all code to FLTK brace and 2-space indent style o Added some comments to documentation examples 3.12 -- 04/02/06 -- Misc optimizations and non-critical additions o 04/02/06: Small fix to exercisetablerow's Type: menu o 04/02/06: Added Ori Berger's optimizations for push_back() -> size()/fill o 04/02/06: default compiles for FLTK 1.1.7 o 04/02/06: [INTERNAL] 'make tar' now inserts version# into tarfile o 02/21/05: John Taber's modifications for singleinput draw_cell() prototype 3.11 -- 08/13/04 erco: added box(val) which now calls resize() to recalculate table. 3.10 -- 02/22/04 erco: misc fixes, added Jean Marc's keyboard nav! o Merged in Jean Marc's keyboard navigation and rectangular mouse selection. Tested OK on IRIX/LINUX/WINDOWS/OSX. o Added Jean Marc's testkeyboardnav.cxx example showing how to use new keyboard nav and mouse region selection. o TODO: Need to make mouse region selection controllable via 'or'able flags: > Single cell (SELECT_CELL) > Row of cells (eg. Fl_Browser, Fl_Table_Row) (SELECT_ROW) > Column of cells (SELECT_COL) > Freeform row/col (current behavior) (SELECT_MULTI) Implement as Fl_Table::type(). o Erco's mods to Jean Marc's additions: > Re-enabled resizing cursor > Some whitespace reformatting, added some comments > Added //FALLTHROUGH comments > Moved auto_drag_cb2() to just above auto_drag_cb() > Added Windows specific code to Fl_Table::change_cursor() to work around a cursor problem with fltk-utf8-1.1.4. o Added SINGLE/MULTI/NONE selection behavior. See Fl_Table_Row::type() o Doc fixes as per dsnpa's email on 08/11/03 o CONTEXT_NEWPAGE retired as per obsolete warnings. Use CONTEXT_STARTPAGE instead. 3.01 -- 08/10/03 erco: minor fixes o Fl_Table::children() return a number two less than table->children(), to skip over Fl_Scroll's scrollbars. o Fl_Table::handle(): prevented unwanted callback() for downclick event during interactive resize. This was causing sort buttons to be triggered unexpectedly during interactive column resizing. o sortapp: changed sort arrow from black triangle to an engraved triangle. o Added Fl_Table::is_interactive_resize() o Docs updated to elaborate on callback() management. o callback() now invoked with CONTEXT_RC_RESIZE when columns or rows are resized via the API, eg. with col_width() or row_height(). o Fixed doc bug: Fl_Table::child() and Fl_Table::children() were not documented, and some old development ideas were in their place. Fixed, and added to the web docs. 3.00 -- 06/28/03 erco: Major changes, callback() API modified o Prevented unwanted auto-scrolling during col/row resizing o No longer invokes callback if mouse button pressed in one row, and released in another. o Fl_Table::visible_cells() added for irush and for Ori Berger (fltk.general, 06/25/03) o Fl_Table::rows() and cols() are now virtual for Glenn Ramsey (fltk.general 06/25/03) o Fl_Table::clear() made virtual. o Now handles drawing row_headers when there are no rows, and handles drawing col_headers when there are no columns. o On FL_LEAVE, force cursor to normal 'just in case' o Fixed clipping when window sized /very/ small, so scroll bars don't 'leak' outside the widget. o OPTIMIZATION: Fl_Table::table_resized() no longer calls redraw(). If you want redraw() after calling table_resized(), you must call it explicitly. o OPTIMIZATION: Adding rows which are offscreen no longer causes entire table to redraw, only the vertical scrollbar. o callback() system called when rows/cols interactively resized too, sent CONTEXT_RC_RESIZE. o Fl_Table and Fl_Table_Row: moved handle()'s static state vars into the class to avoid crosstalk between multiple instances. 3.00 alpha 2 -- 06/01/03 erco o Also modified exercisetablerow.cxx, sortapp.cxx and singleinput.cxx to follow the fltk callback() convention. o Fixed Makefile.IRIX -- missing REZCOMMAND 3.00 alpha -- 06/01/03 erco o Refitted with the normal fltk callback() / when() conventions, to replace the old Fl_Table callback() virtual stuff. ALPHA TESTERS: Please see table_cb() function in testtablerow.cxx, and docs in documentation/Fl_Table.html for new methods: callback() -- set the callback callback_row() -- row the user clicked on callback_col() -- column the user clicked on callback_context() -- where on table the user clicked 2.15 -- 05/28/03 erco o Docs for select_row() includes documenting 2=toggle. o select_all_rows() modified to include 2=toggle, docs modified. 2.14 -- 05/27/03 erco o Reduced flicker for row selections, added some private methods (Fl_Table::_redraw_cell()) and data (_redraw_leftcol), and a protected method redraw_range() to Fl_Table. Fl_Table::draw() modified to use damage(FL_DAMAGE_CHILD). o Updated Fl_Table_Row docs: docs for all public methods o Added Fl_Table::clear() method o Fl_Table::scroll_cb() now calls recalc_dimensions(); vars like tix, tiy weren't being recalculated. o Changed Fl_Table_Row::handle() from private -> protected o Optimizations to row_scroll_position() and col_scroll_position() to allow 100,000 x 100,000 element tables to redraw quickly: BEFORE: ~5 sec redraws for the lower right corner of the table. AFTER: imperceptible redraw time for entire table. Added two private vars (toprow_scrollpos, leftcol_scrollpos) o Updated README to include a 'What is Fl_Table?' section, as there seems to be some confusion about what Fl_Table is and is not. o 'singleinput' compile instructions were missing from the Makefile.MICROSOFT o fltk-config: tried to implement in unix Makefiles, but encountered a hitch with multiple FLTK versions. Holding back on adding fltk-config until resolved. 2.13 -- 05/17/03 erco o Removed all iostream stuff -- sick of all that noise about 'deprecated headers' under Redhat 9.0 and VS .NET. o Changed having Fl_Table_Row::handle() from returning if a child handled an event; caused new FL_PUSH mod to break selection routines. o Changed sortapp to show ls listing instead of 'cat README' o Implemented Glenn Ramsey's recommendation to set ret = 1 on receipt of FL_PUSH. o Modified singleinput.cxx to call take_focus() to ensure it works correctly when parented by e.g. Fl_Tabs. o Added -lXft to Makefile.LINUX to support antialiased fonts under Redhat 9.0 2.13(alpha) -- 04/29/03 erco o Marcin Jedlinski reports 'c CONTEXT_STARTPAGE o Added Mister Satan's singleinput.cxx example to release o Applied Mister Satan's fixes for scrollbars appearing 1 pixel too early. ('hideh/hidev < 0' changed to '.. <= 0') o CONTEXT_NEWPAGE sends table dimensions via x/y/w/h, instead of zeroes. Docs changed to reflect this. o Added Makefile.MINGW32, and modified uname to truncate at the underbar o Fixed 'sortapp' so that it would work with blank lines in the example. 2.11 -- 04/09/03 erco o Removed some unused scrollbar event handling o Added CONTEXT_TABLE events when clicked in dead zones o Fixed problem with two Fl_Table or more Fl_Table's (MR SATAN) o Allow setting row_height and col_width w/out any row/cols defined o testtablerow now shows two tables, to test for event problems. 2.10a -- 04/06/03 erco o Makefile mods for Microsoft o atoi() #include for Microsoft missing o Removed unused variables causing warnings on SGI 2.10 -- 04/06/03 erco o Got widgets away from STL as per Matthais request (only sortapp.cxx has it) o Added table_box() to set the kind of box around the inner table o Added row/col_min_resize() o Cleaned up management of box borders (concept: inside vs. outside) o Fixed some missing line breaks in the docs o Implemented separate Fl_Scroll so Fl_Table can contain fltk widgets o Fixed new row headers so they don't callback() during a resize o Fixed wild select when dragged off table o Select-drag off table now auto-scrolls 2.00 -- 04/02/03 erco o Added row headers o Added ability to make it easy to add fltk widgets to the table o You can now set row/col header colors and heights/widths o Fixed drawing problems with table deadzones 1.30 -- 03/30/03 erco o Added widgettable.cxx o Mods to Fl_Table to facilitate embeding fltk widget 1.20 -- 03/18/03 erco o Modified sortapp.cxx: sort direction indicated by up/down arrow in header o Makefiles for complile/run on Linux, OSX, Windows, IRIX. Tested under Linux, OSX, Windows, but not IRIX (didn't have vector library under 6.2) o Verified compiles and runs ok with fltk 1.1.1 and 1.1.3 1.10 -- 01/09/03 erco o Mods for bcc (thanks to Marek Paliwoda) o Removed MyTable.cxx, an old file o Added 'make tar' target o Added CHANGES log 1.00 -- 12/16/02 erco o Initial implementation/release itksnap/Utilities/FLTK/Fl_Table/CMakeLists.txt0000644000076500000240000000027411460531114020466 0ustar paulystaffINCLUDE_DIRECTORIES(${SNAP_SOURCE_DIR}/Utilities/FLTK/Fl_Table/FL) INCLUDE_DIRECTORIES(${SNAP_SOURCE_DIR}/Utilities/FLTK/Fl_Table) ADD_LIBRARY(fltk_table Fl_Table.cxx Fl_Table_Row.cxx) itksnap/Utilities/FLTK/Fl_Table/COPYING0000644000076500000240000006511211401101412016750 0ustar paulystaff Fl_Table License December 16, 2002 The Fl_Table library and included programs are provided under the terms of the GNU Library General Public License (LGPL) with the following exceptions: 1. Modifications to the Fl_Table configure script, config header file, and makefiles by themselves to support a specific platform do not constitute a modified or derivative work. The authors do request that such modifications be contributed to the Fl_Table project - send all contributions to "erco at seriss dot com". 2. Widgets that are subclassed from Fl_Table widgets do not constitute a derivative work. 3. Static linking of applications and widgets to the Fl_Table library does not constitute a derivative work and does not require the author to provide source code for the application or widget, use the shared Fl_Table libraries, or link their applications or widgets against a user-supplied version of Fl_Table. If you link the application or widget to a modified version of Fl_Table, then the changes to Fl_Table must be provided under the terms of the LGPL in sections 1, 2, and 4. 4. You do not have to provide a copy of the Fl_Table license with programs that are linked to the Fl_Table library, nor do you have to identify the Fl_Table license in your program or documentation as required by section 6 of the LGPL. However, programs must still identify their use of Fl_Table. The following example statement can be included in user documentation to satisfy this requirement: [program/widget] is based in part on the work of the Fl_Table project http://seriss.com/people/erco/fltk/Fl_Table/ ----------------------------------------------------------------------- GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! itksnap/Utilities/FLTK/Fl_Table/CREDITS0000644000076500000240000000163411401101412016734 0ustar paulystaffCREDITS - Fl_Table ------------------ This file lists the people responsible for the toolkit you are now using. If you've looking for your name in lights but we've forgotten you here, please send an email to "fltk-bugs@fltk.org" and we'll update this file accordingly. CORE DEVELOPERS Greg Ercolano 12/16/2002 - initial implementation 12/16/02. Fl_Table, Fl_Table_Row, docs. Jean-Marc Lienher 02/22/2004 - added keyboard nav + mouse selection, and ported Fl_Table into fltk-utf8-1.1.4 OTHER CONTRIBUTORS Inspired by the Feb 2000 version of FLVW's Flvw_Table widget. Mucho thanks to those folks. Mister Satan 04/07/2003 - MinGW porting mods, and singleinput.cxx; a cool Fl_Input oriented spreadsheet example Marek Paliwoda 01/08/2003 - Porting mods for Borland Ori Berger 03/16/2006 - Optimizations for >500k rows/cols itksnap/Utilities/FLTK/Fl_Table/CVS/0000755000076500000240000000000011560342171016362 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Table/CVS/Entries0000644000076500000240000000052311560342171017716 0ustar paulystaff/CHANGES/1.1/Tue Jun 1 03:48:26 2010// /CMakeLists.txt/1.2/Sat Oct 23 10:00:44 2010// /COPYING/1.1/Tue Jun 1 03:48:26 2010// /CREDITS/1.1/Tue Jun 1 03:48:26 2010// /Fl_Table.cxx/1.1/Tue Jun 1 03:48:26 2010// /Fl_Table_Row.cxx/1.1/Tue Jun 1 03:48:26 2010// /README/1.1/Tue Jun 1 03:48:26 2010// /TODO/1.1/Tue Jun 1 03:48:26 2010// D itksnap/Utilities/FLTK/Fl_Table/CVS/Entries.Log0000644000076500000240000000001311560342171020430 0ustar paulystaffA D/FL//// itksnap/Utilities/FLTK/Fl_Table/CVS/Repository0000644000076500000240000000004011560342171020456 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Table itksnap/Utilities/FLTK/Fl_Table/CVS/Root0000644000076500000240000000010011560342171017217 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Table/FL/0000755000076500000240000000000011560342171016230 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Table/FL/CVS/0000755000076500000240000000000011560342171016663 5ustar paulystaffitksnap/Utilities/FLTK/Fl_Table/FL/CVS/Entries0000644000076500000240000000013411560342171020215 0ustar paulystaff/Fl_Table.H/1.1/Tue Jun 1 03:48:26 2010// /Fl_Table_Row.H/1.1/Tue Jun 1 03:48:26 2010// D itksnap/Utilities/FLTK/Fl_Table/FL/CVS/Repository0000644000076500000240000000004311560342171020762 0ustar paulystaffitksnap/Utilities/FLTK/Fl_Table/FL itksnap/Utilities/FLTK/Fl_Table/FL/CVS/Root0000644000076500000240000000010011560342171017520 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/FLTK/Fl_Table/FL/Fl_Table.H0000644000076500000240000003226411401101412020001 0ustar paulystaff// // Fl_Table -- A table widget // // Copyright 2002 by Greg Ercolano. // Copyright (c) 2004 O'ksi'D // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please report all bugs and problems to "erco at seriss dot com". // // TODO: // o Auto scroll during dragged selection // o Keyboard navigation (up/down/left/right arrow) // #ifndef _FL_TABLE_H #define _FL_TABLE_H #include #include // memcpy #ifdef _WIN32 #include // WINDOWS: malloc/realloc #else /*_WIN32*/ #include // UNIX: malloc/realloc #endif /*_WIN32*/ #include #include #include #include #include class Fl_Table : public Fl_Group { public: enum TableContext { CONTEXT_NONE = 0, CONTEXT_STARTPAGE = 0x01, // before a page is redrawn CONTEXT_ENDPAGE = 0x02, // after a page is redrawn CONTEXT_ROW_HEADER = 0x04, // in the row header CONTEXT_COL_HEADER = 0x08, // in the col header CONTEXT_CELL = 0x10, // in one of the cells CONTEXT_TABLE = 0x20, // in the table CONTEXT_RC_RESIZE = 0x40 // column or row being resized }; private: int _rows, _cols; // total rows/cols int _row_header_w; // width of row header int _col_header_h; // height of column header int _row_position; // last row_position set (not necessarily == toprow!) int _col_position; // last col_position set (not necessarily == leftcol!) char _row_header; // row header enabled? char _col_header; // col header enabled? char _row_resize; // row resizing enabled? char _col_resize; // col resizing enabled? int _row_resize_min; // row minimum resizing height (default=1) int _col_resize_min; // col minimum resizing width (default=1) // OPTIMIZATION: partial row/column redraw variables int _redraw_toprow; int _redraw_botrow; int _redraw_leftcol; int _redraw_rightcol; Fl_Color _row_header_color; Fl_Color _col_header_color; int _auto_drag; int _selecting; // An STL-ish vector without templates class IntVector { int *arr; unsigned int _size; void init() { arr = NULL; _size = 0; } void copy(int *newarr, unsigned int newsize) { size(newsize); memcpy(arr, newarr, newsize * sizeof(int)); } public: IntVector() { init(); } // CTOR ~IntVector() { if ( arr ) free(arr); arr = NULL; } // DTOR IntVector(IntVector&o) { init(); copy(o.arr, o._size); } // COPY CTOR IntVector& operator=(IntVector&o) { // ASSIGN init(); copy(o.arr, o._size); return(*this); } int operator[](int x) const { return(arr[x]); } int& operator[](int x) { return(arr[x]); } unsigned int size() { return(_size); } void size(unsigned int count) { if ( count != _size ) { arr = (int*)realloc(arr, count * sizeof(int)); _size = count; } } int pop_back() { int tmp = arr[_size-1]; _size--; return(tmp); } void push_back(int val) { unsigned int x = _size; size(_size+1); arr[x] = val; } int back() { return(arr[_size-1]); } }; IntVector _colwidths; // column widths in pixels IntVector _rowheights; // row heights in pixels Fl_Cursor _last_cursor; // last mouse cursor before changed to 'resize' cursor // EVENT CALLBACK DATA TableContext _callback_context; // event context int _callback_row, _callback_col; // event row/col // handle() state variables. // Put here instead of local statics in handle(), so more // than one Fl_Table can exist without crosstalk between them. // int _resizing_col; // column being dragged int _resizing_row; // row being dragged int _dragging_x; // starting x position for horiz drag int _dragging_y; // starting y position for vert drag int _last_row; // last row we FL_PUSH'ed // Redraw single cell void _redraw_cell(TableContext context, int R, int C); void _start_auto_drag(); void _stop_auto_drag(); void _auto_drag_cb(); static void _auto_drag_cb2(void *d); protected: enum ResizeFlag { RESIZE_NONE = 0, RESIZE_COL_LEFT = 1, RESIZE_COL_RIGHT = 2, RESIZE_ROW_ABOVE = 3, RESIZE_ROW_BELOW = 4 }; int table_w, table_h; // table's virtual size (in pixels) int toprow, botrow, leftcol, rightcol; // four corners of viewable table // selection int current_row, current_col; int select_row, select_col; // OPTIMIZATION: Precomputed scroll positions for the toprow/leftcol int toprow_scrollpos; int leftcol_scrollpos; // Dimensions int tix, tiy, tiw, tih; // data table inner dimension xywh int tox, toy, tow, toh; // data table outer dimension xywh int wix, wiy, wiw, wih; // widget inner dimension xywh Fl_Scroll *table; // container for child fltk widgets (if any) Fl_Scrollbar *vscrollbar; // vertical scrollbar Fl_Scrollbar *hscrollbar; // horizontal scrollbar // Fltk int handle(int e); // fltk handle() override // Class maintenance void recalc_dimensions(); void table_resized(); // table resized; recalc void table_scrolled(); // table scrolled; recalc void get_bounds(TableContext context, // return x/y/w/h bounds for context int &X, int &Y, int &W, int &H); void change_cursor(Fl_Cursor newcursor); // change mouse cursor to some other shape TableContext cursor2rowcol(int &R, int &C, ResizeFlag &resizeflag); // find r/c given current x/y event int find_cell(TableContext context, // find cell's x/y/w/h given r/c int R, int C, int &X, int &Y, int &W, int &H); int row_col_clamp(TableContext context, int &R, int &C); // clamp r/c to known universe // Called to draw cells virtual void draw_cell(TableContext context, int R=0, int C=0, int X=0, int Y=0, int W=0, int H=0) { } // overridden by deriving class long row_scroll_position(int row); // find scroll position of row (in pixels) long col_scroll_position(int col); // find scroll position of col (in pixels) int is_fltk_container() { // does table contain fltk widgets? return( Fl_Group::children() > 3 ); // (ie. more than box and 2 scrollbars?) } static void scroll_cb(Fl_Widget*,void*); // h/v scrollbar callback void damage_zone(int r1, int c1, int r2, int c2, int r3 = 0, int c3 = 0); void redraw_range(int toprow, int botrow, int leftcol, int rightcol) { if ( _redraw_toprow == -1 ) { // Initialize redraw range _redraw_toprow = toprow; _redraw_botrow = botrow; _redraw_leftcol = leftcol; _redraw_rightcol = rightcol; } else { // Extend redraw range if ( toprow < _redraw_toprow ) _redraw_toprow = toprow; if ( botrow > _redraw_botrow ) _redraw_botrow = botrow; if ( leftcol < _redraw_leftcol ) _redraw_leftcol = leftcol; if ( rightcol > _redraw_rightcol ) _redraw_rightcol = rightcol; } // Indicate partial redraw needed of some cells damage(FL_DAMAGE_CHILD); } public: Fl_Table(int X, int Y, int W, int H, const char *l=0); ~Fl_Table(); virtual void clear() { rows(0); cols(0); } // topline() // middleline() // bottomline() inline void table_box(Fl_Boxtype val) { table->box(val); table_resized(); } inline Fl_Boxtype table_box( void ) { return(table->box()); } virtual void rows(int val); // set/get number of rows inline int rows() { return(_rows); } virtual void cols(int val); // set/get number of columns inline int cols() { return(_cols); } inline void visible_cells(int& r1, int& r2, int& c1, int& c2) { r1 = toprow; r2 = botrow; c1 = leftcol; c2 = rightcol; } // Interactive resizing happening? int is_interactive_resize() { return(_resizing_row != -1 || _resizing_col != -1); } inline int row_resize() { return(_row_resize); } void row_resize(int flag) { // enable row resizing _row_resize = flag; } inline int col_resize() { return(_col_resize); } void col_resize(int flag) { // enable col resizing _col_resize = flag; } inline int col_resize_min() { // column minimum resizing width return(_col_resize_min); } void col_resize_min(int val) { _col_resize_min = ( val < 1 ) ? 1 : val; } inline int row_resize_min() { // column minimum resizing width return(_row_resize_min); } void row_resize_min(int val) { _row_resize_min = ( val < 1 ) ? 1 : val; } inline int row_header() { // set/get row header enable flag return(_row_header); } void row_header(int flag) { _row_header = flag; table_resized(); redraw(); } inline int col_header() { // set/get col header enable flag return(_col_header); } void col_header(int flag) { _col_header = flag; table_resized(); redraw(); } inline void col_header_height(int height) { // set/get col header height _col_header_h = height; table_resized(); redraw(); } inline int col_header_height() { return(_col_header_h); } inline void row_header_width(int width) { // set/get row header width _row_header_w = width; table_resized(); redraw(); } inline int row_header_width() { return(_row_header_w); } inline void row_header_color(Fl_Color val) { // set/get row header color _row_header_color = val; redraw(); } inline Fl_Color row_header_color() { return(_row_header_color); } inline void col_header_color(Fl_Color val) { // set/get col header color _col_header_color = val; redraw(); } inline Fl_Color col_header_color() { return(_col_header_color); } void row_height(int row, int height); // set/get row height inline int row_height(int row) { return((row<0 || row>=(int)_rowheights.size()) ? 0 : _rowheights[row]); } void col_width(int col, int width); // set/get a column's width inline int col_width(int col) { return((col<0 || col>=(int)_colwidths.size()) ? 0 : _colwidths[col]); } void row_height_all(int height) { // set all row/col heights for ( int r=0; rinit_sizes(); table->redraw(); } void add(Fl_Widget& w) { table->add(w); } void add(Fl_Widget* w) { table->add(w); } void insert(Fl_Widget& w, int n) { table->insert(w,n); } void insert(Fl_Widget& w, Fl_Widget* w2) { table->insert(w,w2); } void remove(Fl_Widget& w) { table->remove(w); } void begin() { table->begin(); } void end() { table->end(); // HACK: Avoid showing Fl_Scroll; seems to erase screen // causing unnecessary flicker, even if its box() is FL_NO_BOX. // if ( table->children() > 2 ) { table->show(); } else { table->hide(); } Fl_Group::current((Fl_Group*)(Fl_Group::parent())); } Fl_Widget * const *array() { return(table->array()); } Fl_Widget *child(int n) const { return(table->child(n)); } int children() const { return(table->children()-2); // -2: skip Fl_Scroll's h/v scrollbar widgets } int find(const Fl_Widget *w) const { return(table->find(w)); } int find(const Fl_Widget &w) const { return(table->find(w)); } // CALLBACKS int callback_row() { return(_callback_row); } int callback_col() { return(_callback_col); } TableContext callback_context() { return(_callback_context); } void do_callback(TableContext context, int row, int col) { _callback_context = context; _callback_row = row; _callback_col = col; Fl_Widget::do_callback(); } }; #endif /*_FL_TABLE_H*/ itksnap/Utilities/FLTK/Fl_Table/FL/Fl_Table_Row.H0000644000076500000240000001015611401101412020624 0ustar paulystaff#ifndef _FL_TABLE_ROW_H #define _FL_TABLE_ROW_H // // Fl_Table_Row -- A row oriented table widget // // A class specializing in a table of rows. // Handles row-specific selection behavior. // // Copyright 2002 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please report all bugs and problems to "erco at seriss dot com". // // #include "Fl_Table.H" class Fl_Table_Row : public Fl_Table { public: enum TableRowSelectMode { SELECT_NONE, // no selection allowed SELECT_SINGLE, // single row selection SELECT_MULTI // multiple row selection (default) }; private: // An STL-ish vector without templates class CharVector { char *arr; int _size; void init() { arr = NULL; _size = 0; } void copy(char *newarr, int newsize) { size(newsize); memcpy(arr, newarr, newsize * sizeof(char)); } public: CharVector() { // CTOR init(); } ~CharVector() { // DTOR if ( arr ) free(arr); arr = NULL; } CharVector(CharVector&o) { // COPY CTOR init(); copy(o.arr, o._size); } CharVector& operator=(CharVector&o) { // ASSIGN init(); copy(o.arr, o._size); return(*this); } char operator[](int x) const { return(arr[x]); } char& operator[](int x) { return(arr[x]); } int size() { return(_size); } void size(int count) { if ( count != _size ) { arr = (char*)realloc(arr, count * sizeof(char)); _size = count; } } char pop_back() { char tmp = arr[_size-1]; _size--; return(tmp); } void push_back(char val) { int x = _size; size(_size+1); arr[x] = val; } char back() { return(arr[_size-1]); } }; CharVector _rowselect; // selection flag for each row // handle() state variables. // Put here instead of local statics in handle(), so more // than one instance can exist without crosstalk between. // int _dragging_select; // dragging out a selection? int _last_row; int _last_y; // last event's Y position int _last_push_x; // last PUSH event's X position int _last_push_y; // last PUSH event's Y position TableRowSelectMode _selectmode; protected: int handle(int event); int find_cell(TableContext context, // find cell's x/y/w/h given r/c int R, int C, int &X, int &Y, int &W, int &H) { return(Fl_Table::find_cell(context, R, C, X, Y, W, H)); } public: Fl_Table_Row(int X, int Y, int W, int H, const char *l=0) : Fl_Table(X,Y,W,H,l) { _dragging_select = 0; _last_row = -1; _last_y = -1; _last_push_x = -1; _last_push_y = -1; _selectmode = SELECT_MULTI; } ~Fl_Table_Row() { } void rows(int val); // set number of rows int rows() { // get number of rows return(Fl_Table::rows()); } void type(TableRowSelectMode val); // set selection mode TableRowSelectMode type() const { // get selection mode return(_selectmode); } int row_selected(int row); // is row selected? (0=no, 1=yes, -1=range err) int select_row(int row, int flag=1); // select state for row: flag:0=off, 1=on, 2=toggle // returns: 0=no change, 1=changed, -1=range err void select_all_rows(int flag=1); // all rows to a known state void clear() { rows(0); // implies clearing selection cols(0); Fl_Table::clear(); // clear the table } }; #endif /*_FL_TABLE_ROW_H*/ itksnap/Utilities/FLTK/Fl_Table/Fl_Table.cxx0000644000076500000240000010633011401101412020107 0ustar paulystaff// // Fl_Table -- A table widget // // Copyright 2002 by Greg Ercolano. // Copyright (c) 2004 O'ksi'D // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // #include // fprintf #include #include #if defined(USE_UTF8) && ( defined(MICROSOFT) || defined(LINUX) ) #include // currently only Windows and Linux #endif #define SCROLLBAR_SIZE 16 // Scroll display so 'row' is at top void Fl_Table::row_position(int row) { if ( _row_position == row ) return; // OPTIMIZATION: no change? avoid redraw if ( row < 0 ) row = 0; else if ( row >= rows() ) row = rows() - 1; if ( table_h <= tih ) return; // don't scroll if table smaller than window double newtop = row_scroll_position(row); if ( newtop > vscrollbar->maximum() ) { newtop = vscrollbar->maximum(); } vscrollbar->Fl_Slider::value(newtop); table_scrolled(); redraw(); _row_position = row; // HACK: override what table_scrolled() came up with } // Scroll display so 'col' is at left void Fl_Table::col_position(int col) { if ( _col_position == col ) return; // OPTIMIZATION: no change? avoid redraw if ( col < 0 ) col = 0; else if ( col >= cols() ) col = cols() - 1; if ( table_w <= tiw ) return; // don't scroll if table smaller than window double newleft = col_scroll_position(col); if ( newleft > hscrollbar->maximum() ) { newleft = hscrollbar->maximum(); } hscrollbar->Fl_Slider::value(newleft); table_scrolled(); redraw(); _col_position = col; // HACK: override what table_scrolled() came up with } // Find scroll position of a row (in pixels) long Fl_Table::row_scroll_position(int row) { int startrow = 0; long scroll = 0; // OPTIMIZATION: // Attempt to use precomputed row scroll position // if ( toprow_scrollpos != -1 && row >= toprow ) { scroll = toprow_scrollpos; startrow = toprow; } for ( int t=startrow; t= leftcol ) { scroll = leftcol_scrollpos; startcol = leftcol; } for ( int t=startcol; ttype(FL_VERTICAL); vscrollbar->callback(scroll_cb, (void*)this); hscrollbar = new Fl_Scrollbar(x(), y()+h()-SCROLLBAR_SIZE, w(), SCROLLBAR_SIZE); hscrollbar->type(FL_HORIZONTAL); hscrollbar->callback(scroll_cb, (void*)this); table = new Fl_Scroll(x(), y(), w(), h()); table->box(FL_NO_BOX); table->type(0); // don't show Fl_Scroll's scrollbars -- use our own table->hide(); // hide unless children are present table->end(); table_resized(); redraw(); Fl_Group::end(); // end the group's begin() table->begin(); // leave with fltk children getting added to the scroll } // Dtor Fl_Table::~Fl_Table() { // The parent Fl_Group takes care of destroying scrollbars } // Set height of a row void Fl_Table::row_height(int row, int height) { if ( row < 0 ) return; if ( row < (int)_rowheights.size() && _rowheights[row] == height ) { return; // OPTIMIZATION: no change? avoid redraw } // Add row heights, even if none yet int now_size = (int)_rowheights.size(); if ( row >= now_size ) { _rowheights.size(row); while (now_size < row) _rowheights[now_size++] = height; } _rowheights[row] = height; table_resized(); if ( row <= botrow ) { // OPTIMIZATION: only redraw if onscreen or above screen redraw(); } // ROW RESIZE CALLBACK if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { do_callback(CONTEXT_RC_RESIZE, row, 0); } } // Set width of a column void Fl_Table::col_width(int col, int width) { if ( col < 0 ) return; if ( col < (int)_colwidths.size() && _colwidths[col] == width ) { return; // OPTIMIZATION: no change? avoid redraw } // Add column widths, even if none yet int now_size = (int)_colwidths.size(); if ( col >= now_size ) { _colwidths.size(col); while (now_size < col) { _colwidths[now_size++] = width; } } _colwidths[col] = width; table_resized(); if ( col <= rightcol ) { // OPTIMIZATION: only redraw if onscreen or to the left redraw(); } // COLUMN RESIZE CALLBACK if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { do_callback(CONTEXT_RC_RESIZE, 0, col); } } // Return row/col clamped to reality int Fl_Table::row_col_clamp(TableContext context, int &R, int &C) { int clamped = 0; if ( R < 0 ) { R = 0; clamped = 1; } if ( C < 0 ) { C = 0; clamped = 1; } switch ( context ) { case CONTEXT_COL_HEADER: // Allow col headers to draw even if no rows if ( R >= _rows && R != 0 ) { R = _rows - 1; clamped = 1; } break; case CONTEXT_ROW_HEADER: // Allow row headers to draw even if no columns if ( C >= _cols && C != 0 ) { C = _cols - 1; clamped = 1; } break; case CONTEXT_CELL: default: // CLAMP R/C TO _rows/_cols if ( R >= _rows ) { R = _rows - 1; clamped = 1; } if ( C >= _cols ) { C = _cols - 1; clamped = 1; } break; } return(clamped); } // Return bounding region for given context void Fl_Table::get_bounds(TableContext context, int &X, int &Y, int &W, int &H) { switch ( context ) { case CONTEXT_COL_HEADER: // Column header clipping. X = tox; Y = wiy; W = tow; H = col_header_height(); return; case CONTEXT_ROW_HEADER: // Row header clipping. X = wix; Y = toy; W = row_header_width(); H = toh; return; case CONTEXT_TABLE: // Table inner dimensions X = tix; Y = tiy; W = tiw; H = tih; return; // TODO: Add other contexts.. default: fprintf(stderr, "Fl_Table::get_bounds(): context %d unimplemented\n", (int)context); return; } //NOTREACHED } // Find row/col beneath cursor // // Returns R/C and context. // Also returns resizeflag, if mouse is hovered over a resize boundary. // Fl_Table::TableContext Fl_Table::cursor2rowcol(int &R, int &C, ResizeFlag &resizeflag) { // return values R = C = 0; resizeflag = RESIZE_NONE; // Row header? int X, Y, W, H; if ( row_header() ) { // Inside a row heading? get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H); if ( Fl::event_inside(X, Y, W, H) ) { // Scan visible rows until found for ( R = toprow; R <= botrow; R++ ) { find_cell(CONTEXT_ROW_HEADER, R, 0, X, Y, W, H); if ( Fl::event_y() >= Y && Fl::event_y() < (Y+H) ) { // Found row? // If cursor over resize boundary, and resize enabled, // enable the appropriate resize flag. // if ( row_resize() ) { if ( Fl::event_y() <= (Y+3-0) ) { resizeflag = RESIZE_ROW_ABOVE; } if ( Fl::event_y() >= (Y+H-3) ) { resizeflag = RESIZE_ROW_BELOW; } } return(CONTEXT_ROW_HEADER); } } // Must be in row header dead zone return(CONTEXT_NONE); } } // Column header? if ( col_header() ) { // Inside a column heading? get_bounds(CONTEXT_COL_HEADER, X, Y, W, H); if ( Fl::event_inside(X, Y, W, H) ) { // Scan visible columns until found for ( C = leftcol; C <= rightcol; C++ ) { find_cell(CONTEXT_COL_HEADER, 0, C, X, Y, W, H); if ( Fl::event_x() >= X && Fl::event_x() < (X+W) ) { // Found column? // If cursor over resize boundary, and resize enabled, // enable the appropriate resize flag. // if ( col_resize() ) { if ( Fl::event_x() <= (X+3-0) ) { resizeflag = RESIZE_COL_LEFT; } if ( Fl::event_x() >= (X+W-3) ) { resizeflag = RESIZE_COL_RIGHT; } } return(CONTEXT_COL_HEADER); } } // Must be in column header dead zone return(CONTEXT_NONE); } } // Mouse somewhere in table? // Scan visible r/c's until we find it. // if ( Fl::event_inside(tox, toy, tow, toh) ) { for ( R = toprow; R <= botrow; R++ ) { find_cell(CONTEXT_CELL, R, C, X, Y, W, H); if ( Fl::event_y() < Y ) break; // OPT: thanks lars if ( Fl::event_y() >= (Y+H) ) continue; // OPT: " " for ( C = leftcol; C <= rightcol; C++ ) { find_cell(CONTEXT_CELL, R, C, X, Y, W, H); if ( Fl::event_inside(X, Y, W, H) ) { return(CONTEXT_CELL); // found it } } } // Must be in a dead zone of the table R = C = 0; return(CONTEXT_TABLE); } // Somewhere else return(CONTEXT_NONE); } // Find X/Y/W/H for cell at R/C // If R or C are out of range, returns -1 // with X/Y/W/H set to zero. // int Fl_Table::find_cell(TableContext context, int R, int C, int &X, int &Y, int &W, int &H) { if ( row_col_clamp(context, R, C) ) { // row or col out of range? error X=Y=W=H=0; return(-1); } X = col_scroll_position(C) - hscrollbar->value() + tix; Y = row_scroll_position(R) - vscrollbar->value() + tiy; W = col_width(C); H = row_height(R); switch ( context ) { case CONTEXT_COL_HEADER: Y = wiy; H = col_header_height(); return(0); case CONTEXT_ROW_HEADER: X = wix; W = row_header_width(); return(0); case CONTEXT_CELL: return(0); case CONTEXT_TABLE: return(0); // TODO -- HANDLE OTHER CONTEXTS default: fprintf(stderr, "Fl_Table::find_cell: unknown context %d\n", (int)context); return(-1); } //NOTREACHED } // Enable automatic scroll-selection void Fl_Table::_start_auto_drag() { if (_auto_drag) return; _auto_drag = 1; Fl::add_timeout(0.3, _auto_drag_cb2, this); } // Disable automatic scroll-selection void Fl_Table::_stop_auto_drag() { if (!_auto_drag) return; Fl::remove_timeout(_auto_drag_cb2, this); _auto_drag = 0; } void Fl_Table::_auto_drag_cb2(void *d) { ((Fl_Table*)d)->_auto_drag_cb(); } // Handle automatic scroll-selection if mouse selection dragged off table edge void Fl_Table::_auto_drag_cb() { int lx = Fl::e_x; int ly = Fl::e_y; if (_selecting == CONTEXT_COL_HEADER) { ly = y() + col_header_height(); } else if (_selecting == CONTEXT_ROW_HEADER) { lx = x() + row_header_width(); } if (lx > x() + w() - 20) { Fl::e_x = x() + w() - 20; if (hscrollbar->visible()) ((Fl_Slider*)hscrollbar)->value( hscrollbar->clamp(hscrollbar->value() + 30)); hscrollbar->do_callback(); _dragging_x = Fl::e_x - 30; } else if (lx < (x() + row_header_width())) { Fl::e_x = x() + row_header_width() + 1; if (hscrollbar->visible()) { ((Fl_Slider*)hscrollbar)->value(hscrollbar->clamp(hscrollbar->value() - 30)); } hscrollbar->do_callback(); _dragging_x = Fl::e_x + 30; } if (ly > y() + h() - 20) { Fl::e_y = y() + h() - 20; if (vscrollbar->visible()) { ((Fl_Slider*)vscrollbar)->value(vscrollbar->clamp(vscrollbar->value() + 30)); } vscrollbar->do_callback(); _dragging_y = Fl::e_y - 30; } else if (ly < (y() + col_header_height())) { Fl::e_y = y() + col_header_height() + 1; if (vscrollbar->visible()) { ((Fl_Slider*)vscrollbar)->value(vscrollbar->clamp(vscrollbar->value() - 30)); } vscrollbar->do_callback(); _dragging_y = Fl::e_y + 30; } _auto_drag = 2; handle(FL_DRAG); _auto_drag = 1; Fl::e_x = lx; Fl::e_y = ly; Fl::check(); Fl::flush(); if (Fl::event_buttons() && _auto_drag) { Fl::add_timeout(0.05, _auto_drag_cb2, this); } } // Recalculate the window dimensions void Fl_Table::recalc_dimensions() { // Recalc to* (Table Outer), ti* (Table Inner), wi* ( Widget Inner) wix = ( x() + Fl::box_dx(box())); tox = wix; tix = tox + Fl::box_dx(table->box()); wiy = ( y() + Fl::box_dy(box())); toy = wiy; tiy = toy + Fl::box_dy(table->box()); wiw = ( w() - Fl::box_dw(box())); tow = wiw; tiw = tow - Fl::box_dw(table->box()); wih = ( h() - Fl::box_dh(box())); toh = wih; tih = toh - Fl::box_dh(table->box()); // Trim window if headers enabled if ( col_header() ) { tiy += col_header_height(); toy += col_header_height(); tih -= col_header_height(); toh -= col_header_height(); } if ( row_header() ) { tix += row_header_width(); tox += row_header_width(); tiw -= row_header_width(); tow -= row_header_width(); } // Make scroll bars disappear if window large enough { // First pass: can hide via window size? int hidev = (table_h <= tih); int hideh = (table_w <= tiw); // Second pass: Check for interference if ( !hideh & hidev ) { hidev = (( table_h - tih + SCROLLBAR_SIZE ) <= 0 ); } if ( !hidev & hideh ) { hideh = (( table_w - tiw + SCROLLBAR_SIZE ) <= 0 ); } // Determine scrollbar visibility, trim ti[xywh]/to[xywh] if ( hidev ) { vscrollbar->hide(); } else { vscrollbar->show(); tiw -= SCROLLBAR_SIZE; tow -= SCROLLBAR_SIZE; } if ( hideh ) { hscrollbar->hide(); } else { hscrollbar->show(); tih -= SCROLLBAR_SIZE; toh -= SCROLLBAR_SIZE; } } // Resize the child table table->resize(tox, toy, tow, toh); table->init_sizes(); } // Recalculate internals after a scroll. // // Call this if table has been scrolled or resized. // Does not handle redraw(). // TODO: Assumes ti[xywh] has already been recalculated. // void Fl_Table::table_scrolled() { // Find top row int y, row, voff = vscrollbar->value(); for ( row=y=0; row < _rows; row++ ) { y += row_height(row); if ( y > voff ) { y -= row_height(row); break; } } _row_position = toprow = ( row >= _rows ) ? (row - 1) : row; toprow_scrollpos = y; // OPTIMIZATION: save for later use // Find bottom row voff = vscrollbar->value() + tih; for ( ; row < _rows; row++ ) { y += row_height(row); if ( y >= voff ) { break; } } botrow = ( row >= _rows ) ? (row - 1) : row; // Left column int x, col, hoff = hscrollbar->value(); for ( col=x=0; col < _cols; col++ ) { x += col_width(col); if ( x > hoff ) { x -= col_width(col); break; } } _col_position = leftcol = ( col >= _cols ) ? (col - 1) : col; leftcol_scrollpos = x; // OPTIMIZATION: save for later use // Right column // Work with data left over from leftcol calculation // hoff = hscrollbar->value() + tiw; for ( ; col < _cols; col++ ) { x += col_width(col); if ( x >= hoff ) { break; } } rightcol = ( col >= _cols ) ? (col - 1) : col; // First tell children to scroll draw_cell(CONTEXT_RC_RESIZE, 0,0,0,0,0,0); } // Table resized: recalc internal data // Call this whenever the window is resized. // Recalculates the scrollbar sizes. // Makes no assumptions about any pre-initialized data. // void Fl_Table::table_resized() { table_h = row_scroll_position(rows()); table_w = col_scroll_position(cols()); recalc_dimensions(); // Recalc scrollbar sizes // Clamp scrollbar value() after a resize. // Resize scrollbars to enforce a constant trough width after a window resize. // { // Vertical scrollbar float vscrolltab = ( table_h == 0 || tih > table_h ) ? 1 : (float)tih / table_h; float hscrolltab = ( table_w == 0 || tiw > table_w ) ? 1 : (float)tiw / table_w; vscrollbar->bounds(0, table_h-tih); vscrollbar->precision(10); vscrollbar->slider_size(vscrolltab); vscrollbar->resize(wix+wiw-SCROLLBAR_SIZE, wiy, SCROLLBAR_SIZE, wih - ((hscrollbar->visible())?SCROLLBAR_SIZE:0)); vscrollbar->Fl_Valuator::value(vscrollbar->clamp(vscrollbar->value())); // Horizontal scrollbar hscrollbar->bounds(0, table_w-tiw); hscrollbar->precision(10); hscrollbar->slider_size(hscrolltab); hscrollbar->resize(wix, wiy+wih-SCROLLBAR_SIZE, wiw - ((vscrollbar->visible())?SCROLLBAR_SIZE:0), SCROLLBAR_SIZE); hscrollbar->Fl_Valuator::value(hscrollbar->clamp(hscrollbar->value())); } // Tell FLTK child widgets were resized Fl_Group::init_sizes(); // Recalc top/bot/left/right table_scrolled(); // DO *NOT* REDRAW -- LEAVE THIS UP TO THE CALLER // redraw(); } // Someone moved a scrollbar void Fl_Table::scroll_cb(Fl_Widget*w, void *data) { Fl_Table *o = (Fl_Table*)data; o->recalc_dimensions(); // recalc tix, tiy, etc. o->table_scrolled(); o->redraw(); } // Set number of rows void Fl_Table::rows(int val) { int oldrows = _rows; _rows = val; { int default_h = ( _rowheights.size() > 0 ) ? _rowheights.back() : 25; int now_size = _rowheights.size(); _rowheights.size(val); // enlarge or shrink as needed while ( now_size < val ) { _rowheights[now_size++] = default_h; // fill new } } table_resized(); // OPTIMIZATION: redraw only if change is visible. if ( val >= oldrows && oldrows > botrow ) { // NO REDRAW } else { redraw(); } } // Set number of cols void Fl_Table::cols(int val) { _cols = val; { int default_w = ( _colwidths.size() > 0 ) ? _colwidths[_colwidths.size()-1] : 80; int now_size = _colwidths.size(); _colwidths.size(val); // enlarge or shrink as needed while ( now_size < val ) { _colwidths[now_size++] = default_w; // fill new } } table_resized(); redraw(); } // Change mouse cursor to different type void Fl_Table::change_cursor(Fl_Cursor newcursor) { if ( newcursor != _last_cursor ) { fl_cursor(newcursor, FL_BLACK, FL_WHITE); _last_cursor = newcursor; } } void Fl_Table::damage_zone(int r1, int c1, int r2, int c2, int r3, int c3) { int R1 = r1, C1 = c1; int R2 = r2, C2 = c2; if (r1 > R2) R2 = r1; if (r2 < R1) R1 = r2; if (r3 > R2) R2 = r3; if (r3 < R1) R1 = r3; if (c1 > C2) C2 = c1; if (c2 < C1) C1 = c2; if (c3 > C2) C2 = c3; if (c3 < C1) C1 = c3; if (R1 < 0) { if (R2 < 0) return; R1 = 0; } if (C1 < 0) { if (C2 < 0) return; C1 = 0; } if (R1 < toprow) R1 = toprow; if (R2 > botrow) R2 = botrow; if (C1 < leftcol) C1 = leftcol; if (C2 > rightcol) C2 = rightcol; redraw_range(R1, R2, C1, C2); } int Fl_Table::move_cursor(int R, int C) { if (select_row == -1) R++; if (select_col == -1) C++; R += select_row; C += select_col; if (R < 0) R = 0; if (R >= rows()) R = rows() - 1; if (C < 0) C = 0; if (C >= cols()) C = cols() - 1; if (R == select_row && C == select_col) return 0; damage_zone(current_row, current_col, select_row, select_col, R, C); select_row = R; select_col = C; if (!Fl::event_state(FL_SHIFT)) { current_row = R; current_col = C; } if (R < toprow + 1 || R > botrow - 1) row_position(R); if (C < leftcol + 1 || C > rightcol - 1) col_position(C); return 1; } // #define DEBUG 1 #ifdef DEBUG #include "eventnames.h" #define PRINTEVENT \ fprintf(stderr,"Table %s: ** Event: %s --\n", (label()?label():"none"), eventnames[event]); #else #define PRINTEVENT #endif // Handle FLTK events int Fl_Table::handle(int event) { PRINTEVENT; int ret = Fl_Group::handle(event); // let FLTK group handle events first if (ret) { if (Fl::event_inside(hscrollbar) || Fl::event_inside(vscrollbar)) return 1; if (Fl::focus() != this && contains(Fl::focus())) return 1; } // Which row/column are we over? int R, C; // row/column being worked on ResizeFlag resizeflag; // which resizing area are we over? (0=none) TableContext context = cursor2rowcol(R, C, resizeflag); switch ( event ) { case FL_PUSH: if (Fl::event_button() == 1 && !Fl::event_clicks()) { if (Fl::focus() != this) { take_focus(); do_callback(CONTEXT_TABLE, -1, -1); ret = 1; } damage_zone(current_row, current_col, select_row, select_col, R, C); if (context == CONTEXT_CELL) { current_row = select_row = R; current_col = select_col = C; _selecting = CONTEXT_CELL; } else { current_row = select_row = -1; current_col = select_col = -1; } } // Need this for eg. right click to pop up a menu if ( Fl_Widget::callback() && // callback defined? resizeflag == RESIZE_NONE ) { // not resizing? do_callback(context, R, C); // do callback } switch ( context ) { case CONTEXT_CELL: // FL_PUSH on a cell? ret = 1; // express interest in FL_RELEASE break; case CONTEXT_NONE: // FL_PUSH on table corner? if ( Fl::event_button() == 1 && Fl::event_x() < x() + row_header_width()) { current_col = 0; select_col = cols() - 1; current_row = 0; select_row = rows() - 1; damage_zone(current_row, current_col, select_row, select_col); ret = 1; } break; case CONTEXT_COL_HEADER: // FL_PUSH on a column header? if ( Fl::event_button() == 1) { // Resizing? Handle it if ( resizeflag ) { // Start resize if left click on column border. // "ret=1" ensures we get drag events from now on. // (C-1) is used if mouse is over the left hand side // of cell, so we resize the next column on the left. // _resizing_col = ( resizeflag & RESIZE_COL_LEFT ) ? C-1 : C; _resizing_row = -1; _dragging_x = Fl::event_x(); ret = 1; } else { // Not resizing? Select the column current_col = select_col = C; current_row = 0; select_row = rows() - 1; _selecting = CONTEXT_COL_HEADER; damage_zone(current_row, current_col, select_row, select_col); ret = 1; } } break; case CONTEXT_ROW_HEADER: // FL_PUSH on a row header? if ( Fl::event_button() == 1 ) { // Resizing? Handle it if ( resizeflag ) { // Start resize if left mouse clicked on row border. // "ret = 1" ensures we get drag events from now on. // (R-1) is used if mouse is over the top of the cell, // so that we resize the row above. // _resizing_row = ( resizeflag & RESIZE_ROW_ABOVE ) ? R-1 : R; _resizing_col = -1; _dragging_y = Fl::event_y(); ret = 1; } else { // Not resizing? Select the row current_row = select_row = R; current_col = 0; select_col = cols() - 1; _selecting = CONTEXT_ROW_HEADER; damage_zone(current_row, current_col, select_row, select_col); ret = 1; } } break; default: ret = 0; // express disinterest break; } _last_row = R; break; case FL_DRAG: if (_auto_drag == 1) { ret = 1; break; } if ( _resizing_col > -1 ) { // Dragging column? // // Let user drag even /outside/ the row/col widget. // Don't allow column width smaller than 1. // Continue to show FL_CURSOR_WE at all times during drag. // int offset = _dragging_x - Fl::event_x(); int new_w = col_width(_resizing_col) - offset; if ( new_w < _col_resize_min ) new_w = _col_resize_min; col_width(_resizing_col, new_w); _dragging_x = Fl::event_x(); table_resized(); redraw(); change_cursor(FL_CURSOR_WE); ret = 1; if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { do_callback(CONTEXT_RC_RESIZE, R, C); } } else if ( _resizing_row > -1 ) { // Dragging row? // // Let user drag even /outside/ the row/col widget. // Don't allow row width smaller than 1. // Continue to show FL_CURSOR_NS at all times during drag. // int offset = _dragging_y - Fl::event_y(); int new_h = row_height(_resizing_row) - offset; if ( new_h < _row_resize_min ) new_h = _row_resize_min; row_height(_resizing_row, new_h); _dragging_y = Fl::event_y(); table_resized(); redraw(); change_cursor(FL_CURSOR_NS); ret = 1; if ( Fl_Widget::callback() && when() & FL_WHEN_CHANGED ) { do_callback(CONTEXT_RC_RESIZE, R, C); } } else { if (Fl::event_button() == 1 && _selecting == CONTEXT_CELL && context == CONTEXT_CELL) { if (select_row != R || select_col != C) { damage_zone(current_row, current_col, select_row, select_col, R, C); } select_row = R; select_col = C; ret = 1; } else if (Fl::event_button() == 1 && _selecting == CONTEXT_ROW_HEADER && context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { if (select_row != R) { damage_zone(current_row, current_col, select_row, select_col, R, C); } select_row = R; ret = 1; } else if (Fl::event_button() == 1 && _selecting == CONTEXT_COL_HEADER && context & (CONTEXT_ROW_HEADER|CONTEXT_COL_HEADER|CONTEXT_CELL)) { if (select_col != C) { damage_zone(current_row, current_col, select_row, select_col, R, C); } select_col = C; ret = 1; } } // Enable autodrag if not resizing, and mouse has moved off table edge if ( _resizing_row < 0 && _resizing_col < 0 && _auto_drag == 0 && ( Fl::event_x() > x() + w() - 20 || Fl::event_x() < x() + row_header_width() || Fl::event_y() > y() + h() - 20 || Fl::event_y() < y() + col_header_height() ) ) { _start_auto_drag(); } break; case FL_RELEASE: _stop_auto_drag(); switch ( context ) { case CONTEXT_ROW_HEADER: // release on row header case CONTEXT_COL_HEADER: // release on col header case CONTEXT_CELL: // release on a cell case CONTEXT_TABLE: // release on dead zone if ( _resizing_col == -1 && // not resizing a column _resizing_row == -1 && // not resizing a row Fl_Widget::callback() && // callback defined when() & FL_WHEN_RELEASE && // on button release _last_row == R ) { // release on same row PUSHed? // Need this for eg. left clicking on a cell to select it do_callback(context, R, C); } break; default: break; } if ( Fl::event_button() == 1 ) { change_cursor(FL_CURSOR_DEFAULT); _resizing_col = -1; _resizing_row = -1; ret = 1; } break; case FL_MOVE: if ( context == CONTEXT_COL_HEADER && // in column header? resizeflag ) { // resize + near boundary? change_cursor(FL_CURSOR_WE); // show resize cursor } else if ( context == CONTEXT_ROW_HEADER && // in row header? resizeflag ) { // resize + near boundary? change_cursor(FL_CURSOR_NS); // show resize cursor } else { change_cursor(FL_CURSOR_DEFAULT); // normal cursor } ret = 1; break; case FL_ENTER: // See FLTK event docs on the FL_ENTER widget if (!ret) take_focus(); ret = 1; //FALLTHROUGH case FL_LEAVE: // We want to track the mouse if resizing is allowed. if ( resizeflag ) { ret = 1; } if ( event == FL_LEAVE ) { _stop_auto_drag(); change_cursor(FL_CURSOR_DEFAULT); } break; case FL_FOCUS: Fl::focus(this); //FALLTHROUGH case FL_UNFOCUS: _stop_auto_drag(); ret = 1; break; case FL_KEYBOARD: { ret = 0; int is_row = select_row; int is_col = select_col; switch(Fl::event_key()) { case FL_Home: ret = move_cursor(0, -1000000); break; case FL_End: ret = move_cursor(0, 1000000); break; case FL_Page_Up: ret = move_cursor(-(botrow - toprow - 1), 0); break; case FL_Page_Down: ret = move_cursor(botrow - toprow - 1 , 0); break; case FL_Left: ret = move_cursor(0, -1); break; case FL_Right: ret = move_cursor(0, 1); break; case FL_Up: ret = move_cursor(-1, 0); break; case FL_Down: ret = move_cursor(1, 0); break; } if (ret && Fl::focus() != this) { do_callback(CONTEXT_TABLE, -1, -1); take_focus(); } //if (!ret && Fl_Widget::callback() && when() & FL_WHEN_NOT_CHANGED ) if ( Fl_Widget::callback() && ( ( !ret && when() & FL_WHEN_NOT_CHANGED ) || ( is_row!= select_row || is_col!= select_col ) ) ) { do_callback(CONTEXT_CELL, select_row, select_col); //damage_zone(current_row, current_col, select_row, select_col); ret = 1; } break; } default: change_cursor(FL_CURSOR_DEFAULT); break; } return(ret); } // Resize FLTK override // Handle resize events if user resizes parent window. // void Fl_Table::resize(int X, int Y, int W, int H) { // Tell group to resize, and recalc our own widget as well Fl_Group::resize(X, Y, W, H); table_resized(); redraw(); } // Draw a cell void Fl_Table::_redraw_cell(TableContext context, int r, int c) { if ( r < 0 || c < 0 ) return; int X,Y,W,H; find_cell(context, r, c, X, Y, W, H); // find positions of cell draw_cell(context, r, c, X, Y, W, H); // call users' function to draw it } int Fl_Table::is_selected(int r, int c) { int s_left, s_right, s_top, s_bottom; if (select_col > current_col) { s_left = current_col; s_right = select_col; } else { s_right = current_col; s_left = select_col; } if (select_row > current_row) { s_top = current_row; s_bottom = select_row; } else { s_bottom = current_row; s_top = select_row; } if (r >= s_top && r <= s_bottom && c >= s_left && c <= s_right) { return 1; } return 0; } void Fl_Table::get_selection(int& s_top, int& s_left, int& s_bottom, int& s_right) { if (select_col > current_col) { s_left = current_col; s_right = select_col; } else { s_right = current_col; s_left = select_col; } if (select_row > current_row) { s_top = current_row; s_bottom = select_row; } else { s_bottom = current_row; s_top = select_row; } } void Fl_Table::set_selection(int s_top, int s_left, int s_bottom, int s_right) { damage_zone(current_row, current_col, select_row, select_col); current_col = s_left; current_row = s_top; select_col = s_right; select_row = s_bottom; damage_zone(current_row, current_col, select_row, select_col); } // Draw the entire Fl_Table // Override the draw() routine to draw the table. // Then tell the group to draw over us. // void Fl_Table::draw() { draw_cell(CONTEXT_STARTPAGE, 0, 0, // let user's drawing routine tix, tiy, tiw, tih); // prep new page // Let fltk widgets draw themselves first. Do this after // draw_cell(CONTEXT_STARTPAGE) in case user moves widgets around. // Use window 'inner' clip to prevent drawing into table border. // (unfortunately this clips FLTK's border, so we must draw it explicity below) // fl_push_clip(wix, wiy, wiw, wih); { Fl_Group::draw(); } fl_pop_clip(); // Explicitly draw border around widget, if any draw_box(box(), x(), y(), w(), h(), color()); // If Fl_Scroll 'table' is hidden, draw its box // Do this after Fl_Group::draw() so we draw over scrollbars // that leak around the border. // if ( ! table->visible() ) { if ( damage() & FL_DAMAGE_ALL || damage() & FL_DAMAGE_CHILD ) { draw_box(table->box(), tox, toy, tow, toh, table->color()); } } // Clip all further drawing to the inner widget dimensions fl_push_clip(wix, wiy, wiw, wih); { // Only redraw a few cells? if ( ! ( damage() & FL_DAMAGE_ALL ) && _redraw_leftcol != -1 ) { fl_push_clip(tix, tiy, tiw, tih); for ( int c = _redraw_leftcol; c <= _redraw_rightcol; c++ ) { for ( int r = _redraw_toprow; r <= _redraw_botrow; r++ ) { _redraw_cell(CONTEXT_CELL, r, c); } } fl_pop_clip(); } if ( damage() & FL_DAMAGE_ALL ) { int X,Y,W,H; // Draw row headers, if any if ( row_header() ) { get_bounds(CONTEXT_ROW_HEADER, X, Y, W, H); fl_push_clip(X,Y,W,H); for ( int r = toprow; r <= botrow; r++ ) { _redraw_cell(CONTEXT_ROW_HEADER, r, 0); } fl_pop_clip(); } // Draw column headers, if any if ( col_header() ) { get_bounds(CONTEXT_COL_HEADER, X, Y, W, H); fl_push_clip(X,Y,W,H); for ( int c = leftcol; c <= rightcol; c++ ) { _redraw_cell(CONTEXT_COL_HEADER, 0, c); } fl_pop_clip(); } // Draw all cells. // This includes cells partially obscured off edges of table. // No longer do this last; you might think it would be nice // to draw over dead zones, but on redraws it flickers. Avoid // drawing over deadzones; prevent deadzones by sizing columns. // fl_push_clip(tix, tiy, tiw, tih); { for ( int r = toprow; r <= botrow; r++ ) { for ( int c = leftcol; c <= rightcol; c++ ) { _redraw_cell(CONTEXT_CELL, r, c); } } } fl_pop_clip(); // Draw little rectangle in corner of headers if ( row_header() && col_header() ) { fl_rectf(wix, wiy, row_header_width(), col_header_height(), color()); } // Table has a boxtype? Close those few dead pixels if ( table->box() ) { if ( col_header() ) { fl_rectf(tox, wiy, Fl::box_dx(table->box()), col_header_height(), color()); } if ( row_header() ) { fl_rectf(wix, toy, row_header_width(), Fl::box_dx(table->box()), color()); } } // Table width smaller than window? Fill remainder with rectangle if ( table_w < tiw ) { fl_rectf(tix + table_w, tiy, tiw - table_w, tih, color()); // Col header? fill that too if ( col_header() ) { fl_rectf(tix + table_w, wiy, // get that corner just right.. (tiw - table_w + Fl::box_dw(table->box()) - Fl::box_dx(table->box())), col_header_height(), color()); } } // Table height smaller than window? Fill remainder with rectangle if ( table_h < tih ) { fl_rectf(tix, tiy + table_h, tiw, tih - table_h, color()); if ( row_header() ) { // NOTE: // Careful with that lower corner; don't use tih; when eg. // table->box(FL_THIN_UPFRAME) and hscrollbar hidden, // leaves a row of dead pixels. // fl_rectf(wix, tiy + table_h, row_header_width(), (wiy+wih) - (tiy+table_h) - ( hscrollbar->visible() ? SCROLLBAR_SIZE : 0), color()); } } } // Both scrollbars? Draw little box in lower right if ( vscrollbar->visible() && hscrollbar->visible() ) { fl_rectf(vscrollbar->x(), hscrollbar->y(), vscrollbar->w(), hscrollbar->h(), color()); } draw_cell(CONTEXT_ENDPAGE, 0, 0, // let user's drawing tix, tiy, tiw, tih); // routines cleanup _redraw_leftcol = _redraw_rightcol = _redraw_toprow = _redraw_botrow = -1; } fl_pop_clip(); } itksnap/Utilities/FLTK/Fl_Table/Fl_Table_Row.cxx0000644000076500000240000001755011401101412020743 0ustar paulystaff// // Fl_Table_Row -- A row oriented table widget // // A class specializing in a table of rows. // Handles row-specific selection behavior. // // Copyright 2002 by Greg Ercolano. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please report all bugs and problems to "erco at seriss dot com". // // // TODO: // o Row headings (only column headings supported currently) // #include // for debugging #include #include #include // Is row selected? int Fl_Table_Row::row_selected(int row) { if ( row < 0 || row >= rows() ) return(-1); return(_rowselect[row]); } // Change row selection type void Fl_Table_Row::type(TableRowSelectMode val) { _selectmode = val; switch ( _selectmode ) { case SELECT_NONE: { for ( int row=0; row 1 ) { // only one allowed _rowselect[row] = 0; } } } redraw(); break; } case SELECT_MULTI: break; } } // Change selection state for row // // flag: // 0 - clear selection // 1 - set selection // 2 - toggle selection // // Returns: // 0 - selection state did not change // 1 - selection state changed // -1 - row out of range or incorrect selection mode // int Fl_Table_Row::select_row(int row, int flag) { int ret = 0; if ( row < 0 || row >= rows() ) { return(-1); } switch ( _selectmode ) { case SELECT_NONE: return(-1); case SELECT_SINGLE: { int oldval; for ( int t=0; t= toprow && row <= botrow ) { // row visible? // Extend partial redraw range redraw_range(row, row, leftcol, rightcol); } ret = 1; } } } return(ret); } // Select all rows to a known state void Fl_Table_Row::select_all_rows(int flag) { switch ( _selectmode ) { case SELECT_NONE: return; case SELECT_SINGLE: if ( flag != 0 ) return; //FALLTHROUGH case SELECT_MULTI: { char changed = 0; if ( flag == 2 ) { for ( int row=0; row<(int)_rowselect.size(); row++ ) { _rowselect[row] ^= 1; } changed = 1; } else { for ( int row=0; row<(int)_rowselect.size(); row++ ) { changed |= (_rowselect[row] != flag)?1:0; _rowselect[row] = flag; } } if ( changed ) { redraw(); } } } } // Set number of rows void Fl_Table_Row::rows(int val) { Fl_Table::rows(val); while ( val > (int)_rowselect.size() ) { _rowselect.push_back(0); } // enlarge while ( val < (int)_rowselect.size() ) { _rowselect.pop_back(); } // shrink } // #include "eventnames.h" // debugging // #include // Handle events int Fl_Table_Row::handle(int event) { // fprintf(stderr, "** EVENT: %s: EVENT XY=%d,%d\n", // eventnames[event], Fl::event_x(), Fl::event_y()); // debugging // Let base class handle event int ret = Fl_Table::handle(event); // The following code disables cell selection.. why was it added? -erco 05/18/03 // if ( ret ) { _last_y = Fl::event_y(); return(1); } // base class 'handled' it (eg. column resize) int shiftstate = (Fl::event_state() & FL_CTRL) ? FL_CTRL : (Fl::event_state() & FL_SHIFT) ? FL_SHIFT : 0; // Which row/column are we over? int R, C; // row/column being worked on ResizeFlag resizeflag; // which resizing area are we over? (0=none) TableContext context = cursor2rowcol(R, C, resizeflag); switch ( event ) { case FL_PUSH: if ( Fl::event_button() == 1 ) { _last_push_x = Fl::event_x(); // save regardless of context _last_push_y = Fl::event_y(); // " " // Handle selection in table. // Select cell under cursor, and enable drag selection mode. // if ( context == CONTEXT_CELL ) { // Ctrl key? Toggle selection state switch ( shiftstate ) { case FL_CTRL: select_row(R, 2); // toggle break; case FL_SHIFT: { select_row(R, 1); if ( _last_row > -1 ) { int srow = R, erow = _last_row; if ( srow > erow ) { srow = _last_row; erow = R; } for ( int row = srow; row <= erow; row++ ) { select_row(row, 1); } } break; } default: select_all_rows(0); // clear all previous selections select_row(R, 1); break; } _last_row = R; _dragging_select = 1; ret = 1; // FL_PUSH handled (ensures FL_DRAG will be sent) // redraw(); // redraw() handled by select_row() } } break; case FL_DRAG: { if ( _dragging_select ) { // Dragged off table edges? Handle scrolling int offtop = toy - _last_y; // >0 if off top of table int offbot = _last_y - (toy + toh); // >0 if off bottom of table if ( offtop > 0 && row_position() > 0 ) { // Only scroll in upward direction int diff = _last_y - Fl::event_y(); if ( diff < 1 ) { ret = 1; break; } row_position(row_position() - diff); context = CONTEXT_CELL; C = 0; R = row_position(); // HACK: fake it if ( R < 0 || R > rows() ) { ret = 1; break; } // HACK: ugly } else if ( offbot > 0 && botrow < rows() ) { // Only scroll in downward direction int diff = Fl::event_y() - _last_y; if ( diff < 1 ) { ret = 1; break; } row_position(row_position() + diff); context = CONTEXT_CELL; C = 0; R = botrow; // HACK: fake it if ( R < 0 || R > rows() ) { ret = 1; break; } // HACK: ugly } if ( context == CONTEXT_CELL ) { switch ( shiftstate ) { case FL_CTRL: if ( R != _last_row ) { // toggle if dragged to new row select_row(R, 2); // 2=toggle } break; case FL_SHIFT: default: select_row(R, 1); if ( _last_row > -1 ) { int srow = R, erow = _last_row; if ( srow > erow ) { srow = _last_row; erow = R; } for ( int row = srow; row <= erow; row++ ) { select_row(row, 1); } } break; } ret = 1; // drag handled _last_row = R; } } break; } case FL_RELEASE: if ( Fl::event_button() == 1 ) { _dragging_select = 0; ret = 1; // release handled // Clicked off edges of data table? // A way for user to clear the current selection. // int databot = tiy + table_h, dataright = tix + table_w; if ( ( _last_push_x > dataright && Fl::event_x() > dataright ) || ( _last_push_y > databot && Fl::event_y() > databot ) ) { select_all_rows(0); // clear previous selections } } break; default: break; } _last_y = Fl::event_y(); return(ret); } itksnap/Utilities/FLTK/Fl_Table/README0000644000076500000240000000616011401101412016573 0ustar paulystaffFl_Table -- FLTK Table Widget ----------------------------- WHAT IS Fl_Table? Fl_Table is a low level widget intended to help people design higher level widgets, and is aimed at becoming part of FLTK's core. Fl_Table is a low level widget that helps widget designers (or self motivated app designers) derive their own higher level widgets to do what they need. Fl_Table can be used to implement many table-like widgets; a 'spreadsheet', a two dimensional table browser, an invoice generator. Anything where resizable rows and columns with optional row/column headers are desireable. LICENSING Fl_Table comes with complete free source code. Fl_Table is available under the terms of the GNU Library General Public License. See COPYING for more info. Contrary to popular belief, it can be used in commercial software! BUILD INSTRUCTIONS Tested under Linux, Mac OSX and Windows with fltk 1.3.x. To build the test programs and related Fl_Table object files: make These test programs are created: testtablerow -- Simple row/column test. Scroll around, resize the columns. exercisetablerow -- Exercises widget options to check for bugs. sortapp -- Sample app that loads output of an 'ls' listing, and lets you toggle-sort the columns by clicking on the column headings. widgettable -- Exercises ability to embed fltk widgets in table singleinput -- Shows Mr. Satan's trick to use Fl_Input effectively in Fl_Table, with only a single instance of the widget. UTF8 SUPPORT FLTK officially started supporting UTF8 in 1.3.0, and the default Makefile should work with that OK. For some years before 1.3.x, there was an FLTK utf8 'patch' that was used quite a bit.. to make that work you may need to manually add the -lXutf8 link option, as there was a separate Xutf8 lib. WHERE'S THE DOCUMENTATION? See documentation/index.html. FEATURES Currently the lib supports: o A full on X/Y table o Column headers (that can be resized, and disabled) o An Fl_Table_Row widget that specializes in row selection behavior o Complete custom drawing behavior in cells o Ctrl-selection and Shift-selection, and drag selection o Callback mechanism for when mouse events occur in table (sortapp uses this to do sorting when column header clicked) o No limit on table size (or well, there 'shouldn't' be one) o Can act as a container for fltk widgets or custom widgets RELEASE NOTES/VERSION INFORMATION See ./CHANGES. LIMITATIONS Known bugs: o 'sortapp' is not sorting the selection states. Probably move this into the 'Row' class somehow? Problem in demo only, not the core lib. Currently *lacks* the following, but will be in there RSN: o User control of scrollbar visibility and selection behavior Not sure when I'll get to these: o No concept of a 'current cell' or 'cell cursor' o Keyboard navigation of cell selection o Footers (row or column) BUGS? Send them to erco at seriss dot com itksnap/Utilities/FLTK/Fl_Table/TODO0000644000076500000240000000714011401101412016402 0ustar paulystaff> DOXYGEN the documentation > BUG: in testtablerow, mouse wheel does not scroll lower window if mouse is inside it, but PGUP/DOWN work ok. ??? > Bring over changes from RUSH: o (DONE) is_interactive_resize() o something else I think??? > exercisetablerow: Lots of messages on startup.. WHY?? 'Demo' callback: Row=497 Col=0 Context=7 Event=0 'Demo' callback: Row=498 Col=0 Context=7 Event=0 'Demo' callback: Row=499 Col=0 Context=7 Event=0 'Demo' callback: Row=0 Col=0 Context=0 Event=1 'Demo' callback: Row=0 Col=0 Context=0 Event=1 > exercisetable(): add a prompt for changing 'when()' via a pulldown. > Add a Right click popup menu, and see how that would be implemented using new callback() system. > Add an any_selected() method to Fl_Table_Row(), so one can easily determine if any rows are selected. > TODO: (see 3.10 Jean Marc additions) Need to make mouse region selection controllable via 'or'able flags: > Single cell (SELECT_CELL) > Row of cells (eg. Fl_Browser, Fl_Table_Row) (SELECT_ROW) > Column of cells (SELECT_COL) > Freeform row/col (current behavior) (SELECT_MULTI) > Autoscrolling is a hack. Need finer control of auto-scrolling, based on proximity to the table edge, like normal tty scrolling. > Don't show resize cursor when over header for left edge of cell#0, or top edge of header for row#0. > Fl_Table_Row: A way to pick different select modes; none 1 of n (file browser) multi See Fl_Browser_ > Keyboard navigation of a 'current cell' or 'current row' using Tab and Shift Tab, Arrows, Spacebar, etc. Again, see Fl_Browser. Needs a focus box. > fltk-config Greg Ercolano wrote: > Does fltk-config somehow deal with the issue of having > multiple FLTK dirs installed on a machine? Yes, through the configured directories... > I just started playing with fltk-config this morning, > and noticed that it reports paths like /usr/local/lib > even though I didn't run 'make install'. Easy fix: provide your source directories when configuring, e.g.: ./configure --prefix=/usr/local/src/fltk-M.m.p \ --includedir=/usr/local/src/fltk-M.m.p > In FLTK 2.0, there's some way to avoid having the parent draw the rectangle over the table. > Implement a way the derived class can disable/draw their own dead zones. Do this for ALL dead zones. CONTEXT_DEADZONE_TABLE CONTEXT_DEADZONE_HEADER CONTEXT_DEADZONE_SCROLLBAR > Need to override /all/ Fl_Group methods, eg: add() add_resizable() find() insert() remove() > optimize for fixed rowh/colw > If has_fltk_children(), don't call the draw_cell() stuff? > Add row_margin/col_margin() to change overall spacing between cells. Esp for use with fltk container. Also, gets rid of need for user to handle those little edge lines in custom widgets ---------------------------------------------------------------- > (3.0) Document all Fl_Table_Row functions. > (3.0) Fl_Table_Row: If you make a selection, but drag off the end of the last row, you loose the selection. > (3.0) When resized very small, horiz scroll bar clips OUTSIDE the border instead of INSIDE. Make sure when scroll bars drawn, they are clipped to the INNER boarder. > (3.0) Implement callback() stuff. Try to follow Fl_Browser as closely as possible. For keyboard navigation, and probably right-click stiuff too to bring up menus. > (3.0) Don't scroll if resizing. > (3.1) HEY!! When you click to resize a column, it ALSO sends a NON-resize event to the callback(). MASK THEM! > (3.1) col_width() or row_height() should probably invoke user callback() with a CONTEXT_RC_RESIZE. itksnap/Utilities/Forwarding/0000755000076500000240000000000011560342171015621 5ustar paulystaffitksnap/Utilities/Forwarding/CMakeLists.txt0000644000076500000240000000135511111163100020346 0ustar paulystaffPROJECT(SNAP_LAUNCHER) # -- EXPERIMENTAL -- IF(NOT WIN32 AND NOT APPLE) SET(SNAP_EXE_INSTALL ${SNAP_MAIN_INSTALL_DIR}) SET(SNAP_FORWARD_DIR_BUILD "${CMAKE_BINARY_DIR}/bin") SET(SNAP_FORWARD_DIR_INSTALL "../${SNAP_EXE_INSTALL}") SET(SNAP_FORWARD_PATH_BUILD "\"${SNAP_FORWARD_DIR_BUILD}\"") SET(SNAP_FORWARD_PATH_INSTALL "\"${SNAP_FORWARD_DIR_INSTALL}\"") SET(SNAP_FORWARD_EXE ${SNAP_EXE}) CONFIGURE_FILE( ${SNAP_SOURCE_DIR}/Utilities/Forwarding/SharedForwardExe.c.in ${CMAKE_CURRENT_BINARY_DIR}/shared-forward.c @ONLY IMMEDIATE) ADD_EXECUTABLE(itksnap ${CMAKE_CURRENT_BINARY_DIR}/shared-forward.c) ADD_DEPENDENCIES(itksnap ${SNAP_EXE}) INSTALL(TARGETS itksnap RUNTIME DESTINATION bin) ENDIF(NOT WIN32 AND NOT APPLE) itksnap/Utilities/Forwarding/CVS/0000755000076500000240000000000011560342171016254 5ustar paulystaffitksnap/Utilities/Forwarding/CVS/Entries0000644000076500000240000000014711560342171017612 0ustar paulystaff/CMakeLists.txt/1.5/Thu Nov 20 04:23:28 2008// /SharedForwardExe.c.in/1.2/Mon Dec 10 01:25:39 2007// D itksnap/Utilities/Forwarding/CVS/Repository0000644000076500000240000000003511560342171020354 0ustar paulystaffitksnap/Utilities/Forwarding itksnap/Utilities/Forwarding/CVS/Root0000644000076500000240000000010011560342171017111 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/Forwarding/SharedForwardExe.c.in0000644000076500000240000000165110727112623021573 0ustar paulystaff #if defined(CMAKE_INTDIR) #define CONFIG_DIR_PRE CMAKE_INTDIR "/" #define CONFIG_DIR_POST "/" CMAKE_INTDIR #else #define CONFIG_DIR_PRE "" #define CONFIG_DIR_POST "" #endif #define itksys_SHARED_FORWARD_DIR_BUILD "@SNAP_FORWARD_DIR_BUILD@" #define itksys_SHARED_FORWARD_PATH_BUILD @SNAP_FORWARD_PATH_BUILD@ #define itksys_SHARED_FORWARD_PATH_INSTALL @SNAP_FORWARD_PATH_INSTALL@ #define itksys_SHARED_FORWARD_EXE_BUILD "@SNAP_FORWARD_DIR_BUILD@/@SNAP_FORWARD_EXE@" #define itksys_SHARED_FORWARD_EXE_INSTALL "@SNAP_FORWARD_DIR_INSTALL@/@SNAP_FORWARD_EXE@" #define itksys_SHARED_FORWARD_OPTION_COMMAND "--command" #define itksys_SHARED_FORWARD_OPTION_PRINT "--print" #define itksys_SHARED_FORWARD_OPTION_LDD "--ldd" #if defined(CMAKE_INTDIR) #define itksys_SHARED_FORWARD_CONFIG_NAME CMAKE_INTDIR #endif #include int main(int argc, char** argv) { return itksys_shared_forward_to_real(argc, argv); } itksnap/Utilities/InnoSetup/0000755000076500000240000000000011560342171015443 5ustar paulystaffitksnap/Utilities/InnoSetup/CVS/0000755000076500000240000000000011560342171016076 5ustar paulystaffitksnap/Utilities/InnoSetup/CVS/Entries0000644000076500000240000000014411560342171017431 0ustar paulystaff/InstallSNAP.iss.in/1.3/Wed Dec 6 13:27:46 2006// /itksnap.ico/1.1/Sat Dec 2 04:22:27 2006/-kb/ D itksnap/Utilities/InnoSetup/CVS/Repository0000644000076500000240000000003411560342171020175 0ustar paulystaffitksnap/Utilities/InnoSetup itksnap/Utilities/InnoSetup/CVS/Root0000644000076500000240000000010011560342171016733 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/InnoSetup/InstallSNAP.iss.in0000644000076500000240000000432410535542522020666 0ustar paulystaff; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! [Setup] AppName=InsightSNAP AppVerName=InsightSNAP 1.4.1 AppPublisher=itksnap.org AppPublisherURL=http://www.itksnap.org AppSupportURL=http://www.itksnap.org/bb AppUpdatesURL=http://www.itksnap.org/download/snap DefaultDirName={pf}\InsightApplications\SNAP DefaultGroupName=InsightSNAP AllowNoIcons=yes [Tasks] Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:" Name: "quicklaunchicon"; Description: "Create a &Quick Launch icon"; GroupDescription: "Additional icons:"; Flags: unchecked [Files] Source: "@SNAP_BINARY_DIR@\Release\InsightSNAP.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "@SNAP_BINARY_DIR@\ProgramData\*.*"; DestDir: "{app}\ProgramData"; Flags: ignoreversion recursesubdirs Source: "c:\windows\system32\msvcp71.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "c:\windows\system32\msvcr71.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "@SNAP_SOURCE_DIR@\Utilities\InnoSetup\itksnap.ico"; DestDir: "{app}"; Flags: ignoreversion ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [INI] Filename: "{app}\SNAP.url"; Section: "InternetShortcut"; Key: "URL"; String: "http://www.itksnap.org" [InstallDelete] ; Type: filesandordirs; Name: "{userappdata}\itk.org" Type: files; Name: "{app}\SNAPProgramDataDirectory.txt" Type: filesandordirs; Name: "{app}\HTMLHelp" Type: filesandordirs; Name: "{app}\Images2D" Type: filesandordirs; Name: "{app}\Presets" [Icons] Name: "{group}\InsightSNAP"; Filename: "{app}\InsightSNAP.exe"; Comment: "ITK-SNAP 3D Image Segmentation Tool"; IconFilename: "{app}\itksnap.ico" Name: "{group}\InsightSNAP on the Web"; Filename: "{app}\SNAP.url" Name: "{group}\Uninstall InsightSNAP"; Filename: "{uninstallexe}" Name: "{userdesktop}\InsightSNAP"; Filename: "{app}\InsightSNAP.exe"; Tasks: desktopicon Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\InsightSNAP"; Filename: "{app}\InsightSNAP.exe"; Tasks: quicklaunchicon [Run] Filename: "{app}\InsightSNAP.exe"; Description: "Launch InsightSNAP"; Flags: nowait postinstall skipifsilent [UninstallDelete] Type: files; Name: "{app}\SNAP.url" itksnap/Utilities/InnoSetup/itksnap.ico0000644000076500000240000002635610534177603017632 0ustar paulystaff006  h)(0`ЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮٶ޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺ٶܸܸٶ޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺ٶֳܸܸܸܸܸܸܸܸܸܸܸܸܸܸܸܸέέΥƥέֳܸܸܸܸܸܸܸܸܸܸܸܸܸܸܸܸܸӱٶٶٶٶٶٶٶٶٶٶٶٶٶٶٶٶssZ11)11)11)JJBƥٶٶٶٶٶٶٶٶٶٶٶٶٶٶٶٶӱЮֳֳֳֳֳֳֳֳֳֳֳֳֳֳֳΥ{{ksֳֳֳֳֳֳֳֳֳֳֳֳֳֳֳֳЮ̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ!!ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ991kkkZks֭ӱӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱƽ{{kZZJRRJBB1))!))!11)RRBRRBBB1{{k֭ӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱέk))!!!91)RRBkcRs{ӱέ{!!!{{cֵӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱέZZJ)!!91)JJ911)!!))!11)11)!!!sscƥӱ{{cӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱssssss11)Υ))!))!JJBέsӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ{{ӱ{scֵBB1ccRӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱJJB!!!έ91)sscӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ991ӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱέsRRJӱӱӱssc!!ƥӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱZRJ))!ZZJӱӱ))!ZZJӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱέ91)11)sΥӱ{sckkZ11)RRJӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱ)!!ӱӱӱBB1ccRέӱӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱsJJ9ӱӱB91ӱӱӱӱӱӱӱέέӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱkRRBӱ!!!RRBӱӱӱӱӱӱӱccRkkZӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱZRJRRBӱ!!sӱ{sRRJRRBZZJsZRJέӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱkcR91)έέJJB11)))!{kkZ))!JB9BB1!!!JB9sƥӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱƽ!!kӱƥ{sc11)91)11)991{{kJB9))!ZZJBB1JJBֵӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱ{sc!!sֵӱέƥέέӱӱέӱkZRJӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱέkcR{scƥέƥέӱӱӱӱӱ֭sscRRBӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱέs)!!11)11)11)11){{kƥӱέZZJ11)sӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱΥZZJJJBJJB11)JJ911))!!RRBӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ{)!!!!sscccRZRJkέӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱέB91kkRkkZ91)ƥӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱZRJ91){ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱέӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ̫ťЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮ̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫̫ɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨɨťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťťٶЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮЮ̫޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ( @ ЮЮЮЮЮЮЮЮЮЮЮέέΥƥέЮЮЮЮЮЮЮЮЮЮЮЮٶ޺޺޺޺޺޺޺޺޺޺޺ssZ11)11)11)JJBƥ޺޺޺޺޺޺޺޺޺޺޺ٶܸΥ{{ksܸٶ޺޺޺޺޺޺޺޺޺޺ֳ!!޺޺޺޺޺޺޺޺޺޺޺ٶֳܸܸܸܸܸܸܸܸܸܸ991kkkZksֳܸܸܸܸܸܸܸܸ֭ӱٶٶٶٶٶٶƽ{{kZZJRRJBB1))!))!11)RRBRRBBB1{{k֭ٶٶٶٶٶٶٶӱЮֳֳֳֳέk))!!!91)RRBkcRs{ֳέ{!!!{{cֳֳֳֳֳֳֵЮ̫ӱӱӱέZZJ)!!91)JJ911)!!))!11)11)!!!sscƥֳ{{cӱӱӱӱӱӱ̫̫ӱӱӱӱssssss11)Υ))!))!JJBέsӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱ{{ֳ{scֵBB1ccRӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱֳJJB!!!έ91)sscӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱӱӱӱֳ991ֳӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱӱέsRRJֳֳֳssc!!ƥӱӱӱӱӱӱ̫̫ӱӱӱӱӱӱZRJ))!ZZJֳֳ))!ZZJӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱέ91)11)sΥֳ{sckkZ11)RRJӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱ)!!ֳֳֳBB1ccRέӱӱӱӱӱӱӱӱ̫̫ӱӱӱӱӱsJJ9ֳֳB91ֳӱӱӱӱӱӱέӱӱӱӱ̫̫ӱӱӱӱӱkRRBֳ!!!RRBֳӱӱӱӱӱӱccRӱӱӱӱ̫̫ӱӱӱӱӱZRJRRBֳ!!sֳ{sRRJRRBZZJsӱӱӱӱ̫̫ӱӱӱӱӱkcR91)έJJB11)))!{kkZ))!JB9BB1!!!JB9ӱӱӱӱ̫̫ӱӱӱӱӱƽ!!kֳƥ{sc11)91)11)991{{kJB9))!ZZJBB1JJBֵӱӱӱӱ̫̫ӱӱӱӱӱӱ{sc!!sֳֵέƥέέֳֳέֳkZRJӱӱӱӱӱ̫̫ӱӱӱӱӱӱέkcR{scƥέƥέֳֳֳֳֳ֭sscRRBӱӱӱӱӱ̫ťЮЮЮЮЮЮЮέs)!!11)11)11)11){{kƥֳέZZJ11)sЮЮЮЮЮЮ̫̫̫̫̫̫̫̫̫ΥZZJJJBJJB11)JJ911))!!RRB̫̫̫̫̫̫̫ɨɨɨɨɨɨɨɨɨɨ{)!!!!sscccRZRJkέɨɨɨɨɨɨɨɨɨťťťťťťťťťťέB91kkRkkZ91)ƥťťťťťťťťťťťťťٶЮЮЮЮЮЮЮЮЮЮЮZRJ91){ЮЮЮЮЮЮЮЮЮЮЮЮЮЮ̫޺޺޺޺޺޺޺޺޺޺޺޺޺έ޺޺޺޺޺޺޺޺޺޺޺޺޺޺޺ӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱӱ( @ЮЮЮЮЮЮЮЮЮЮЮЮٶ޺޺׺Ż׻޺޺ٶܸrumV%m%rټܸ̫ӱw:um6Y%rźӱ̫̫ӱౘrӱĴR5Uĺӱ̫̫ӱڱ䅳R%rηӱ̫̫ӱӱӱdUum5ӱӱ̫̫ӱӱ2yum5Ǫ׭ӳӱ̫̫ӱڱ:um5uRRrӴӱ̫̫ӱڱrWu2rR3Ӵӱ̫̫ӱӱF%rĦӳӱ̫ٶЮЮخмЮЮЮ̫޺޺޺޺޺޺޺޺޺޺޺޺޺ӱӱӱӱӱӱӱӱӱӱӱӱitksnap/Utilities/Linux/0000755000076500000240000000000011560342171014616 5ustar paulystaffitksnap/Utilities/Linux/CVS/0000755000076500000240000000000011560342171015251 5ustar paulystaffitksnap/Utilities/Linux/CVS/Entries0000644000076500000240000000005511560342171016605 0ustar paulystaff/getsnap.sh/1.8/Wed Oct 20 15:36:42 2010// D itksnap/Utilities/Linux/CVS/Repository0000644000076500000240000000003011560342171017344 0ustar paulystaffitksnap/Utilities/Linux itksnap/Utilities/Linux/CVS/Root0000644000076500000240000000010011560342171016106 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/Linux/getsnap.sh0000644000076500000240000002066711457606212016632 0ustar paulystaff#!/bin/bash # ************************************************************* # SNAP UNIX/LINUX INSTALL SCRIPT - READ CAREFULLY # ************************************************************* # This script will download and build SNAP and all the # supporting software. In the ideal world, all you have to # do is wait for an hour or so for this script to do all the # work. # # However, you must have gmake, gcc, cvs and svn (subversion) # installed on your system. You also need to have an opengl # driver. If SNAP behaves erratically and crashes, it may be # due to a bad GL driver. # # The script will require almost 500MB of space. You can delete # all files except those in the 'install' directory. However, # if you keep the files, you will be able to update SNAP quickly # by running the script again. # # This script downloads ITK, VTK, FLTK and CMAKE. If you have # these programs already, you can modify the script to include # them. Beware that the build flags must match those used in # this script. # # Please report build problems to itksnap-users Google Group # # Good Luck!!! # Paul Y. # ************************************************************* echo "This script is out of date and no longer supported in ITK-SNAP 2.x" exit # Uncomment the line below to debug ### set -x -e # ------------------------------------------------------------- # Options that the user can supply # ------------------------------------------------------------- # The directory where the build will take place basedir="`pwd`/itksnap_build" # The installation directory, defaults to $basedir/install; # SNAP executable goes here (in bin/itksnap) instdir=$basedir/install # The location of the build log file logfile=$basedir/buildlog # Release versions for the supporting software. Change to HEAD to get # the most current version of the code CMAKE_REL="CMake-2-4-7" ITK_REL="ITK-3-2" VTK_REL="VTK-5-0-3" SNAP_REL="HEAD" # ------------------------------------------------------------- # Check for necessary software # ------------------------------------------------------------- for prog in "cvs" "svn" "g++" "gcc" "autoconf" "make" do $prog --version > /dev/null if [ $? -ne 0 ] then echo "Can not execute $prog on your system" echo "Please ask the administrator to install $prog and to " echo "make sure that $prog is in your path" exit fi done # ------------------------------------------------------------- # Initialization # ------------------------------------------------------------- echo "PLEASE READ THE COMMENTS AT THE TOP OF THIS SCRIPT!!!" echo "Preparing to build itk-snap. This will take 1-2 hours and 500MB!" echo "Log messages are placed into $logfile" # Create the directory tree for the build for dir in "$basedir/itksnap/bingcc" \ "$basedir/itk/bingcc" \ "$basedir/vtk/bingcc" \ "$basedir/fltk/bingcc" \ "$basedir/cmake/install" \ $instdir do mkdir -p $dir if [ $? -ne 0 ]; then exit; fi done # Create a file for CMAKE password entries echo "/1 :pserver:anonymous@www.cmake.org:2401/cvsroot/CMake Ah%y]d" > $basedir/.cvspass echo "/1 :pserver:anonymous@public.kitware.com:2401/cvsroot/VTK A<,]" >> $basedir/.cvspass echo "/1 :pserver:anonymous@www.itk.org:2401/cvsroot/Insight A?=Z?Ic," >> $basedir/.cvspass export CVS_PASSFILE=$basedir/.cvspass # Set some variables cmakebin=$basedir/cmake/install/bin/cmake # ------------------------------------------------------------- # Check out and build cmake # ------------------------------------------------------------- function get_cmake { cd $basedir/cmake # Use CVS to check out the right version echo "Checking out CMake (Release ${CMAKE_REL}) from CVS" cvs -qd :pserver:anonymous@www.cmake.org:/cvsroot/CMake co -r ${CMAKE_REL} cmake > $logfile # Build cmake echo "Building CMake" cd $basedir/cmake/CMake ./configure --prefix=$basedir/cmake/install >> $logfile make >> $logfile make install >> $logfile # Test to see if everything is ok if [ ! -x $cmakebin ] then echo "Failed to create CMake executable" exit -1 fi } # ------------------------------------------------------------- # Check out and build FLTK # ------------------------------------------------------------- function get_fltk { cd $basedir/fltk # Use SVN to check out fltk echo "Checking out FLTK 1.1.7 from SVN" svn co http://svn.easysw.com/public/fltk/fltk/tags/release-1.1.7/ fltk-1.1.7 >> $logfile # Run CCMAKE in the FLTK bin directory echo "Building FLTK" cd $basedir/fltk/bingcc $cmakebin \ -DBUILD_EXAMPLES:BOOL=OFF \ -DBUILD_TESTING:BOOL=ON \ -DCMAKE_BUILD_TYPE:STRING=Release \ -DCMAKE_CXX_FLAGS_RELEASE:STRING="-O3 -DNDEBUG" \ $basedir/fltk/fltk-1.1.7 >> $logfile make >> $logfile # Make sure that we have the necessary files for file in "bin/fluid" "bin/libfltk.a" do if [ ! -e $basedir/fltk/bingcc/$file ] then echo "Failed to create FLTK object $basedir/fltk/bingcc/$file" exit -1 fi done } # ------------------------------------------------------------- # Check out and build VTK # ------------------------------------------------------------- function get_vtk { cd $basedir/vtk # Use CVS to check out VTK echo "Checking out VTK (Release ${VTK_REL}) from CVS" cvs -qd :pserver:anonymous@public.kitware.com:/cvsroot/VTK co -r ${VTK_REL} VTK >> $logfile # Configure VTK using CMake echo "Building VTK" cd $basedir/vtk/bingcc $cmakebin \ -DBUILD_EXAMPLES:BOOL=OFF \ -DBUILD_TESTING:BOOL=OFF \ -DCMAKE_BUILD_TYPE:STRING=Release \ -DVTK_USE_HYBRID:BOOL=ON \ -DVTK_USE_ANSI_STDLIB:BOOL=ON \ -DVTK_USE_PARALLEL:BOOL=ON \ -DVTK_USE_RENDERING:BOOL=ON \ -DVTK_USE_PATENTED:BOOL=ON \ -DCMAKE_CXX_FLAGS_RELEASE:STRING="-O3 -DNDEBUG" \ $basedir/vtk/VTK >> $logfile make >> $logfile # Check whether the necessary libraries built for lib in "Common" "IO" "Graphics" "Imaging" "Filtering" do if [ ! -e $basedir/vtk/bingcc/bin/libvtk${lib}.a ] then echo "VTK library $basedir/vtk/bingcc/bin/libvtk${lib}.a failed to build!" exit -1 fi done } # ------------------------------------------------------------- # Check out and build ITK # ------------------------------------------------------------- function get_itk { cd $basedir/itk # Use CVS to check out VTK echo "Checking out ITK (Release ${ITK_REL}) from CVS" cvs -qd :pserver:anonymous@www.itk.org:/cvsroot/Insight co -r ${ITK_REL} Insight >> $logfile # Configure VTK using CMake echo "Building ITK" cd $basedir/itk/bingcc $cmakebin \ -DBUILD_EXAMPLES:BOOL=OFF \ -DBUILD_TESTING:BOOL=OFF \ -DCMAKE_BUILD_TYPE:STRING=Release \ -DCMAKE_CXX_FLAGS_RELEASE:STRING="-O3 -DNDEBUG" \ $basedir/itk/Insight >> $logfile make >> $logfile # Check whether the necessary libraries built for lib in "Common" "IO" "BasicFilters" "Algorithms" do if [ ! -e $basedir/itk/bingcc/bin/libITK${lib}.a ] then echo "ITK library $basedir/itk/bingcc/bin/libITK${lib}.a failed to build!" exit -1 fi done } # ------------------------------------------------------------- # Check out and build ITK-SNAP # ------------------------------------------------------------- function get_itksnap { cd $basedir/itksnap # Use CVS to check out InsightApplications echo "Checking out ITK-SNAP (Release $SNAP_REL) from CVS" # cvs -qd :pserver:anonymous@www.itk.org:/cvsroot/Insight co -r $SNAP_REL InsightApplications >> $logfile cvs -qd :pserver:itk-snap.cvs.sourceforge.net/cvsroot/itk-snap co -r $SNAP_REL itksnap # Configure VTK using CMake echo "Building SNAP" cd $basedir/itksnap/bingcc $cmakebin \ -DBUILD_EXAMPLES:BOOL=OFF \ -DBUILD_TESTING:BOOL=OFF \ -DCMAKE_BUILD_TYPE:STRING=Release \ -DCMAKE_CXX_FLAGS_RELEASE:STRING="-O3 -DNDEBUG" \ -DUSE_ITK:BOOL=ON \ -DUSE_VTK:BOOL=ON \ -DUSE_FLTK:BOOL=ON \ -DITK_DIR:PATH="$basedir/itk/bingcc" \ -DVTK_DIR:PATH="$basedir/vtk/bingcc" \ -DFLTK_DIR:PATH="$basedir/fltk/bingcc" \ -DCMAKE_INSTALL_PREFIX:PATH=$instdir \ $basedir/itksnap/itksnap >> $logfile # Make only in the SNAP directory make >> $logfile make install >> $logfile if [ ! -f $instdir/bin/InsightSNAP ] then echo "SNAP failed to build in $instdir/bin/InsightSNAP" exit -1; fi } # ------------------------------------------------------------- # Perform the actual build tasks # ------------------------------------------------------------- get_cmake get_itk get_fltk get_vtk get_itksnap echo "SNAP executable is located in $instdir/bin/InsightSNAP!" itksnap/Utilities/MacOS/0000755000076500000240000000000011560342171014461 5ustar paulystaffitksnap/Utilities/MacOS/BundleResources/0000755000076500000240000000000011560342171017565 5ustar paulystaffitksnap/Utilities/MacOS/BundleResources/CVS/0000755000076500000240000000000011560342171020220 5ustar paulystaffitksnap/Utilities/MacOS/BundleResources/CVS/Entries0000644000076500000240000000013211560342171021550 0ustar paulystaff/Info.plist/1.2/Fri Apr 16 04:02:35 2010// /itksnap.icns/1.1/Sun Feb 17 21:02:16 2008// D itksnap/Utilities/MacOS/BundleResources/CVS/Repository0000644000076500000240000000005011560342171022315 0ustar paulystaffitksnap/Utilities/MacOS/BundleResources itksnap/Utilities/MacOS/BundleResources/CVS/Root0000644000076500000240000000010011560342171021055 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap itksnap/Utilities/MacOS/BundleResources/Info.plist0000644000076500000240000000454211361760533021547 0ustar paulystaff CFBundleExecutable InsightSNAP CFBundleIconFile itksnap CFBundleIdentifier org.itksnap CFBundlePackageType APPL CFBundleName ITK-SNAP CFBundleDisplayName ITK-SNAP CFBundleVersion 59 CFBundleShortVersionString @SNAP_VERSION_FULL@ CFBundleSignature itksnap CFBundleDocumentTypes CFBundleTypeName NIfTI File CFBundleTypeRole Viewer CFBundleTypeExtensions nii CFBundleTypeName Analyze Header File CFBundleTypeRole Viewer CFBundleTypeExtensions hdr CFBundleTypeName Analyze Image File CFBundleTypeRole Viewer CFBundleTypeExtensions img CFBundleTypeName MetaImage File CFBundleTypeRole Viewer CFBundleTypeExtensions mha mhd CFBundleTypeName GIPL Image File CFBundleTypeRole Viewer CFBundleTypeExtensions gipl CFBundleTypeName VTK Image or Data File CFBundleTypeRole Viewer CFBundleTypeExtensions vtk CFBundleTypeName Compressed File CFBundleTypeRole Viewer CFBundleTypeExtensions gz itksnap/Utilities/MacOS/BundleResources/itksnap.icns0000644000076500000240000041136410756120330022122 0ustar paulystafficnsis32݄g߽÷ƿÿķƷʷŵÿ0ݽidƾõƱØʛf"i;ýÖN(M˯ccƿČ^gݽjeƾôűɚg"i¿͖Q,NʲdeÍ^s8mkil32߁ނD߹ùžþĿȺɼ¾õ¿Ŀ¾Ʊм¿¿ÿ๼ü¶ĽſƭŜ­ĴʵŸߺĺʮɝ_ʍc4Nj\/¾³ãdēGA̼ǹuȰmƥvI;2qȺĿžýƶŰ09¾ʼʾý-O0*ºúɢ2rVD¶ú·ܥVɾŹ÷ǿǼϻŽſʵdz{5ƸY7sW=aϣߺĺʭɝ`ʍd5Nj]/¾³ãeēG@˽1§¾ȻvȰmƧwL<1pȺļƿ¾ðĮ2;Ȼÿξý.W5*׹Żݺξ̥4vVɷXJų4ŷY޽ϾȱɽsW=aϣl8mkit32G¿ ¿#¿»Ŀſ]Ŀ¹ſÿÀX€)þǾ¼ÿĀRüþýÿƲþ€Ⱦƻռ߶¿ۚĿύȸ$ſȿŽƀȾŽĺѢĿ¾Ľ»ž¼¿طѩ9̦ȼÿĿÿĀĸijſ€d¾ģ¿ýϳƽЫ¿ÿ¿ŀŸµýŻþůǥѯͿÏݽ…|ظÿƽ¾ƀdĶþƽ¾ĴÀúɾ¿IɘƳ}¼gǿȨŽ¿ïĿǶǵ¿ȬüÿþɎ̻ĀTþŀƺ¿ſz¿¿ÿôĀ9¿ƮƼĸ¾ÿƴƺĀ:¿ƨ­¾Ĺ€9ƽƿ¿֣€|½˦¾̱纮ĿƬõ`Ŀż«zǗ޾Żü€\Ψ¿ƽ¾żʵ¾Ķĸluڮ¿ƿĵ˾€ľŀĦƿÿ€TĻɲϾڍ]vʥſdz¿ƵÀĀIĺþøɷٺ`xݩƀƀſā>óļju˧ŀoĻǿºïƻô軍բĀº¾ýýƼǾ¾ĽÿĵάſƥÿǿǺ¾½¼¿Ŀ󺞹¾¼ľĸþý€<ľļüûżƿ·ŀ>ijљŶĽĿþÀyƿ¹»ÿýĻɑϡȽ˭ƿ¿ſ€€ÿŀTvtuᨦ뛹ľǹĿ €ƀÀŀ̳hٷgh}ËsÿzzŬŸťżƆ}ѯÿՋž½qmjþpüaltrf^^ƍSuƭ¸gh`|ƃB?_tezėz§¾Ö|rr|w¿Ēowgqpojl[IJƣ}vue¾{]Uc~&Ǚuŕò°ywozÀ½uw|ÙiİÖ{mȁdso~ơe}þ~Ôsžúþƒў[:éÿõmú¿ĽũğƤsǖy»ôáƻDZ퉣ʻ¿¶¥ŴƼ±½ö¿¿ȵ½iþd[ǻ9¾ļ̼݄rſľVāŮɋÀ¿ƽ½Ŀ¸ɛÿ÷ÿ¹ޤ+»€iºƻĿȿ㽚þ¿Ŀ7¿¿ƀG½÷Ƹļµ泼½ Ā&*DZƮDZ¾ͩƾĀĀ¿¿ĀŵżǽŸúÿ¿¿¿¿ŷ¿¿ſƀ¿ ¿#¿»ĿÀſGĿÿ¹ſĀþÀX€)þĬÿĀRÂþĿмq}ήiÿƲþ€;*ɫNǽÏDUȶ~7+ǯp-;ɽȸB$ȾÿʹvȽʫ' [ľĤ\="%lѿá{Q7$ʼĽ»5VḊ5.˺zþѲY3*>F)ÁΚM0.:<7,oȊC//<=5' Ŀÿ/ȾCQŪ[%}o!Cÿb39/F8x<]6::7 S}ÿ¿ŀʪSǯ´!M]hU #ɿ̃ 4FQ-$eAUR#3$ȹ$$_-2ǿÿƽ¾f¶Ǭųʹþɺsƺa~c`ȳ~pbedɱÿ€Àúɾ¿x|b(eר8ۊ Qȿ¿ï4Ǹ¿ʢ"ÿdGĀT¾?]ľ¿ýxþȦÿDžĀ¿˗#€ȼÿɪ<Ā:¿̋\Ŀ|`ǿ¾Ĺſ€9ÿy½|z€rÿ[ȿɿ՞ ZʿĿ¤̋-x˿Z[ֿĿ9|¿ǚB8)VD<ƽZv¾ʡ'ż½SȽ¿6 ¾ͼƯ%%# ! 5b¿ƿƿƬ$€9O ľǝ ]˽ƿp$€TX t #7B!  $sɿſʑ¿ɚÀVədȪWvÿǤV\|&89%+-  !$ƀɀ`óǬ96%>#'04- D:kŀvĻǿúƻw (-.4#  6FQ:)6 TĀº¾ýýƼǾ¾Ľÿö-,9]G*  ,D=83-3=tǿǺ¾½¼¿Ŀ󺞹ýŶԂ& && ,:F9='žĸþý€~ľļüûżƿ·{K53J11GŶĽĿþÀ<ƿ¹»ÿýŀ9ǰ=*. Fu1, 36/Eοƾ¿ſ-ÿŀTM%O" :115,þǻþ¿.Ν8)])+'+sƿ«ÿÿøĽ÷źR;E%S¯  Ǿú½ĺüùϟ¹¯ļĿo(¿˴½ѝïɷ¿)ˀ nķ+ ϾĥÀ½ϭºԞӮëåнƩˇ%ĿB9ǾýȨ¾þ´ʭءԜƝĵ˱ƾúþ_¿HסĬʪƖ=Ҿκƫ࡫̎ѱĽֳÿõ-ļ v¿Ż姝ͷЪ̊Ͻº¹əݾ颭پȞɚ߳{Xs1ÿ¹没˲٭̍Ư՘۱碫÷רЦ¿⟣ƻDZ ˶Cݲϕ׼Ϗ¹DZҰ՟Ǧᡫˎ˺־ijR# g\»׭ºӝԢћ׺ԝҰ۠Ʈʡʹ¿¿̺ϰ[*.+'ͫUDûթ՝ݿϳɕİڿۛΟЫВf03;D>)¾ͽy%sžԨל䳝ϱ»ޫߞÜҹΑ¾Ÿ)%9C51ӨО۸ǯ8ďמǗÔǢΎāB  \о¿ŀ#ǻؤћܳ€Ͷ؜¾לִԬϗĿ­b  xÿžùÿɻj0X¹+¾¸4ƻĿa, >þ¿'W½¿½÷ƸĻ˿d. /½ Ā&*DZƮDZ˱? DƾĀĀ¿¿ĀŵżǽŸ¿Úľ¿¿¿¿ŷ¿¿ſÿ¿ ¿#¿»ĿÀſGĿÿ¹ſĀþÀX€)þĬÿĀRÂþĿмq}ήiÿƲþ€;*ɫNǽÏDUȶ~7+ǯp-;ɽȸB$Ⱦÿʹvǽʫ' [ľĤ]="%lѿá|R7$ʼĽ»5VḊ5-ʺzþѲY3)CF)ÁΚM0.:<6,pȊC//<<5' Ŀÿ/ȾERŪ[%}q#Eÿb390G8x<]69:@! EoɿG 89>: S}ÿ¿ŀʪSǯ M]hV #ɿ̃ 4FP-$eAUQ(3$ȹ$$_-1ǿÿƽ¾f¶ǫųɺþɺvɹah_ȳ~pbgdɱÿ€Àúɾ¿x|b(eר8ۊ Rȿ¿ï4Ǹ¿ʢ"ÿdGĀT¾?]ľ¿ýwþȦÿDžĀ¿˗#€ȼÿɪ<Ā:¿̋\Ŀ|`ǿ¾Ĺſ€9ÿy½|z€rÿ[ȿɾ՞ ZʿĿ¤̋-x˿Z[ֿĿ9|¿ǚC>)WD<ƽZv¾ʡ'ż½SȽ¿6 ¾ͼư67# ! 5b¿ƿƿƬ$€9O ľǝ ]˽ƿp$€TX t #?W+  %sɿſʑ¿ɚÀVədȪWvÿǤV\|&79#:? !#ƀɀ`óǬ96%F+'05. PIkŀvĻǿúƻw (-.5"  7ReG*5 TĀº¾ýýƼǾ¾Ľÿö-,9]G* 9XI90,3=tǿǺ¾½¼¿Ŀ󺞹ýŶԂ& && -@$:F9='žĸþý€~ľļüûżƿ·{jO3J11GŶĽĿþÀ<ƿ¹»ÿýŀ9ǰ=*, P2, 36/EοĻ¿ſ-ŀSV-]6% ;115,þзººÀÿĀϖ6͚L8\*+'+s»ڵǼުÿؿƵ¸¾˿¹R=E/gʭ  Ǿûºŷٟ¿ﰷƿѯ¿Ǹ~¿pѥ(¾ɜ½ۢ¿üٙ精Ŀ¾檾¿¿ˀ l̯) Ͼף¾À甆駩ӴĿþҭˇ%¿F9Ǿȶƹע¾þ螭Ƹ˪ž_ĿVПڥ栶¿Ƶ(訽ź-$v¿ĽΚ۞񚡽ɹƽޠ{Xq1ÿºҠ弛֥Ųϡήý¿‚zDZ ˶A¿ФŶߧ쫾꛶À7R# gf¼ΥA¸ќ¿¿^ϰ[*.+'ͫUIýϤ·ѯg03;D>)¾ͽy&rĿϢձϝ³鮶¾Ÿ) %9C51ĽСۗ𹷳¾āA  \о¿ŀ)ƼѠݠ½þû昰٩Ŀíb  xҥҹѺÿʳѤƼңӯ¾˝ÿɻj0X¶¿VöƻĿa, >þĀ- ½ŀG½÷ƸĻ˿d. /½ Ā&*DZƮDZ˱? DƾĀĀ¿¿ĀŵżǽŸ¿Úľ¿¿¿¿ŷ¿¿ſÿt8mk@ic08v jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2R \ PXX`XX`XX`XXXPPXdKakadu-v5.2.1 v  ҡEA!#w a5dT^z3Bt@GL'sF ^C.̇m? f8:Bzê\ߠ_GuF-P 顁\8#*{Ŵ68ɴP}{_V|qP9z@H UW_(olo&JNt`z$EN)/o/{N/ Ǔl^XŐy_Ԇ`w49ȖgXcI~پ8jY7 7$S7K=焪}!N1k3w@6E @@:ڙ'yUw*xKa"yت .JGANcEt_L0@ݍKD WȈ뵩H.:k仉Q0.` {F x.YS* :wW_NL o3ff¢n:fCw9=p] {Fc['j 5X~ֆ UZ# ڊ0J@RJlkpyeǙF5-͢Wr %iEiUˏtKNuA?ko@X? ʊ|n^쟡(-`?_[A̭ptп*ќ-$hx @UŴs_Ջu^сr{o=R3&=ἷm}@ٟ%K!ӼIqJ}VS!pg10Mk!GLBb,Zh}d =}Z)us%*-̌"rȼ[pWH*rONW]kcc/BKbKBP AEd~!qLTAzok]sٗvQF+cdF sYuJ9D7U*ҍ*[]JDOIma,޵nYI>|%Jdq0zIU@{Žr- T ^(Q+s(A4ǒՖrD&'RJati=a]n(v,OnP{ V=;Df D[aijg" ´9P PNsxPdž?4ڗAN= g@Jա =Pjn8bn=4¤ZK䘎L#n}^Akƒ~2]"3t?} يPrDReSvޛom!Y:#,L!*жo@$gf8hP$#@BY neI>}H c F~0HG!Pda!.E Qk|? c'S8=8/ݐ0A{OY;+ݪ,8{ǩ `XvarGT|G)x%7vcH>vT4f iM,\FWjm ,hĴ-1Rpp ^̼?#oKM%1kK8OWdavZ*n*C]`EQx}v30ŘjhSCtkH60jlOXaIPFDM6 2~U?)0IEDj@8i2ʬ+ MٹRP7/ SLH$wƍX, n9G9zJ àOoj SSx/v"""yynN̝q2yqD,ELOgNqxΓq9zNox> JIn_^5~-5mGrcj-S̛G<ă?O_Pgn!i*7K#s-^#ߧ/D68뾀7xR\yYZKא':vqsE3;>TdH(6,5f;\cfzL\C%LԝgiSo,T79uG؂5.˓;9LQBbC2Y0䎸( 9"v6Һ 5S}T?&na]!եh58y_He]_Qf勉;D׼@6 G֌ (V3l0WDC롊$&X U,m w#F4A&'ڳ*9&'|oQ9i*dze1#_5 # 8p'E>Ӷo2!X;g/.Nf6lèAr;XH] p2ܩ5r)RvR*3e }P hj>, ÛߖF5t`%-mLpuq{6ue='XuIe&; ot3,hUyDzy< _ s֒|<s#2J_\`e㻉{[t&7TLq 3$v ) ģ5ކFya!'ۭf #}y(Gr2&'@S߄a0P~u{"xE^UBл,'{bo9%^~?j3•ɴf^gKG_P׭"#}u.5U ڸi~=ofW%*؀ޕ C庄f{AcMYͩ`[#`s .1 b@~:1naAE"lmuK#Ŗc_Aa 8b8UfF[wU X"kQewwVi 3o:U:Mܑ!R9XP'_3( b IhQM$8Hb y̞֌㚮b^0Ȫ:}!h:+? 9Q  ,0"B߲@}|k"G f:¡dE#SI@0XT.neS9`96ԉҳ}`~ۘr| ӯgsdX`{T.nd5ipǷu%&B՛XʮcK֩E Z'AZs2m8% l%Q2#]$b~5pyCAzT _MpΞ#. B!naǑ!B5tpe  |pbW9'N5 艏Br>Ji8N/c]|FtܖPw[zfBDQ$@SmwPvPb.')i_[TCFn$N MAyҪc[;j$o=YJr4K?{쫼?3W[/=5)A-ӸPԛSs+PV+z)o(F u֨G{Ou7Pʜ![더  9g{n3~Q?V7 k(B)1?UAY0H ?|6i`=r0xqzhpȄ c1 %\AUۼc'ᄯ(-}K~qt\\(z.JE}ۚߩ 2-|L>Y7}|ZC6lvh~PՍ˳ىXSN;tyxth t6WyC}7ǸNAJmA'#vqk0m } ?-*>?~ґ2_A'sl@>0nc;l](U/!}v%ּKbp-C i@0U(Sˆ:m@ rэ&| h2H00~eAD1A(nt tW2u~4TyaMͽ1 hs#InqR uQx #QbJRM-2N;\фJ(>:: ` _*=O{!QQ Y$M~NX2-2@(,;nvW ܈fik<0UkakI熹) J[gZ`{(gÊvi_~_AH47vOg}4Z0 w*q(Mpɳ|z1GZIoMҷ_xnT.>H3xP;RI²g@}$J$;W8b+{6ʲxPjw%KWZ3#p[Ⱦ+ vR!3B\|>;:,Ž>,gq\jZ;o yP) Cm}Ӥ#py)Uf!}C>.B?4Sj ߲O߄w xN^J,ԥ_ 6ONZ9_y~̅EatY<~ 4NG]fV0=uZcg7pٵJ` ѲzqŶ?G*.IzF-Y˄R=?X[u LFn]An$W.Xl#A$_-xYh2N/Ha &%!+I1坮*@76~Y/-жr'g*>oPKb.D^,01Tt}I T9pAde][#,Ad'C5FMFPz%R-#_[cWeH7,y=J(ПVX]2jNE0lRGTΧL8/ˎ}O }^ouvpT;=\: C!ͼ'W0A(Ptf78)u~ { ȯ_9֣*]yCj=v񋫭R42tvnlrzr@ ZQVݍ(Qx>}P{:[uw ~OÅh^@ϯ>"Rr0g%EN&׹~Æ4義sc"[AlRaܛ蛐dr!9rr),ępOP^[4^wz2FB_VבIx i aA=#ls9eus&{ 6 dDI5DL(ߪɔl% 7Bۑg}Ո46bE|GkM0zAsiG#$B\ v <.kZVƗ$h r2$uofCw}+wo-nsӛ?Js{W ^# ?N#}zeozŽ7 FV19u8I_6>CJ A",LV5%5/uO4:S uB`~cR6e$J ke분(pטv5e6ML#my`ڹoIpSM2UrGj&_+dރ9`P^pQX<_BXSZ5\xLDhcUE e+1,0a~KY0 C[%`uس=P9U5ϵ]e#ھY5ԗ~L2*ۘ~]$:rY]Yؕύmk@lNY?R`?D]?&OMP CeH}]uhS$@Z軟%YXr6 ËUŽ e~Q(z+t䖨 `nxwsu|A-)Ue'-ŸaapJ0 91IHȶ_4_`R>7ɡ _:/U+nxx]#}:R'}Ρmr~)~D8y1u $άitx*w+A1ҿqV#|*Z\#Y1bS =z#p<=O[< !Dެ(\ם?V54QSЌa|ű_$[|5w!?_Op6GŇ 0X=~t׸M9X7Do$/ mg^NR^ùG=g`O6Z.aV*D#dHlZLI ٗoA;Zs [_5q3B+atZaxƠFt)&2&U_cģ!*PϧE,$7}SUrng q˩/W7cf\O]ABZ?+*\) I=d'lmx`B~<v6 yvC}%$DcD[EyIUkv(vl Jx8JF:MH92}P`jT[g(ALkϤ<0䍀!m*\om6N.f:#$Qp=Ia: Eu$/s; SzFwZhFͻ:- jaG~ilfeYDbM>&a9DO|ӄp샴Xk۔IQ?Jޕn^K\4|/XmZ5ߧPԾM ,0\ r;՚:: S6mшzp Y=z"MԱ#@֨!_tԱd^]\;bl$f/|4<E jG>EiF"St6XH(B{ »/- s%zyO/,Xfe۩!Q9SdPګ\Of-p/ dh9OͼEXhtj)g "Bd=MA/$lQSA dXC,j$FM ,B--}]  &G3>ǻ@)KYJq F8E ӛZ鰘h7P) g2>x~Zd&;mv3ct(% $z1"s6u:{\}ZL+f7ʼ;?JZƂK;Im $*k_ޟڨfo3UWj'^䯮v%`[WH *q*G$,cŋ=Yq,Q~z4h0'9Lum4IΥImJ}1 26ȸZsR ^eɿ!Vnaxx% qpmp!ZŖ8`ZO+,Sra0uH(<[Dt!G B|2guM>Gj@w$TK$P#2]p^h!-o2E s Y|"mRFŠ88;#[N7dل4yފx1C#h+K,ֵ*5ThQmHsF]ps[=M,R)R nⅢ֮D-$4]B W VQ-uG |hЅpR7lV5]$*}P).>S4>:^@iLxj;I0#BA\ĀB^dax# _OGg0P[{n?ovxܶwi>c?pP5NRZEsuʴۣwkcR8?(W牕:Qw}ƧGTjR x4УD0vDwށm r0 T*MNԊ46V_t8%k!z};}^`kKŭhtH\g]?Dp.A'2)σnB(U.n:$!1Rc?K{揳$? lȲcmlveh!\+%Q^%'& -D°cy `fL)W>KXU3jW埵4J@-)tAնA^q {bZ:g>DȧPs}7Aӫpcz[l3)vlьʽnLDOĺx H\UIA NH9@ڵ9i1*֦h[9nUyc=0^F7W;v}Chg  G <~7πϴs3:v)Jfw(XQQ L.FC=~Zc,jJdɂ8n'g鹜W>qq݈ "*S`G411( VgQ091dfk P4ֽdb"vxB9m|IVB⧸޻E&S9a4wBa-P&2"bU”p?Z6rܷi!زHID,̩Zޖ¹^ %8&ƘPi)˂yثd c.; h4wc 91}/te>ؚϚx4Z6{lj7iLx<ܝ=$/ksoOpĠ|32&'%,(%_}!ԭ]~uyz70#!e}͂Mʜ1OmɼqMmpǩ>H&g#1g`$6٩m0?PұpԀbLF]hLkFG6' EN5\֤0h ^S|Β'T.[vT`fOT#Co#z='Ыpp*;u&l(ᝪ I%Q[P"&}^LwDjd^C CfW^BО3\lIɸ!PXaO-9>kR 3$<}> Wɱ ACi J0u69W,@4A?n^wu9pPqA;w#ӝüj/Q^<.q0 1jky 'Bc!"\@udvUsXW[)'hO$̔vefbeЮ,na:L[($%P͝/*e;%B|&Ib_4SSm0 Q7d"?]C ֞/KpWl! zmӮ.@G_K4`\|dAw!g(xNd`61)*] 똁F.ZkI Y`ZZu7`㪨Ҿ|uNp(3V1k,GR_=1h:oO&5FdݝNJOKTBk>(%'OSnUJ,V*wp!g?]h܆LeB(*5#<\UWS&Q8GD3VmU$7 ̭5 iҍ[|ɇLP)TyzmG$IXkD*ۀ;-"$V ^_Ν֡b9Qأoc}P==4sΨpE ,EۉD 1W?O ؔMеl<*x庽 = rĜ]4#.㘂O1.ny=+):}"R%lKhGmuIYsy cZl 6f+I^.13՟Z,=P=W2G>fnKfeo;lc8E7JֆO=ؾ00Mʞ]{V,jxǼ]r 8q1oku˗г?翭Ule#D jRA~. rQ&8޵‰ ,~ͬ%?+ ^C/.&beD(mfFr9}\~>wBn6#`Ӆ~rmIK b Il0} 򄂊2 prHc=KȺLS"=93LbxP旞#R-mA9BԓT`q1Nɚ-ňiB/ QNMbu517-w y'!$؎E+ /_o>2_kG20ErOlCU Y*Tϟsbzf|-sZF sh25/XF8ן\qdW/4sֺt`3 g2E0IC =5.*.yݨ;65h*\$1s˽ "&6(MZ]n<ٲ:V b$gRʪ \-1K+WѾ-Ao( G60rݣZgh!pN-/Vj\LZsN]0~JE'-1;v )3`:=.fŏNp_eci{Ini;"ۭk7}X_d0#BcX ous bAΫ[._tc&[a9}jB]L clB=.:Op<C&[{dbF;ڬ0z8D2fE3sq' {Gna 8$2-4JQ '\;r''Ʒ 瓢tbCOyguwԜ''Y_#SQE/UCqzU(ėqRy^ph/T#LLY&lrE1HV) `3)|ʀd%px筆L!wUd:=]64|2NʳV6ϨP R|z3LˢpatT/ruVN*oܦ|Y,+""<–aG&xR߁nүyco{'_>B'&Ͷ,G8ؾm2!=rBۍه)WYke hGOTXeQ-_+滃XW6Œp!dG Q LuefOm3ST0aAZpL!A=t<a.wð O$qGvS鲈'v؁zcڿYosAlL'qj] C:j u-+_N *1]\1UY8<[:~O^V2zLW! vǒ1AEq-(mH3wL\QJ3߱ɢaEd2ܙ 9Y2vh/ k#Q1Z! Ej*-hyp 3s+P=٥L͉!hJ,ܯT4q9Ce}C  ky9a\ 44%X&(d'ԯT\8Ct90#I^௧j"h?|}S+\YEpSAd{ W|PXNT-K~Z݌hdlV.*K/#5_65tLeq?Zȁ?peA l]ǜײ6˙Ԫy d9/s\O+'59vh ϴiddcmj`-4XMfc/Kn xEuɏøf4HH3vT,!VyH?:*c҇?l1"k^zB6u=Jpx6f rg=Ct j_Pàb!g캓ZL7Rtɵj[7\`y5p3z `Y}tQy $c@P'vZcQY FBWAyjz<=p急Jt-Ot}a6KǏ@3!fo7!!RDo AZҽMAy I{PҩnD[a1xL`'b @ψ CUx`;bVP鲱p4|-䀿/){_wyPr%#污[Aں yXa-3<șծwWrɏo5{S/\[8=4x6p~ ӣ/ rښsN/Tfmهe?g.'̴ZHv:d.[ߞg"BDPOxf8KgE~#L "$CMnG%L}ٶ-,1"Dq7/oecM^Izfx.+5zٸA8sڎ5^.Y/RFkB}N:ǎ5ndjIa,_wf"O7!q-e=Qr?MrodiXc,\&fAܐM&Є"1b%O]_R o:(ALTbo}9&M%bTإE2ʪ6h~v{(^f¥WKnšF\Syhecx|#qe M՛ X=j!8l E; ƀ0$ t#R"*r!@mu5[=P#odY0x^J-XZQSjs@|츾s_scղ?ʲpZ#.Q?6` O6du4 } a(d\M 81L]#i n;3LwEÔd"'>%8H}* $ƂmbBJ t!&Cm'GC4a)5ݧe`4G2`)/Zebd0 kS,%p(B3D(b >Swd;n8z8Eܢ)T<җM2h~ֶD-bXTGBP FYItV<|W>zyLp3N[B+X["+tp`Ø?xT&헁%xl'^w z?w j„ ub~w>&|qҴq,?ݰhN:q0$#a= *x!DGKH GD2#CA1٬Ϸ8+*sY gi*8xGg'ϴ=O<@-VMzA@A%-R*ZZkw "Dg`n4s mӛB{]<.%~ecn{Q?rFkղw JZU g ~u6i@7%鮓`ѷv;Ȥ-^mekF!#qqɰրjV5x?ZAfQWz#P"Zfý CP;l*:z1w}N;Fp&P_wd~޹t4QqĶD@-\?Wk eʐq^z$0<봐`̨xzטɢ`Dw`u^ȃ(`/ln퍬jW6ӾMg.RCt0w3o_ןV, m|mdiʁJj)ԝ/kKz2~߮‰_a9ofK.-&13CV0!$I#tW+v _ONSC7ʯ/<`Xͬ?s.J( 8LW_H67vū`7~l޻Zh?*6[{m0((LtڋۅܧE188zKe&%N|b{놭!E L)Dв+c7AroeD$iʓ_,ֹggUUQ2]M}ef'c8NU;oFJkp =L`(u S<}(~&KmsڬX[ /r&QC}$*ԗNeWBq H瀈u^!G࿴K7jъpIVf D?eEvɘcgQ8%VwĸRb=BC08aЇ} +6v \bF&幤$N 0;Ol;U Nh+0O[<0. uN>X9CX^ܡړgl$,c)|CRsb@&4F6:=}\D ݔѷך3ԆNmXSH7<$ }0zbsW穯܈.0/Jbl<4Pϴ}Uy`KϾ6dÎ3cvNX)EousZ sQH"{'_ڞeM*6.ynћ6ݹIJDK9?Y|8`%\V&ZH2ą1oJCN.矅(UQoC ˆ\*q/ŵP+ȵ6mh>*1nt'Gl<`/@ڲt3UJ⟐F!3Kl&[OO?\"vBw,yF?{67Sʶ,&چIXJrsV"}j\%BP+(x[{^<} h',-1yQ R,nm!S7WbSnG?.dAD:FEV5еH. L{ Ϩ.p.NiCђG Sc5,;qkMblE_<ـkWĩrESEď s "0:ƨZ/~WjϿqM4`nZ{SWwcᚧ%[L$*[m}&]** ~>dkāƒT(#wo8v-%!26i1bl8cͻ#c#]Ҽ5O׿&VDŽ+=L @AgL;6&Qȋ-Co|Kx2bb't%BO{>[zM3@1U9G~~߷.o KCblwyGY+o"ۼxXjA52Pyg6!G<BC%ˎbr4?JQ@~_]kpDS|SL_y(A&I6fa?+#Ьi*#>Ɲ1I~,ͦ3cuc>/s"aKEDp^ڤ P1-C-Fy{+=rz40a}*|Fw K>r YZztPڄWC6z.Vb4cKma e-?U}<@ NnH0ڴ(vHz=Uw6j:^[)x߽q=O٠{M9冻tCiPN@_=A53M[AbYWvԹ}\QB_q]DŽڻ {|ޕFCRsVz֩J%H$&6`_}h ;|0UF% g0ȯ\G*FH~@BʑZ,]Bgml'=|믞BZվ_S/~~~~JkTrdbXj՟<Β3793\7e_T2xU"$2 $JVO$AXzpըYhvb,y;6U6!`?H;BKJ\@5"L# *@L@9SBL?h۪ug?k.<sD_V ]Sa@n62MFR,G=a?T+M /Z/:yǿ  :DRo?2HoVf%l)}"LQ !;֏Fv@uNL3yE!b3ǵfIe|YUj$B(/ lIk{GEc}|NY ڀhF/K"B-YN|I~\BuY],62P@.⫬~2ZeWI*6p3I]g| ~ eT@130ȀօΠE텭 3ü1ș*Dv`;<AsQNq=KD!*Ue6}#X% Duoˏ7,YVTLb<'QQ Uabm. =CҥrG첷" uIdiu^Pu:{Y"l&tEa DT,f%i|M m^<_xo8*9ZXMmf@G/EjX cWwnhA!_i R+Ha kr]=w_v@D]-G J.;+\h ݘrDh#F!-kQ~\)g# 4 3ʢOwJ'uv\P)%ex׻^]??މtO WPRe .ޡoZ 7$b%s'n]vUy3ϼ,̰%|U7hcTUeL '_Vu' 2FyP#'!j8Аd }Ǽ80^A9Ÿ+уd?SגD"yjcHZ@wtY]:M;l@6\R ^Xȡ\`w)cp*zwuت+Z.'8+㳍q(Q%RK!߫P:y%1f6%6R8`fW[c9KS4A~D)R9g8|;3NI^8xcthǕIrr U]NʋMdY#~:tlܤћZ.w=#kbSQ:DKfJCB| ewNJ֢r<2A*`DX ҼCI 쭐`SB,ww02YD{T8SE㣩'B~%t|<؝֏RsLpN$nP:Z*}j\flH2*<gŶ ^ ddgWk3Z}[?|@@^h+010h×PU^X@9rKuFwYoXb>lAjʍT̓4cYn~ 9Mcc(xE@[ 2)BPH\rr2LĤ`ȴv1$שʰNBR:MCJ^dibmK5NRg'|=NA ?!֑d .NyR2(]$16L Ӳ~GrԨLThX]Y%+\#w`Gz }@T^sۘA3[,d.Bkw6#S] -G@}K0nA+u*C={{xt: HMNtR ]wxO> vUA~%%§o$xqƬ0xQJ`1 [JS{[ *LURjzC?ˢXuG :VK JbR5Ԟ*E}c[SWd~$db2<)!bv [E"u?W 0Iէt?]OaWѕ,~f92k:`.3agb#e{ݟcИ1+#5k'mckK\2j)jWKi 3x(e@=~4yɠK| gToAV¾ ðp"ɯ ےR4hKO6ZdL;svSyAFC(ޥ7xLӑNz. heEn003ǛxNsi2̵:L\2]һr38%3;4pVN*ӽ /l/%89}6^iъَ̎{VtC\e85u] 9sHb0ޞ+@UV#LcCMH̼SaنP5|5ӂ=\&H>TK<POe |Mf }̃ F=5ޠ;[E1ٔ|Us&×e2JW |>ݩh"1' XqPqϦ px9= #גp$7'kBIF({-Ԗ%-'˝iS}ꅑY3KUnpM8Lj _0@MMx.ci#fcmT# 4roGXx$$jEm_h2&DT Z@ߪN-nKrtѠ0ϛR0}{n+@H+,isaJN8K[!'cGۥ3J S,m ̚bW5jĜ<:~Q& S]Uɛ.-7l[|r'€d~7>cK %Nw{2w7bYB_.M÷78E.Կl̼R>sz_J+&re}YsŸFZ5a`顸DW*]ԭ}thLwNZT7ov6bVsdig9dɊ2\fSh>jiL1( <S<ƪm*SZ&uNw-"`/ +ab|DzO BH}_GچTvfN%q y#Z E#M=gG2Qԭ.;_jg \l4?F\xe7'sfbd d)`އ$# VeQNQ# Rvl5. GRrm{ {T=MWK WU_(D3@18V4Qkޔ׈y>>Ų<Ⱦy@ҕ+Fz{aBصh "^`CF)ϜyW׆qb1MgF.Qk[ =Xo4vۿZRi(؃J._ %+eqBQ6}Z}i<^4zZ zX+eiir9#<6cb$%C>YV`}ttbrtpMnxNsKxF Db!\Y[ pSM^-u1`Š/Eg_!)/VnS YoJǒƗls}g.(K_`naݨDcew h5\\Ntp79ȉXC6Pd(xrPs?B#bם_jaأ}6Q3zHtЇ.W8/Bҫk d&S)IGn2: 1aC_ɋtwR@մ>Bҍ_qh'PץI>CQj)pSth`C~lLo3CԞĨ:-tʘiU筰Vvij+zeh"=$hu/T.PXѦK̖QД,/A(G#K1k @wƂH Xꚺ„7xnd&<}S)=\^Cb%%OiIBNzJdPߒɄ+- >u/ܞׂz@h4a*b+cFH;v|vpbïr²lvM7TAԡnQLcN'= ?$)3#VVI6ݟL\zK~ CgX eŹʶu`n픧8L~bk@ | },s~*,a7rYЙf)@/Ae6~]0G 5e00Wo/ z2p;mhU RxeO8$Uai6f#4F17?0/Sij{ 9/r'E(I22D)!uKGT{x~ktօ5T ;ضB3\ L P>3͗\"KZȀ; " ۧҾ)sูy[nL5l 4-%Ujʇ\)]w3 L!Uhc&s נG87 $@υse U,zjWi@sυG4c0 XV:̔"';Fk;W̯^%9?(sx c7| lJ$*B})5I K3FJﻳ%>O!uVQfTx*sm$"И*oy@1 ?fyjl2INT.','ZNq`Y4K |5{iyWD٬3:*shD܉E,3C Zk`w$)&ؾ} jRGRɃضoITV+{%B}5Ýe E;`RC͍ c΅F_ ^")d;.KYrA<\J/JabhYv,t)Pסd # :0͆u.csT&oM.щd|]ޝI9'bd!=s-rr~ywODrߔurt0 U&/zL(cY+p^};owg)FjBʤNKieiL7T0VH,~O"N 4)+s4VYl(s|7Ϥ򢱈*=ܟ(쑃ehL{2cMac`3U (d%WlT;."pO ؏!%1W^ Hg`.iȈ"ø`kee tp"4]0ASpNj`գA$"oG_r83gKh18:Pzj= no$8oJ2 Tdܞ~~/!:ɀ8yZ2Ge~绉\&VŔ/%qqIE28U"(A>DZdmgWGDoVs+pDvW]N2<߄e~j ng[s©yuk:Tߨ2S(s5XꩧzdI-jKmRS.f䄮!ٽBe.S&ؖOCUN(#C}ة ?aP39jr+xe9hi{N>þ zf]gtݚ+`9ʁ?{@MJ2"'6d|UŦŊYb>yeK.pݣtOD D%T|K燎A\R66FΏ9DwL 2T\^gDmrكML_:"*/#R?h~ QLx"G}k^G3= f1MsS$-87OFP>r\i⢗c~‡Qsx!(ݛxl(V|WEvVTPG\aß't9:ڗP60ٟjEWN&#|('R]c4"k)E&A5Pa<6T#Nٹ'1 f 1x|YpHZ]Q>$Co)ŤJ"2k,̃v ;gAuvy;W꽜0 im0DQÎ;wYnye!h+FNOҙ<<"y"QqIwMmh dk`xw xϢG:]1ᡐtfȁd#GLcئ?JʽU ipf+m$ dοX~=qPc[<3R-PT]$x#,9.Osо\%g){$.U|;! ڴŽvR%!H)y3(ue&UryU5A#6o}RӨ&yHnܖ1E2eI3a^=jqiThC*2nREi֎d֫p +wqq@|wBeҪUZ+ɹybW>o, " m35Ȃ+Tem`ik}a\Z(FEaaғ?N/'OqPZ P)o[-V?iH-!"A8z #FaL5 hgP|MY(黭Ԧ2mEVh:mhϴBGƚ}-Cމ\n}X~Vُcx:ךB VlР._|obiCOݨ b 6䔨M=Hq'0'gPX$|sm1yeoɾJkw7Fhzw|`nS!Bd Ts[RޒZ^fJN"$/pgo7&d&f%"lH)$Oz}J05S MR:PmܭuZ͓,=Ird~[s!B H9^7tׁ-b>&Y}>ǜ~@ծT"ċdy;S$X&.j"{%>֌.YC&@n R Ͼ玮>gZ2H, +%K5qSՉmrKt;-&>k>.6@ /wL挑+"[צ\z[ He@8֣_rJcR:0g&SOb' cqxy7ʎ҇ sGqȅQWX(5&؅}YmlL (y!;RWݾ C]==w vssX((T)( ?VpmQltaBNLNqP^IřfЅW<:=hi`b ?;AG@\mW͟ρ9 Zy)v׺1'yt\ȈGdtt9Ǣ~k' mQDACI:6LM1bqdB b֗A"ceTU"Wu~gs.3Y4 Ǫ<:4~)% )NQfkU$hHjDQ f[x  @.S5ʓUq_}?e?NYއ d) m (ڡ6m2t3Z' @zmtVUw~C{ROb Z7 hUqZbf!n_Vʷv؝yv Ý6|ВL[٪;'* S %$*=*UVOQ*'lkSW 73/e#+!$ipKAUthħCaB{}b#<`[Ɂ_nke=)G;z1 6.걵|t9?-~d/ٚ;v.APD td}0*9?%[xF͘~ cXB%zꨃ`z/R1`ѷ2{a_tġ}ڤc8ĭX՜3\Pe< ^ _Z/BnX%'hy?@BсaM8#;^'+⶟ ؙgPеPaxiAC=TS4`Cgp`OxxSFQ`8lߜWSd+*z}0;&Qdmp[8mxjRɧϽOI]/W.qp#\ o$ǔ{ɤ/l`43R Z-*= 0*>,Z/%Ouĵ S == ܔb35 X1+@WWW.o_BRS;H4` 1$hv}C׹b ǜB)PPirΣ1C!tTۧ OM?#eh/*;,ba&KƲkQN )[Dܮ۳Q1ۈ'Dk}o6l/}eͳn0[>z6ߓfĠ{6aA9坘1')'"wBD@XsI+3]1}斫dRo謝6PMov^^rWTWﱾ["\R-̅5%TR:1(-U8\/7-3687:9g5 <Լvؖb]S ~nvpKmE|OTb m`h't\ϥX!0[qYst28I5.֫bU }CPDM@^8]:;UiX˅ϧ?ƠU$d1 4M`:[E <3;WQz 8R "ai5nZxrfȬpNso\cqU#8B+`O1! D2~a&0$liʶHENAd˰4aDdߏlE6 ~(#N+cO (q6CY_!㧿 ]*`8W)bT ہN婬pBuM}mt |_kWZou g)vIfM,a1"si %0nEuS}jNq10᣸3#8_ Ž˞,vf2߄IGz 13aa ]'k`<sԾj:4&c&8r'@Ay$>8imGvLeX$,^:mјykQg}ܮun~:xePJ"v}-TXھ߅bԸLs48>9@\&%[r r>So[mVPjX!uSKlQ...ur/U;?~L=m%ૄ髮榝lsbfTw?%Dgl %(y{f* qE1H 4T &$+EoW74];i\/ƳbhYJ8!}yq\!]7o,amq6 :0Vhv;H!u8=F -.=A m&R~.RFee76^HBd_NΩbL;'_|ˠ!YU#{ԉI6]QA'{0Œ1̔412hlkqgq n2m=toq J:I]~khuf}fyZl,a΍ϓC=YRa--bjmj:<֟rBLLv%_ '>a!S&jA(<: 4Z2 Oj87PoFuG{X` wQ$ r&[}zCƁl*z9rEśͫPϺoX@RcN[d9ÍГtl;Ir4y1.ejRZ٢Om ّnUk4̽ /#iGcDe*UYӾne7OVɌGkta;A̪%ͣe6x57H VbaQ$Y%xD GGOܡՓj[s@8 >(vi0upw,2I7Oy(ԡ4ba~Y6OTt*X _D #?N P%(Dߌ}riE=+nnbC>ʵGcƹf~δO 6l/k+E+ذ8N6Hm/.gC<RCaf :Epf6t_![߉v_r=c1l)T_y/(_bcx*SLYKaW,)h]Iq:R:Db3/+^͵/<42L1|؞̛76逭ԜM?M!E SL6 rLR FG/EnEEWFr1Py.<={pxLa<,wm*d쥚hl#!ϡrvrxWtBzܙF z9W#~1 p)Z %P8͙tZaH5s ՋÙi|GD]Uq*{MMH{3dNoPJ읯ꂊwDZaS PFHiUig x2hBu1Ag9WΗ)> ^ -_rܸL8Mk޻fAoR{(PIJ49b OEOiBTmʐ"_zkW8dATe vÖ-'Qz.00S 1#{aٷP=t_r>zn4ax7˟sq疆UfmitsmSt -/HVMַIˀ=Ktڡh s"9g|S%:>M!kq~uP,37l*Pܫ俲eRνL\zLÙV ^f,r0 ]@t.fa!AL5zi53axvNc<.I3kX|p99y@Ky!SI^j>ZrUt <")AofZ+%mP-+l?Toµ5>dEkcaNa"A-X&g帗% )vpd5h!ۉC%Su҇q6M 'B?J $Zˀv)SsdH $%hΐJ$# ,.震"p{j 7P*! @f>9zU~ް7_oYï uv1}b`%wG.@0pUGcFp: >ôƜ.ـkϾf7Pہ=uuaY+<(=r_=Gg#Iz]^64 PS42u\eh`5\O6*A"}W4./B@7;4aCPViXD2Ӛ67/[^&} i14@8 븟np{s[l|+]4HezED,JR0ݮ㡂wngx9i6Fd#u~k0FjlB5if<,MX-~[W?=L:#.~]C3h;9v5e|`0]<2k=%1U̫1|2APJQkZpBFn/-Foyj9"E¶-2ܛ 9E'\OgYK"J>R,շl]ϸ@2ji&8ľW-u#,h>[I/[@ xq!TlZm (?%ܱ[^|A22lÎ%f0})/qEܬ14l:m0lTu~rj,[RJ]_v- i,YvO@i+vx>˽Tl /)A~HUT sW/KPv_ڇW?J"[ӻYUxfHFQ)h=R3.1S#AXb1=&vrs֡P=zSޤ043 F-`G#nH\"O#:4H*|0_ȦN׬-Ę w[WӃq[U`U}㺈`(kxsÑ2m.'z#ȿ%vwM4[9R6(ik>[ LdžMbZ@A4Ht(4Sp1i#D\iY4Ťd/QL#Z"09X5\@ud&HYaG Rݺ~s<񛘨&=^$$6rI>azWz+!Su:#'켑` `ȅZ 'ؒ_62c³0 JK J]1pxp1Uma4.ٝs=p)\O\P'@9 +;gu7%cZ|/nxR$Í/NSR{#iTvl,2PHm1u$QP_St+ 9n!Ӑ THPHPG<[F#A ٞO>/sJ3~{wb`.7;AtE?Y+N^ՉS]LR 5S]ٮH>\1@;%OG?n|Ο'ig0vwvk[ԵtEKII~Hi1 #\chu*0%!JB}Z0ꯆ5ZGǨS;]͘ҷK);?-1z`H г(jghHwOXЛ )=J_6 3T39f! kzpͶn1A{1̸b/Ad!:{@ͱWSDI%#Ժ>ͻj3sm`d0͐P{2Q`F CUt$5^tg%Mu;QLbbѣW2EVU0QH=| LP0CE~>Zi !Vz_eivOũ#h0)|GN: .3n~@'Y(O(%xR5py;p +@ry,ݔݲ:.kU.|2ֆ`OhBi_*2FZj.N^F˚]SQUp!ܸfg9Mx2|PRaPϜ3JrSLh=:h̫-~2{SSi}Cg.*3Kb Vs4 lSbxc6)[y)JRyU6[7ze!_Zq*E˘'Ϣ |&Àٷ!grj&ru Э(^4x[ =RK_gt7DX[(J~{eceb|3Ȗ:`f X5ni\[ddFmy䇕;9:p$Dzxޱ:sr Ȓd␆bY[ohՈa߈[|u2dDOƗ4*ɘ_;Ј%tS!?wLzV(Kӏ5A^%jg  ^#[OJ.\5Bؗa6˛wZTz/" -VhJ}D^bF-rmH(yž̞]N|<\ ǀi܁'f_r$ƒa> YTKS%AG0Zf'qFEb#"ncSF_ \ Kw{dD61[$z-K Z5qF, 0=76rӸ'$)vAڅ+6do9wI7X\cOc_>!h?!XHE',S2ħ HP裵}ʹ ֢,(9 ³#c7*t^]>,D+UK6iDM/Stxld" KE*˱a{'nMp 6uNgZzU=-d1][T01'Գv՘uG$x~ϴstY׹/S4ؿaBYFVjI(<^>Y*)y* a3eɺ2pߞM;`h1Hw󙟍DS㉝SO(n:P]X]%e\4Op HF+rz)rt@wYT CK`:D(a.4('Č:a-[5z585[z-\)xm5v,NG=ߜ)XڼЄf"> :>i,JlR{Q ?}~diM2cxv!jEX!8Ĺ3'}Rl;JA9r}L~@٧? 5>*pZv50鼼e)`(aQ~<91sL!bШJ[ȃ_R'C$U_ r'騉)RG(Tsp;[%{iQ(2? xH/g#jlc+OC7|FJ'%MLApηɃ;z#0# aơt!#2ys6Ocjp),A8K ˦(HS42 4+P]ܞ^ĕ7I}!HpG^Dԥτ*At αp.:䈡o/,X_wMlrְu-.<F-!gA]jȵQF#h7yTA FMK9,9sKk3G<<8RNռ 7 eR-cँcֶܹ:TYT&i%6zx*Im#}H|KVVU۷߻<& xY`AAm* RBYby< b DEl`ϖ#Ѕuu\5^.Vn\k;E甃Jf{aqi<攴٤]B_w@;0uߌOnYo7!ܒt< Y0Y Zs9"ȹmV}|Ēꪗ_{[U7?H&ذ|Aʉ +Lsh*l?ٓ"1 /R,B_b#E./J"JUUn> ܱvTH>!,˄tE^>8D ~ 1[Cppg]戭x4yvMo* c{ !+%~GBctd*0E1 CScP3CErW<5YMBMLwgq}8JԄ,B+KѰbsE= : =LOG&_]} @} EI~`V_U5R'Gw7;wt)7 >@Ƶ|t:դd5.p8OLIhp%v1d?.;W+z+oA*"C:L3׏mT,~qpρs[(@ZJ"=̫x&T@ }nnMҔ(7w# ;hNb{hЬ-uFxz>=uRz/\5;4ͮPNiR`fVR &s|MWzr*" //9<A:GbePVqVN(#A}Q/^iJ ˡ0Z;vƔUdޘ2WR`l3NxTsۧ95Y%';<+)_IX1OK'϶?֋'AO=t0w7SA*9ﻑ(w氭I o{ZA0hg#om$Iֹ xe4z"Rع_к89wxÌtݷS+$vagf2ɜIϖa4FS@y%nJ jA8^KPgxFک̮,$?#5y*mqE&⩥YոXX8-0۠BvմM. CcT+u5>8!#ڡ +*+΅Ui F4Ahx]%`фF:_u${dP47I7;TD/ӺrJ дq]1=2[9 bZ+qR }Xnk1z(,tQѼe,\xQb{vvX"uݷ< u]/J. g/h )lB"i.2#*89} "[l3W r|ZJUb$ߌ Qc@W /n^DdnIّ|Hnx9)'4VxD*ntd~98T/];āb֞RPDʬ[YOP;\';`ch4v5:#|ٚ,!N3⓮>A{}<@Oi{Jzw(x1f8oyH+NjgucZ䛗8MS ި O-qr$|ْU Xdר&GAEvPdNGK/0({n X"@_Nsu&5 bQRF<5/&$%.36F, rѿ >ò/O=Λz՗tO 7h8h!-VH{Xֵcãϩgqi TQA_ֈY5~zEw.mh4.C U2ުA4@{M3BLS%h7@~,a3[gPՑ8#{ HBJC}ģj5NuN-*[׮M޴<`x^u445Q xf@lˮNw>Qg;NA&؇aVV-\24Q GRtcI+ i>l/dJ:f| 67AIwH;[?Ͷ^hq5ws_yA6LN; 9̵O {i><( `*o'>~!33rg\IA[>߆'B:z0͎K"Qib*A=vםaX be%R535D$/zqR9ɰ⺣*E[ `׆ E3"KuO0-X_) XP(,ѰFKAvR| X]۵Th}iŹ;wF]Uez aQj"d[s H=p8;2f:6j/y30NqAGf3"68$vi:cm4j+:iNӛj,x$ nuE脀OL$j.džd`*Šji9YRa#=2%S6#Jý?}brDi%2C9hb\ۺ^0DMŖr h Sڵ#@{WFe9*9jS]2|Ĵ+\|bcVm `f@\HRVYC6N-VkCMek1%ADd>b38puS,9: 2=?QwJSK薶`&t_Mø|\$1fnN! lNqȍw| *}<2X|h2ӞJKWhU+EYrHC;\GΦ*S*;."o:a9j6`X#M0{>M^mC#ˣTVڗyit-zr33z!#\e?+ܔʠ4k&v} 1<Ď2[iPJ{Q;``ly(j)8t)~&H5aYbQoUSPo5;Pmz wB G /n9XAXyp@5ˍR_aS#G^qֻE8n6j!K)`:Dm-Jn??+ȦJ Q_2[Qm[TNZ--%cϴ4e!qu|69/"?H1을 ѡ#q@7V&x rp%I`o\ANFY >G~)} $ڟh-VhE:Xv c@cDV-&WgoÛP#? 3s:Đ40>T#WIo_odV՛oQ09ǥ;KfiPn:$2D@`MS?DBya@) kjYXu"CCovaɉ$N>iLa9\0 ݤ0Uc ;Z._  |wO-D|64(HfgDwR a9TwJ1 95|^;6C(t?Y]!gy*V5u25IX·a¼y# >o}dX۝JNCԱ,;8x-nRJJNgMqpo&8MinCo/;7D̆RG 뽆^Q@=",\l_=gAmFL2(9kv`=sCK)ț Dl(OxiE~On̗''F븅 _dQk7ٯyAjlJC[F.f2tSk| Q>hݝ{Pi kRW'4/C*cML:>UakYi(s_KyN]m ǕEOu?!Ng cfƎLskV/ebYMLUFlp=*YW7XFhKb%klz1C4cw/Bi#RKNjdTW޳ѽkibE O&ݳr<>@c(:Y(YK| XZ0Rv1o@V-N UU¹#G+jrFXI[G.JWB>GX)#KE7x|\ӗYj=b0|dD&HȄGm9=ULG,wz`JH HP \ERA1o1M7Ju\э-/eE .&?&!yR@9s"Gt_I)3 9jg,:c5%%GG9iń6_[>X z3s + ٸ7-0]ԻT;b~Xd>fMgM߆y--~0pcV@an5 Rkt`+-2X߈<l03;}_UJ:~xc7Q0D Uxq0p'N(3wwq(-bP:(-S x6% &#NݷP"f0gi{`!r.tvI,>%Y+AI]jJaW L-4;|i_>=H(O0PD-_Pݣs;, H,r1ʇZ+EdZKw2,d6"tO1g$E#S5G ~&6_]i>- ҿCeFꗰwR \G:nwVm\LP~\lc (eB ۨ`j $ayM3ZjUY3ׅ ,^/aV?/3O>ߣU|kO)ά`^Aa]Xv"\}4B؝zѩ YICɗ~]>d7Dv1$3QP~J>;>h1 G7%|{quxϑu8,9+ȑ6Nd8C7];Yj)fVL%5"MgX~pşԞ˰@R txZ]햌e~&6O>"SwΡݭDzO4ON=;ԋaG cw"һƋ{]}F>/@c_yYz39w^?bmÀ6mpxAmu34c)fL6iD] dC T0bUy4޳M0j/@["(N1anh!z3<*(MN ey|f +=˱6lWqB;T8 /[PcZ\89'&,й(<vXBֻ ݅x1qJ9Y8L)s4/B&4B<-PȥF_ Z: {\%8J]9t~y~S5\ڡ@o.V)ɰlۤkUȷE.]?NJ;Wҹ)qPs6!P3g q V10#ԽfMU nvv3dc0jG,^55ox- +LAcΘB 9ITb#Cj_Yv"FXt-vPO5}ٯϋh)IV\,*~@iaGτ-4#(>.!z'CaS#1Ds0`E'<@XPTtOy2G1Hܙ'YhgB"n:'@[&&;ֿMև`ZN漸{_f޸"T#U@{PDd4آ@$.bl=ChAVkG#ʃs Qp'R ýPLe4R^cw6E+jyFXj\Bh:fsC;\ Zyi3МlMFCwlL *txF0xw+usbڒ90s3rHFS:y)i5vQ':WŞ0d,c6Z(\{@H̴0612v HlP95NzT1$i;c;[Ad1?U]z;{bD} B=ʭnH/v>ٳz\(9kIjMIl0xtؑ|7zc5':sFONᮌUʳ ϡڍp~He2$#7χx >HCπI.q4l/ ?t`k=%qX@Y 3 rH^hj|JOE4/$"H -&m$VVLgvd}R2q"o N tS3WAȻ26wd8:%}CA$4#912!86O*;M d2DH7d񞆀1*węFi/ӛ<5ҭ0.2@Q,lNxhct!$MÀ[|doK"zڗ ^ N%ʡAH^'OlV|*Aal~w@ y:# [ 1OM<z,1̨"0'0P {E 'lur٪`C1%㤴pd'mflX0tfv0&[<BT;LowWrUCWw2G}P.z߫鵿W2A]QC]wzUU[n쑛A<k\O%+\Й1HwW+&ۛ³hQ_UNpluAp>u闀of9$gxL[s>w]_ {+֮cRH37:/.F[E:ML26 a+!hQ 'p3u>ȂѨ}]0[͙Slht_'|cxI2 <;f=IN-zLV Ҧ=|Ԍ(\7r=UX%oDF #/x^%q0S fc997@1T4tpekgvU Gzh[=T(s&Z6}BܟHiJlj.Zy$Dpg|(XGJ1zkZ ~:9Z Tߐ&%D$^UX> #sܡ;Kz)hZ S:~ u+. Q2X\sn%<קlC3y.O?IFh go/yi*qF!\I'U~ 1LU ,Y$禭2FFb<*0l.LW-Lm/tWY ^q=L6g;ЮA^I;O @YJ=2)a T_ӽ ZzrsVʊl"aXoΪ賣 ]D/Kј36Ni(ajtYemVT*TsA@ C~0NOS$:$jMr&cdEP*3>Sf*x#iM SP!6ҒMLM6o[Hg긏oM)ȈB?+JU_\(o^5Ī>&Q Zy!_MTm:qrtjHru{&{OU oBY]gԩ%Cʓ/4cZt}S83TViEfW?ؽ8+pM;97qz|+ $7GwdF-Fi-G,Ķ;5ic?F7| i3֯XװD2 { _k N0UaOjm9KsMjx?^'a(aq^u<[􎄽UBƺBڗ;.If\ uVC/jOF+3!|UđuXM?q@-_hfxYBxpZD }'X-iL} E[><2j 9S{9G]l5o~X`۶ )2nGȶ\B#McR ZK5O3k @!E$ >b_EtfsI/Z9#mZAEK,Xup bEYDǪ._IW[I%Lѐ}U-{J8|ɠ3\wu+XAlwiTƾc5M'Eb&, 5z4đ!e7/sZ?Wй4IFlf Kԗ3k,tVK a@Qp93*ϣbnD[ŀ..\]Y7,дmIop+x.nV+M$벙8z]74&i5,&Oжu ,UJӖ-2D"% K@cYOQ,bwL־ +0V[_ smeDo]ƆW Vc|9xMKTA_>aqCOg]Crc3SueLog|M|KE偺ddѭ3q,(m讍bm bev@ P&4]C\0usNq 7O-cv;,Hk}YWx2`ǻ!4AhF:+;JOh "^nsmӹF\@HJL-s62o=$m1#Q_>eDR<bcZĢj /%5^;mx*V뮥 /G Gk[bRj9#jWH8(#֝ ]9w }}7F |>psXXb%j&@[YDb#FR %0\ݻ73GDTIɲ?qr5!|_x W'Ifw_@ʞ65ϽUAR@9\ c+Q{i9^ `q$Y;:8֔ީl#)Fv1}hp% "uzP;1 pݺ1qSwTlg&W$k}&DZI p!f2GAgĪ򖔿YpF^3^AB24gݷfkL/|ڊN,P>O""S ն@-IPdEwvs1ko`q}_-)`X^ wx o@¦C {oZ<S9  o..J 1&DsԜtR l4wI$;$4$7CiM9#z_(K_S^&$v9׎q>x OJ3~dvL+ z VuvˏkO$ew'_}U߳@ԠtUfϸQF~ pr*w(ȭ ,Z$9MsdƦ$vPmGV ~[W!ExkR/wR֜d/}oM%,{= ~*n.\C9K-6:^Cԙl Ľw7^4Ø8aySUP\O* ͍]ln™rjb$V ӍCiD֥sMy &J+0O,A--OHՄܻ)CjDݐRq(8iZU6v:+}0zLkQǩ<3_ז褛Y%+XUWW1D u@[Z~O&c8w(ő`LՕ?@XS"#^A݆-'Ba+[^PnMxL-OU+Y5rg(;wt>^vU߮ ed'7 1J, ыW7db+QTleԃZL` ( \bsn*Iqi^\?̣"I=.[)@ǘ3CYdnXBaLL̑3VOPRM|( 6ď›;vt=~;jju#^;f5O@[.<X(;VCz"iT B2B *I{AʎQ ;U3L3cO;rerUtC"n8 dH#拲\X,#̩u4*=2/=힅a#t"[}dsTѾ !_1X2Kmtd.'EPԏr Wj*VjϔkeӸa}VUD# 7Q6yGųY 4*3+Ȟw[,PD^}d.z:g (#ۀܨd.NPZA]2IE=/Cވߴ ~aGOb4#ޢmIv?i!=ć8n BB)W{ LO>UME Q &S;z5(d`RkSBv\ w!ą74 x/t`my*5bȎX8 dc,}f oTMJ R]]evwJ^Ӷi ]!{)I0ċC1d\E m7@x*f]aB0}Kdᩲ(3X1=7 &iIjJKS_A*H"Y&]tLe 9(-TuJ@76 NIߎkSKK=XYbUuARAn3-lW2b]}@Nq*PE?*[hg[5׫Hˍ.Kr;H8ARial@=I u m.r/J(٧lT[!n/,3HHztnoQZ(MnL1?;(n-3M|moo$8 |9q 2+~0t'g-Zi}hpGvHVܷjzAv-r B2`RaV%<[Y8J~9'ܸ<XV} uv6{9#Ƶ4. 35`zTrWRlECYJxNLqyZ荥%犭w ]5cjB#ݝyvO 1h)n@~ Հ5= ށs{N#MDX tꌶy&!o-4(1 (?BDŽ発ym%b}Hp_Vh.^ 8x݀3|#BThG۞նuq/xEhъq(gkE˴Yi MRWKH9JbE∞pSH]g˭EgF78[8Jrs}0 йJKҋK\[ko(*K(%,{}pv&(Q:)Gö2)g{1NYI|XC~W鷡>xRHz~,.UW\+"9xGw /g+N2Ii! A\NU駱n"f2ĝ$v(/Q|1cqBVϲc=<'#0; bS,hiQ:ch1_I`\n7t=+ iA ,ݷϯmɱ1YD4^Q0xf|mtAcnmCf.M|O vI@]O5[*׿le޾:WD|"Ůp 7l:5#0zp)a=Sh؋tgyogzOyKJVyŸϲ 84("+ʵ4s}ĭ6zI&RhqafѪX&o9`o::ʡΨbʒ4؊izAt^S#qkꨢCNSG9`̡cOb[Wz,&9w/BZ!W@o,}cxe%%sk@rNuQfs qs\È&0Yo|?=2ҼJǠ;?cw&@[k}M/8P˦ Z9#5sc \8pxk]WE xrxF)#ߟAˎրU)fЮOB^|doB\+'Ag]vWFzZ? gUXVsyq?W>FaI]ƸC޵6YܐGb{$*D#2lB 8JN=" #b_ՙn*F6cF̓)ll9D"WsT3[b^kxaZviMwkV}1ݝ TvIWm>Ғ̓f$Țח'N d=- >[ٿ raZh<(xե ޿fV"r..`C'?KDwLBkx䝹Ws+j+8<(U 懜^]DUwdco2RT-/-*7Ws{sK*"@,a\t_G\N^L=Su*3# 6 pFR鶄NHDE;#p}u]432lV P$"b|ܑ&Xۤyb_E )ci~f=(bl(U@6's 0yqwϩ3Α} }_.jFb}-wEY? ZWSAkɞ{bqzE#å[)JLO.l?ϳW[Nſdck H{Rcc5E">:^ S:<^y U" NR1Bm;/< +蘭uclIPZxL.psb۠9]?Z5][7K_ oeu@ v/g FE+A hSi~ؓROmڴNxULXC" ftXRήۢI4aC=͒NIx(D@:F Si M\d ]1Qnv%7ƞr|pM̄7{}:9A uhsftajٞ (i3odQi}wCTx*>Ux6LEj)<R/쪆&6XжfNwm)< b6p+S`W9=v 6* |xşVxKOkRX307nm-LWl™j$V#ӏ (r cf B>JOӚݣ\eDQU1Yw7рkKVQ$hYCǥ#w^y/{4-35]`s/MXkh?HNWHsΎY\jNt 1ONNr;Og{`9|^xI]D `mǕkt aUb&U*F >?i"ANX.D@)($*QaB˴ɀ)7h额 ԐEgR(5hM1 ?[ܽZt n8DMUUg3b cisPfn0 nTtk)[fxᄆ$AA3N 2h!Bx^JF »=` O8= j|ŵ" /l{ w̾4[m;%u8S:D)axqsUl܈IR!,2\se|訍TWiAw$:>b-نx6ШiW,^5HsjȷR_U]X!4xu6r 5Ǖ|dhuC,\ ky&P[?BEuu,:ZIוQ$A9E*v<_n/R#M'oNWP^5w S$C䏿[<ipw 56yCD$8.4XJ4LJK;㒡A0#p':]%"BMT:HQjW~3x4^QbkXIjŷ $9hEGD< 3Ty+h2gAGgrD { {ڸ{p-ۇ;@xdyҧU;U~b7އ}̻*F><]^Zu"*|7&ۧ uDcIH' >[gjým c#\6cx˜XA\54xK=0ԃĜPRJ6st+Y*[<@^:Z9S_c}=_ԚZ\HiV%E(0ڷO4; yyv !`#V5 jI>z6n4HGx }td_W8mӀfMتKA <@e滚 \%6XQ/_p&(@;<_wnl2"c;ό vi6_H{ǀο\sw`v`Hr.# Y4U1b5;EM[ W5[ƭٵnſb@k>jO(-OO4H`,8!CB!:Al3FPB-:JTKE@-]~x١GxYO'nCK7^8EANR#3J5# Q8'&b)4R\7eLxi+сi?r~W)';b N{B)`>N&#з9"Vb!Ḱ_:F1-)꒬%n˟1z6L &K{͛׼}hըNȺ a8Nli@i$ Ց|o~>l{S+Nn EK=d|Y)/z1TH/4?Y'+{G"jE[ d )Z-íă В@v#HPhW-kEl?\g6nK0@׆wpbL K X|jpl9pΙ\,dԅkT):D4vlA\KNIPPz 7eyCjDI(`;-ٛNmq)XKZv|MIX%x)c X`a9XtBRU/&^=#)Σi-lCrhx)Ƚo-@1/ܺک,>OEzPN+upw;M&-*2W =?GR%+ԜEvĒj\.* TAN-}CͽNAdE JΆS,{$-*cRx)O~eȚi%4x 3ySUحӖʋ$!YYc~,tYؤ[V,p=h+#O\sr`TaAJtnp ڍL#z_!~ws #f4 #4!Ob#S,7=0U7{5+huS|nrӵ2S.bl^@[H*|K9YI^6nvmq LM^=J\r҈(6h|#)/^O|Q_U6y,J MHffLz vZٸ+2ςפ@ȷ6 0?՝ѱLjN !~m( -|Ԡ'B} ?Xb/P7/ ^rZ &X,G\B,9Xft;9  ڹ&$y{΅"%ɊZ84Ӝ}^ijUc1'و0XnɧmV1 }7'/:r%#`=4Wʺ< b$ ␤9Qҕhe[h6!WoQ&jмI$?7!#WgkKfmVr j@ӈ`'|`F@4Aa6\>DPg\#(^{2BN(%F=Uc:^` MR$C<=]!4F_? VE-V7ilk,PnͲ(P (oik([ OExi;j,ԏ m;uԥv2VۜPjU!T .|QrKh}C5G0grJ{M˚};_dvV( U?|l.1DмbRr p)KE1?Tn'$20j=&(`6eC I-YlZK5Ҷl4rǖLrGVәOp4DJܽ, 'JRAHH=~_ )FQ_krt4]FGCߋ\7>DͺcK9"4^<ؘG;{#!W_+Y;&U(؈\78OMKcX&>sU1] Ƅm)K*}ˮv4;+)0>6"Z6RWSکr H߬EZx ɞye ɫ'}9}4SXu^B1 TEc/g6 e⽳MyC]O(1:&)nbGO}%("E3-@=~w-Ub>5ݤ@b. ;|hl-yD)Zm(x\"ǖrHw71gʥcx04Y6 $g0#P ٸ f/O^3ebrHIT|ըqK?S#GuK _B~weT)}{)V w@h.~ ;&NWCFt8m4N?, ZEգd17a]cqWP ڸcw6Qb@1gkV= Ӈ LP.0ʺ%(To 2]B˜zp^ wh; @vJ̀ 0ۙsܓҥn%xdj2$8liRo谗Q& @6Bůt a*[UaKnitfΎp U~wH/kg~hm)˜#%iLm>PGX 6=twݏOGZm WJjzCp`xlٞۦ+ZO͡=zx&вS~߾4ܛET֤רMWa[$ gMekLvv:DT$ͮZ=C&B@3HtVӽ|VPbp–C2(*'7`415}jZk%n|bΡrSBoz >l4,@6 +(2MS=ķdR|5^^k,QZPxjK40DCʯ9w/ iqFrܓYKM;c[K6JS2ѽϯ"*bwR `"q<9\GiJ͎A:3O 'fW/Lq694\xیJ׋o [| RTdz$r, +HA攜P:m{薰Ruib@),c>qT?t 9E;bhy>cl-QDm.ƬH IP^a_ϒ ||_IFm:ybko&{Ft/6 SA[%q;'uq0IUt_2{H~+*b%S ?Q2},$Gŷ?7bʵ%rq$ϱ"f Hq^ϚƵŖA#gVʹ]9Йd̩ݞ<h%5)mGܑZBst7RS)<-7{T7P٨֏GڊplTWU$m51A$V-*9kL"&-kSG LY9$_:Zv[7ltrO?V},,E; Xp;`S<4|}48.$+LڨYCv;7y@ƨHpR3#qD $KOz ԦK՛ōQNNߞSl:7^,qU\7wɃevFV*X&tVX}ZZ6&7xtTV1J íg9M=6(G+0#,!?D$]*# -[BckO g)Hz3|AX!CTK{!*} SX e?DX Lv6yy}/ ZȀEt NS[G3yh)+ & HbO\XY0zJa"o x率&d }3a`èKX#ݞ|XfR Tq^%rC gȍ4IrW[HIV{hcXPd\kϖ)m0\NB24`cy K ̻ATsHjMJH_տ\x*zӟl{\]6֮#?[W\QH[F;f%nO|_ [Iå<{9 '{.5#ϤZص2 #MŋN%oy90a7#hGK<-B*L k`}A-Nm!2~O8 ]%-yjQ4vHYbdjN= Q@TFҚSnG4RFuVX/mʎw_En9' PܨXxqxJ܁p˼r!Cc7$>4Z$ds8tdeXHard(O·A*(%#eP:sr[U_.-tzl"JD6,)KQCZ|s=fVkN^ID.)S#hS!z |Dp(loK9g'nj*\j#r>n"hr$4- ܀SM>| ˴4*N+XOkCnJdF1YKN$ +U=sgXGdd!u[SL jup*d#/ʇrI"+ nyn>F ܙ}D."5^zV0YM0WvԪ0G;2HO祯ҫ/'l+miiZ5l4־F\4!|]>Zu&W9Eēip5h,[!u-%ʣh :Gt0Eo6 ^FvٟqC츷ہv\ZX^5NJVa B;@+둚|HP>ɒk!.UJ \<Gk2p#%7JѵrOӁS2qa$ D2cR!-ֱE9Њ 4LuĄ+%:T7/r6X^˜H7cy w)q?f U ޜjuHo`^SeLh]#\ # ߗ]ꙩ^5/; Bie0`M*a1y *35Ӹ%4E-J496ӪǺ`:M̴-݁N͸vGgHUS١j4sKDf@?q@I [@1AטXIQŤ<97a<ſsƂE=ޖu8QfrƔK>/r9vlDC>Axwq.?LJ} Z yyu JP!M) :ꗐx3M7^Q_gC=,x'Ps䬜="ˈinjT,}pwN4[a[P4DW^Nʱk>Y!2-z腺JtaYۉ gkt.qa]B!!ydCᾛCAƼ9v# jMT3dk^\kYknmLM5B b)6QɍT61G sJ0ڮIn/jؽг#xK*ôPvX# : # "ha4;rQ+ث5Ub~+y&AGe(I۴YmF9uTT0 J@F+zە=hjGuKδԫvNH"jc2WꉞPhlo捊@]EV݈bH'Q mӔywAZGKIեށ(U:;Ӄx W1B)am @UXJW8RFmF~] c[9<$ 6XnDijNS;7MT 08.A$ &?WiWI9+$۶JAGmo޿dx|*"%ZǪh5O~z } ,ѧ!0i8l PU3[Fqh!IgE`+Jkׯ$Z D0{Aix!/M1XҔjòyKM7Poe+{S,ͯw=TH:{)\qgc3$¤#J~[B_%ץ9,6`s[SH&ͯعvia\H3)u2)Uo#Oib[‡=YD$RF,iqg?N\a =[HD5,S<,l%7ٛ`%;R* ;v ߾) ܌ c J(H娂$PFZ"BOBmt'Dkf_0kk9Z]pә}bdq]S#dihs/٫CJ̴ޡ U'^̦6k g'* wXٹ]_C85 j_&j/\Xp9TEWkveq;J>rm uRPLfuH`m޿8ْCCetXmx\uM_;qF~oFLu(/E!1uLñf9hdjo|:{)8kҴMP pP+7KKs FR/|AKKt˯W A0IX%"3u loVE~'.z0,leVT)=(`>?NNާxyn*cq醁EIGVŎz[VC[LR "XС7dZRK8 `J3;u=xgqaQcU yEaYaa|ls FncT, Z#t2uH;BcB|p2Z$Ajbb IuxFڋ .\J&GWcsZqtO`jQ'tU+sAೖVgcf9)G`HGt\͖`'-IXm|A\+&y"G\_T vˍԈ&lWNSWATs4IjbwnP~ Gysnn{;X?#pHL(q I e-X`A:u_p$רҺ> gfjx/Wl3JY8Ė qt1"7a gʏu}16ZF<'}zYHP:a9?emn-i9DҨKKU JUgQ-y,V%9wIfi+KH6gPIn[/߹`̉tWHGB-o_bX ! No7ᘃ 'EPmˠ:4A2Qqΐ3(-+`b u#zu[q3v@aw;VWkG7+fVp?^֟`i8wMwE^xkeJ84ݛꁯ@ mZtTLŏ l(2zE9{E l.Ħ}MYCOb\"Ќ/]FZTQB 5?-*7&Ƭ5\ӭ%DwHS ݌.<[&$ſjظTכTֆã~{sKXJ@r׎W } yh:슩Rǹ(^KbLVo^0ONv$Uœiȏb-QU:|.述N.I a \Glϐ*HږIZ^]k۶yȁ>:Ҵ2Y)]xKI993)dhʼn՜U{?jsoZCLoqhT;,9;F= I;l6[zQx^X04ȋONQl¹LK!Vl%;Nm U*CₙXt/hO}jU_0Z/8yuB¤3 lCӋGU=Dín rK>= 5o& Ez;֖Y6˯=]&T>OMp8Plu+"Ns9VԌf [_9?E+1~5_`Y#!-FrM~`Y-- mCT{XfY-t`I:щvLVÒU^ҀVѾ6 %SĊѥ+4icQs>! B0wO_NCƨ qUzp1y\b6ǯ"4#y`b l3x M6&i)%'I9ay2N.AЇ,U8?lqmmwh[=Tp H99-CjM >X?vE[pq?GOj:yZ>vp1&UntiֹgwDgDdZ1zxC<7 x.SƤq+{|פa`Q3@Q3ց Y=۷ j~B:`(vcC h'3D;9 w^e;.Qӧۀ&C.ѥשfcx Z8@ oÄ>ۥQ ވkb$NE^:Yq 98w3 )VL- yݚI9"Ώ*{}qdv‰'v鋋.Mmwb̄eֺT3v'S_2L*2eNM2/ {kB` ̔G|NXٯ8 /KR!e+!%L)(w|NGKbsNa66Z(d/XR(KI$)E0cŋ+02ev nkbvsN1S.kJmic0bq'QY4 %/YC^0#h)Lh1ޣ|GM\Q$j d>W-K+U '1V霦= 9ۢEVy~tncQs@'䇔pXI*C'C۠N5Y3oo뤢!+Q~DC@gk"Vf+e2zd@~t1 H\1?ܙdV;$ 0vd ~h4&!bՋm_MF'fQ1&9@im4F+G LFK'܅IɃ?U,lA$ԯ?c+ܷ!'@:M12yٹ?Fm\Ab!OR唩-qB><ǐ!5(Gfr\IH%tl]@fSMໞS_D%Hvuo!lj@e92?w:WEk ]wjmmq N+XDmB±p T؁\4 )5.F)aFmEu2YzM3V$rS1u5Jmʼ5'ܬC~.hmxX4WsI{W2\ (q˅Bc&SNrE򞁍x+\V( 7BrgM 0,<'3jtHK6-s2\P=LdV\eSA߼J~CMT_]ГA0|U?Q;^6ݏ`+7> s%SuU'"T宠0K& dv|Eӳ*nMp1vfb.0 VI`Qⓥss Bҍ+R>Vl-y7~q&)5 'g c)T(4GxQ~Ά;ۚhvOieCQ\tTH@T `yqe%N:pHyBD/&\ MA53TFbM OS1H1ϜQ{8`r8]Phg7 .ĝRWJ00n3,]͟-kӚҹ {F\< >5 ŲNzt;[x}!ξ':jFߌpm"~ -KFSBG#Gl*w臚戮%"Og^41u/٠bT( mW(9N]5rRq3dɃBqHb,9ͦϱ[۷ ~V !< CJQ-sC1; E|(qZڈD Y?bQ؉/D2L /o8vAR+D:8<%u!~7VXUl*b8(3׉&`9}8;xv],5d\uw^Y_쯧cH5WF * N?!==fgL}dð`K9%2~n3 T,~]:uMs儅DfTcE8X#J~3kezWF^Æ`jUeJ8j_F Ut?8 ;l jKЯ^fzYvwON7U$_jOEC\yN(/ƅ<3LΩ9DSujDXsAk*C9ZL!vkb_r2Sqj 2Ul፦ՃLwM9`QX#A@o ~Qu* eJ7@t|,2A?!|u?XWP`A n-Ckd,0m@za 7sj:z w߱ˮCR0#u_͎*?-<`F(;^57j!Jq獅S.?4N01'Æ䕍k69,g(Ip~wEU兠M~I#] 6J@ "UEЫv=7uC$$W AU=G/3A}> T%F͗|7_0*ik#{nV)B1 X[R$)Rz\h]aivs~ MZ,ZK%D_v`M<*,_K_WVTH9l:5i_ՠ-Z],U>leɋ}'Z7@X6&Qbtߕ5~Xpr:,H仵+bTfI;%US'﨨'BrE"gxK' 1 xn}#~0ndBX0S9TzYD1@̠">cD=[ DSJS٪А >!AI[[uf:+PH%ۭaVIkgY Pv6k!#Mcpvs&Yr~M jQ4Z{ia\х@VlzL)p u͐˅ΒNB 7ωǧG9|c|lDY _Itn5.31~vQ55=:ҷ).:J ~W c^fo5EVC(u_?qOS쮫n(~U*0jJCdA)5ζg~1-IҿKV?^w qk0\ExBQ_fl,相L+x"f*YbR/i/,BƟA99mϵА 86Pks͵=ebn}wlF9c Kճ7k[6?9i4%=C1yn)q|DսsLÖkR$}6ۖC]@Epʱ z.oI:wyKK= |c zzk8urP8mvO[m>Jbk  voFcc<˛rF-2>w~A;`%Q3" q$0@/$z1J᳸u[k&Y)pJSucbڕ:c$?@0{e{R,*j>U0>qo Aqk <vzAk~hK󣽋Z#؀E*h}W*:pn=.ɢ ]}Y5oh2Ѥq۞[?M8-Wŷ8OSXe'a$%gq1\. `,0@F mG}x^DY&9ؑw![@yHBT5ps>bi=lU]YsA"A; Oer >.S |i2*`_NBGJ=E][2{&i=D+MWcMh3]B{p 1eX&l:z>(:n:Le>K7"v.'s{HvdSs?kk ͍)an<9?C8:WNK @Y'0TsEyEy\, d<'4Sd OhM䗨o?,AR3o.`NJv=C]Rԏ_`_6ycX%kۿ-Q6T i!O@M#"t F+s"љ(D Ek֔-3ƺǞ1v@l@NǓ4<))Fǹ˫eE/?h!_5"悭U-O/@HDf 1>y>AsMs n&r rT {fK}pb>_ٷHǥKq L@X7Vh(hZD/q`zk@뒯 f`?LG,%;rQlA`nytۚ?R7S5.^.&MdPCV-~ W"1U.8͑;^f+*GR\AgMpbE y&Zr4e&6߷duEfbB83B~Y>]J .gmUQ+έz\9^c@XR<9E sϠaE !s*w`d;riUzC,WM,8O@pD q -lYlrEއJTҠ08`;e8s1maڨ{Fm$8xnUK'B>" vtG =ɰX<_`) ̫tsC~!ĺ_K4Y}OȂyeVaf)Ӄ@4d8j;-e J_'33ۮpUϵRUn:sAxi?=j; *ls.Y Y*S謣JWZXt%F&Sױ{Acz2A _)bmqk`vGR@ Ci{V gGN|Q+lϳ6^R%=mbg$*>ߚn2vxE>-K8Eh8`>޿8 nv|x1*IK+4ϢTQcr s4t)[;80DƲG4l/4hk7$xn+c˴ Orʇo2t,V v ෆS2%Ctr)6P&7> aUYir6d rF`zfAȯr`b|uM~kFv8G.)rd?0 RsdEBlLڰC7aP D[yWg|36]繆PK(b@0g~!̫ႉ#j" jacA3}1U}c4cQF؅aJ(V\y\ .mf :s6HzDt':u][|8O:nf4Wlq]֫r>cpmzM;&էLA˜"wvMgs|ӊ*1RQ+g;Rvj!)̵DjrP1IlF,ˇ惔5O"\L7bUg+D֠/iͷ`/al`WΚc0j{~hmyաLEg)Rgo'})έm""٣W8mu BQD՛y# _Ew>jP~Z%d1!|Ʈ!z RŎO$1U+YP̭\J!rҲ3J3BulZ+*#Y26/]b>f;(cy@(!>HB*̻ ߯W2鏧DۭQ> ';,E_PP$%:6`kŷ<k|"]o# ;i}ȣRTu\ugz €por;xR#"%^L_KsWFoj$hfXO=HCy+NΑV NQ)_2Q Gg̊ޑJ^>{xTÅvW//V9>-ɎO M ӺA,.n"LFZAk m?yJs䞕BqL^sIQ!l}pA[rI<Ҽ 4[BcՐr W)kZ~>+y'kc2A]T+tApSU/A>k 2;[D%WKY+&q˹`oGTo;P}r]ݧG猥}r`z6PJOcG=_6Z1쒚|Pw>%Xs? {q|Ns ;"K>CbǕbN}`˗Ib#^> +vR*B. < efwxfMR^¡Q-R͖&;i 4.!(R !DG>θVRި-^7L2*gO}y:2n_դjG~].Hڏu??Fч}[-zK oFy+A7|u[wEeDSEFlr@ͩtÖ"=niVy6q 𹺬X%Bnn%l];+Rwmh%ЬCC0:VHBSax^@!T_gh"΂rRbn֢޴kk^X3^J%;΋W+hh۫44ABZ"Hĺx40'_Ik0Zլ5,o3eFS" HL!EFT M83G_j‹^,1iq  v% WKsq~pD iϏ47BG$F$ AP$E4lRif!@@paqU7@y$e?ېN AqZgNNiy arʹe *dI2뀔MZtg+x)iVC3$N.Ɵ WK,e z&J)gdKφ~" eq"nd] Y<&`DO",h2Z#C>j_W "azmK (~㜚>5$Y벟}] dR\uuu4;hDbC":mAo.TبDGoYj"HyUܗ Rj,n:} k V$гO@~B=/ 0| xMw{'NwY~'Sc_{Ji~*f'oфk @MU- jy9BH`qkccX [m+YR'_0׬ߝƭHd$C"%ܺ#*5[E[GYJ8#®dlNsbƏhgN~>2 ~LZЋ60`r+Oyy-^8 in'}fs5n|VEfly2MMRO4r8C9F%Dtݿ}П۹8=w5ұF] >Dbu23e1ȡlBNTcVtn)@K Li1ft9ks.Z:5|W!tI~*M,]dΟ}uJ҈xZJ@G:cXp\sF:ijqD8[r ճtHW$4//\vC qE9#F-z 5JC E :U^)0҄xF<6R6âX[fNNɌs7ۤ)C.>k:\gz1'{b@f5RӇGP7F$W3n;Lc)\xWTs|8LW `B߱AqYz-*zx;<˒ƭâ^%[ѫYl_L۾r[`uhX#'Uvv|CXN |0:]t:Aق-t W mi-i3wg2yHtۑCDڜ`eaEt)vTM0jʻdy@t7 mYm|s[ZaȈeJ;gb ~I! t1nJ\!-Tm_i8BueӾ &h)nmL5ަݿN>J)>Þ\TXqԴп :P{oEsq@{ԗr gZI.Bc_w6fl5 TU! Y&Z%_ki:!W>C?|'0ip2^6%GLK^ny,1.̬ؗ靪myε(V8 $vs-=1*ci4w&j=Kx -_Bm\0.$qM@du/ VG]fHdr;)Re(;ы 8H'd仝TsJ!%$}:0-cHeLYu%.PNCYwDؼ{u~qNNjU-*dU.O\gII7I|yj\zm9LОh~= TAxXa ky]ķ>[=JJk7J(e~ՈP|Y^9SBX^7xy!ْ<2^ n}-B1'KgDpeg9OFmDݠ\^Mt5 Tݟ{>/EpIP*Ur1\ӮO|P,D21 -L oM|ߏ?PuLQ?8l-yo 9Dn@: SUe!W3s&кz3v(;U* 6K> DFRO&¸+r'GpJNR$E%hgG#Tnzܹ|Emӱb C@+f**|s ,ȃjdթeAMU%@a.6Ct qua|ϋ:4b}sd{SEH4k{իN[Bđ֪aƺ!,d.pILp+51"Ԕfoq>)zybHf b7Ƥ^mr,^…Ro  O( b[02 j&ס 5PA#5@BsB҆opQ+hAvf*| 33!*4#Zӷ*m/PUtǓFy֑=KSQ``kemI_T<ťbq7릶islpqL&x;{Wr yZ%z AӻF0eBs'iZF M^#?bQ `%LN"נYv|Iދ}.7z8;yqbٰi~6S}(#0c8ٲnJT-8H!G7=dV< ϒukN%/{4v~q@ק_RF=J^bݍׂ4vwo7fUnvŻ4AҦ`GX Bțjү`:Q>Pr-o\]WS~\ #FmMTTl͏M3*d9p/QDh^;!͢{LT2{{-Ie:gL]MDezîMvUI+vJi_T6*vm(6⿂^g^H6<7#Zxhku&LQ++ɹV";P*`Zg ުB0WʕJGi782I9lQ٫s98n/Pk.CP?p r1 o<lxEAߒ2Apm1k no6L 3{ɞ@l %93:Caۤgd(a$-xiݳ>nQ.4c|YL{-hs ;h@P!ŜGM=M峲{xԯئbf$8VP"WIf*iaq^4{ vT]t6oz>θ;pN{)5|ԙ]UG3&'L]6:e1S@xhpZ ܷ͌D#0)<->{틶$bkvCm7e 9EΓsōj" OL@ru|COp# d};o#czJH$c3qb[9ϸfl<$k\B]]>ͅI/߂\{n|̣v G,fY1 KؔZp4ua?Mb_qK~σI_ vf걄 ^d\ipn몸ciS]f @zYsQK1Tb|Z6P[5^U4tms|,uQ;Ui-!3>/ztQ/?An->I=1$@VVb V;xʃܶ8f$B!vA^]Fhg%$5Z0O&h>-H>y4Fzɩ۟#x慼GmS1iV|EW,k9%'𮧶m!f_ۨJ6=ҩ$~Rf$[5UdlSn $>AiBWU40JU_krS?<'w`~.vsC÷1:k|Z_U@-r*`]!sJ9 tό&z7BܷߋKŚĴ1Z.Q}Q Bb8ThIWk#Bq_c|_SJ9"Zof*4 v]|eG++g_Ta?#6K?pדAiSϘ1W`rS $SV93mSó x}cWBR]ndm>-K$t'WG 6X%FN"ta||azk'ﮙLh6ޔh"5چrR @~UmHAUG8LRتfAv,)rٗ]HL!Ƥ|帻 Ҷ@ n~J*LjFr\k_& C_VApðZ[PA=Qh--vy, fU]Xo$l<*2Qˉ۝~:O"PM>PNW#k1y@i-ECyN Eh\66//2b9H_`77=bݼ+l5ZD(*P%@s"F=ItՏ1L?#yXC:),pwpE6݌Œ<6Lٌ5"RoWL7R4Fo4 j%U-gW[c7"a/_ucj2QJʏ.ʍCI'#v?`VJ 2p"ryMKxlX=>6l/L( /eڍY[5H#ڲ~u :%HΖOJrig>\ⲹ͖E}s R?vȡ>"4ǀ [pxX}Xi٢mbV+/s~gAr;Gut &1- (2wQGNV ! !Pbmg8މ"z5)J촸{yx*Sva!pe^.:>| 8ty5σMsu)9.< 0@;, ܷ6x`s43+﹩V+j9j<5G!Eƥy7SMA4vI6i :ҁC-i(cirCsF@Ys{x/*Te+uI@Ζ&s(\{(vb-~W$r⯜Qyu?kV{{E$TUz S//Mr%Ժ7OZ̯ {Hv=[2mX>rӡM۔(< S:'Rƅ@^ ++k,KqGYMiIP~BlC/],(NB7vyz6>-$3(_U,R=in[A o*Vv=>I(GMŗ=8q*fZJ G?o͔q`<VfxҧncO9{+B`ĪĀ&[*)GΙd(f6rjg/<,B :dyK"59eqv[X>ZM1?Zʼ +cdXu%Q^1$iYJ*҈p޽$V Vi/%>+pҹ _qDjG0-cת=HHǫTkhWKM,V惄P𶖻5@ś72/ZQ z?އ_=7AbEI1҄~,~[xch̅lOH!c-+ wkKduJ0"!$1*ŇۉͺAL2_o_M0l43)-eΎQQFF^Z_$xweWv90zܦ0R. qACG&ɝuPV򑭶]g+HYж\Q -a4ؙY=Is0J /)vWt{Frwa0:z Pø:q)o|Lq0,>I Y2gcf?2_GV I#-B!;l$jm~J3Gʂ}7a1O6;ƃ4 l+)Q-7:Y)EY c|_?ŔkX9/$ݑg==u$EU e͑\pݘsdN;Ȯcx{M{:~:2e_ $"#Lc`,i ]A+V06E=RIC~)(Rɼ]A#N3qӠuXK%0X!]Aq"U q#$K3C&M`/x&>7&c=@fΈzM[Uw^^~B俫dHDKb}i@%Gg;5{ P|j(nK$i2/iG' 0UD^Rd{_iM}vX*O5mÔÛ߹㑎NTsQVRjTHu^K@ $%X*L[S7 s2UUU9QVt T!06pHh4:]s2UUUUUUUULQVt T!06pHh4:]s2UUUUUUUULQV}kyR_ $%*U`-ā fX@~Dww-WVu?Vu?VSt$U{*~ V]Zֵ icnV Bitksnap/Utilities/MacOS/CVS/0000755000076500000240000000000011560342171015114 5ustar paulystaffitksnap/Utilities/MacOS/CVS/Entries0000644000076500000240000000000211560342171016440 0ustar paulystaffD itksnap/Utilities/MacOS/CVS/Entries.Log0000644000076500000240000000003011560342171017161 0ustar paulystaffA D/BundleResources//// itksnap/Utilities/MacOS/CVS/Repository0000644000076500000240000000003011560342171017207 0ustar paulystaffitksnap/Utilities/MacOS itksnap/Utilities/MacOS/CVS/Root0000644000076500000240000000010011560342171015751 0ustar paulystaff:ext:pyushkevich@itk-snap.cvs.sourceforge.net:/cvsroot/itk-snap

UPD^Xn6Z`y(ƘcKS7Z`fY F T@ 9u TK.F s2AxA7c]hzEZk]xԝJxo1K2;w;>䏫^ZzFaI{pB@7L*Fp*P\dH(@[q^GQ_ ~dA&~l;3,mN.%T % ٻߥ|SAȆ<`@=3`P"8 Ҋ7OV8|<&2VZ,6S%[6h@.v*a3LvҍYsh_&VBVj FWQV5dqLC0;zc( :#F<̇uB0̄@_ bCS`!|a =C -`a8SN\Ђ TD60_Q'yONʗ` 3h8d"5FOd| AIIr;(e*S HP DX de ̀ˢa)H/6i8F #8 E< 6 JJ3 b`֭k [*9@Ryz8:.jsݹ FR&t\IoRUs̺Umt28с*t|Ȩ:iՃP5i_&PD$Bx}h!c~B33RB!4` `t p Ґ c] `{`\_`>e+c;c%p8q7066(eG'#=+pAQ@P  `pа  \wn%}!g(1< X"I.O'C5[ɐ ~Ȑ ȠhňĨǸϘ(ḧȍѸ؍՘؍((TΖ5HQ+4`\~%40& H @ P  fg=3^Iggr5irQ 3#r{WJqT@qU3@@(P|   3c=svp(mEa I%``tn4inp"'űGsmo q)sIuiwym9y0)}9xٗɐsɰb+H7b i$(p(`   0ؔ[70ahm"w1pPa7V  5! @ 0 @VI)#`{Y- Y|`(&L7ORC5wCb`nY~h% h%ۦڠZ* m V "'(J!q,z1: Rp=:;zR?ڣ9*C@D\P~`Q1uI$`72 w pP y@V=w{_xX]=c 0IUVg^g\%z0p`o0kxZrU7"Ki9Ч`e?.V{&CHCW[`n&_ m0^Ы/:Vn/jnf Ϛ zjn% z55/ڠٺǪIjn犬* f t`p Xy^X*vC%Q*  N } 0੟*wn5p0|:@$)` zI`?(q 7R 9`H]YOzj:+b?T.5P4&0>-.Vs\7B~v x L-0>-@Q2|3{K)>?@&`2<0t-0 k-6)V9@!Pƺ6Z~f?^JknVt˻ +p &7-φV cGCt(c ;) h@ I{XVuFw |U o`Oq"or|x1X(g@0c' ѐZp)OyXP5y_0ڊ&tqrT/<0/"7q&%#^q% 5jH7C/T`1& d3|~@6C T 6J TTf/ >\~AJ Z5`0Pap7ȍ\ȏ,ɐ<ɉ  ɻТT 3c3cT/p@V3z=7" @V䗾xj* %"{Y 15'D0i rpsZ=wqeHBL(B3% c(-p2DR Z_00)!/_.0p3@Рv.5T@g|C}<06 . $Ppx* ~)@4^s =*a аx mB3Ȁ/j`P`Sm78*8eP,Mep&ɈՏ|ep␼B-ӌuc> DyF榾 j\w0.0RRDn0ۆ{ 3`"sͩu˜hS}nٌE|V%M4%W0ln~PPK_YT !bnd0bhBi0fЙ-UḌFb c4v=i~0Gq E ,m <\`#''p&0R@,@e$p6`,0` p .+ 0֠`20` ˴rK{H~\m٩cLhp8(6b^3l0.#x.9@OGp\~osJM~vV#+R[.!F,Ɓ⠹ *Mx05 hАGV s0#~,-NR%n''el=qhn:p#=N1`Yvd$g̍ k}T . 虽!.cV *3PlI_蠄s~uΛvvR@=#*5`h´ +>$:Ka0n0'bmK'._ͻjsN *QTpUJ<C~!C~]y B#1//R03Q~ZPU/+Dx6f S0 !Sa"j~v=7%<#>f˽9j_m#!pSS5|(on{r t,>"L=;q t Rm7p```/_ttϻpp\HR0a3]ZM w6`!#m ϛSZ?SO? aE1n̘Z xE !B{ ֭];6)ѤA'O5[ٖ593E6hАĨ-\X3v$1D$HA#H̘"E +Vx1~1Ƙ!, RH>* ff(!(x.0'=bC Dx=> H*r*0Ȋ riL/` L+h!1LЀ.*LaQ p \`i!DAHHAOa C-a<2ÂdJR/Z0;L4#2YivZkCB7| 8f(!* N+HΞڝ@ ` d*,r/̰O>b.?"Q#Bƒ"@>* XJ/c-ɤ!"x ԓC`tpXb3/$2Տ?fL&"®XByb-$+X AMRI/ ݠAK aP*H](u3*l! O,f srkW=Liәs +֋K5 0Cx. > _^8> G>Ѡ-< w @ /f`y`4ly}ة>$*a`HE|a%ј09`2 YH|RC a 6__ @g "h(ȃV"0$%1bJ K6ݰ-^@c. 4ĕNrHŁXI;ÀlRh`1 `ȇ 䰔:rTx89t-jx@+ A BHQI^xWT0}M4F@c4\) TAFnt89ը<}Q"pj?!cRJ@ qSGK%"Pp,98x]DCQр2%w,D^8Y6MocDTJJpc,eqB0፵pI q9#4E%qJr@6E].Ω$6JqӡԦQ,[2C9VPTKP"3:4sdz7JDnp{ aiO T0CJDf e"S(c?P'1K!݇BƀNQ?*;/ е!`j$-8(TX6اSQIӂp%Ir,e^1"ƶĵE7k3 3Ɣ1jb/L0f%hF\})we.WKVz{mBѠ.< Qu ~* J Cpb 7U+HI/|^ivN@=3yYhjt)|P, z2@0fr)y"b+ .-d-yQ.KX3zü.2ά0^84p0j3+TICx~ #&pi̞@ HXByOLtsG|qA2!pWT0( bFɦj (Nǰf .J/7bYA,%NDyLJnw6Q g6yd]rݣLq+.r'( x#dllf.h {E0%Ip31Zg `_64 CFfEd@?3@ ^P>9p!)C+nMx/#ǖv NRYz0 x8d{VXfTAvB2}o7|+YgTvڛ[i.98@\PGyWfm2E`f"R \WT4RRN "CD9G(}:T3{A*2O_؀@; bRG^@NΧF JhI !Oſ90'ID 2 0nSd$pd@*H@ x @,A@T*8nJ2\c+_9 ~=&@Rq Պ2 $A">0>s4% s #'B 9*8 c?j-=E1<CۏsႭh ̒-э()zhzhv0Q$EzR R Xz(EW{8op4pU vE`DaO,WEE[,Ffg)FhTiz"m4 풳ݘ:_r4>B:vtu|GڃGPv\vGsdGxr}("u VήS>%1wUVz5TVp*pF X*8-0+ "M!R 9g]WlV )4@ÐT Q|ٙlo Mzh 3PUUTP\ ULQTP-I[՟Uq%uHnn9b%2Wtec55>7 0<pL-h7B [0Sh)mw1RRo)X<(вh?- 99Ђ*C6fe,mxf}]JL")D/lin^joHiP^nn0^o^U^M^mlu^5en^jP%i8_-mRu_=mm3.uC@ec5TfmHp5 R%]`mX[/*pdES3-TؕOd •{%\;U+[p_YbR]#p/ͩuWSu/#zq!)}H<7c789:;<=mTLhJѮXyblxuNȓH Xd/Mdf=ƃf5{5SŵaƵ6]0ip" X 0эF`b0chI$Ӵ5cX3i*ԮoFNtqV3u.H 7xV0qv747uL4VQ&:W >e/U+/Wf\?W]%mְuW)u܃NvH HUi;meS6OCܯ J]*$%./ ,Jb..֑m~JpAxjPPj{Ȉ/A;Sc&`z݃DeuVT^Sw}fecn1EVe.Xxa6)-h$ېڭ%(dHnNmԫ=pgt~ jn~̦zNm9"Ծ9sAJ>=mm nVf&n~s7ЂB"Qjgf޲<tcF6hNrgh')f4lo#g/ Q*6(+`j7O.s (/Iu#㥶+{h9H@݃j&Q`!Gm a8%V&9 8@8 qqw4q /&!^)rßI%G&'7(z鉏"bjo?<+ЛPi9>@ 0=Q7nl˰+Шt;AҞ  rÚ& y-Y`q9 1@)o1 t,5_ G#(1zz@4h;jG 9X vpsr?ϑ4S̖<}V/X^&qq>?>.r ࢅ"苤 ` /j>+F1r@9؀ ~^+&r{ 14 XGJ8m&Y %Ix(q7w@{zwzH, f 8&0{?"Iob{hn%t.Wi]+#RQ:`KO̢H3 3!QJꦠi${ a|0rVx!4%sODk)Aw+t^H{PQ@G3HT B157PVVb1aEpZ䑆$M2QBUJ9eV&̖RB%4~ fIdR &Y%qZYfdeiSf9%kJ饣u ʥXIk(q!E`Otzc"X`R)~x; RN9EͺS((RIM_3"FP v18ȁB J@&"|"HފNNRO [n!7 !EVklZ1DI4fkq)Wlŗfg2<%lr+L3^|LD]-$D  ]`@@  @ HhIJa II EbD JL"'6щMXE*bZb/zyHXryct]h&^DV;Zd+@9$   Y i) dc{ԍZTxcX>:ڱ;C(+D&LTrWt#Xc`?ݚ.va ÎdJ+Q V͈E3-ĩ9MSk14INpќiMvSh:M70sKr=:7JjAN>?J &HH`> sH@(- `hAׂD#J1HA U x$>(>z4fJ`i`62S@r)\| )w c9S rFL'_4|=UzkaXӜou^#+YI+91EN}z1!+>jK;xgr(A 2ׂX5$) fH0$~r*GKZKV+]y %!m4Br9.\e#Y08 4ZMJϛ`j֭aŁ`y!*b@X-0 Wx0O,8zpfA%8`9DSRaUw7j"/+ B~M'h0 !AqҀ]h<3:N$-LpM$&R_HΫCv0:2@7AK H51a?*؀uMa΂ֺ%ͩW:ذAcW&62Yt fyPCg\cT]("b Ȣ pyҎ uLU}/hC牯#* E4q Z0B|гD1r[2D@'L7niB\n>x,<77p+Monޣ?BkR3*s<\yd–x$v8zïkF?2׉=<1\*#h``I-Zhc ?wXo` H^o< fQ -i~ @>>7QD恺!w_M7ʿK0g@dc=tAu=cdk{lZ$ m [mTOIC4AuFF"v`^E @R]zsE!xT ~唞E44 YAhW}H [ DZ8fW9<" Ǽ PD(l%P" B!>b"T )R-nP|`RMoU( L8P[@ɚ, HB5&B%PB"! !(B!B!/;2VUi !Gڑ!KNth͹ܨVAh@ ĂAS "Bqn쯱& '!gA$ Yn.;1brК5iĠn˵. H$!,$XDM@1E&@@fD H,AM Lnuـ ˊ,2QA"w4"#!4%[{ 4#4K eg  d=YhVlk^x9 @ P,|sA =[R0@H4$a_i$i>tΚ.暏"Q:<8LCf/n4kya6+@ @,0@"(&lB.d52 8U!4H$8#" oQH cP `Iw TtR(O\ܪLG,$t.h5Hu*! P&$0B%h[TmF\5zߞj3@spπpTA dqd`XA૿1qmC2dj*f)'_Yq9i" &yNuRn@%M}|HCY ͪxf4@ RH4+bA'0&j(6eDЦ),tW0uV@}xyzOh @ !#'jogO5tw # ϵ&@7M]GMdmfncItGb!I_&%"=S[|u HU qAf&$G4=8E `#@p"SY'9}FZTX۸FjHby& y7@K 8 NH !!ba@` C 1SdBfZ̠ θ ӌLp;{{*4^ @Ҧ@ٚC'HB#((yD z(KǺ% u)>^ \)N4PICRqՙs: 8#4 <9<1^0""@% #@(LRuf U/i 4vfn_| ŘAd=s}փ{=֛ؓ}أ}oקس}k}ٓۿ}==ݷ؋Aj /M9oDJ^lϋĿPA;&,.P%H@<DilĿy3!dpPx M `"`B~ cLe8܃&pZk~2??ˉ?@I&Yd hPA:ذD+"h0Š#j1!G$Cn%%-yp^ϞͶA;qPABD Za ?A6DȐ7|)1J9x)2!*dDƏ1L(aKMDhkД 3b89(-sEv8Lnp8L/J>Hq#ɦ4`MIl-+/Nj ,kTp!Da6O/x HPDb5nc;b`F51͏-đfryEszGKWtv/]wuw4Rzŷ_6_ NhB9渇AYc.֕&1 af"ƀa&+`<  \`w,Dd<-vaBvb 3@?AB rr<ퟙf a((?иC(1qQ.Z+?yh+@@vb >֪1~l¼zQ/ދO--W׊wSwyw~y#zgW>r^}o 0_~\dP-$sQĒ߾\)'P$f!A. DAVV0JPgb# yp)x[l3khƀ錁^)QL``  6ZQ5?xh\P!V.OvΖ mnCfd 9OTZ x!1a&2dxdr@' 6P%H)H )% VP$#I 5h@)@ xAPrp fP×,XI࡙ ^앰 e9љNuB~<"daCFRIÜKV"p_ V"/h  E*Q;je,Q (}ZM?!`@@  Zx!Ã<#)́SXMZHzpǍo'\"⍲_0\1.\ǛK Qhx+9ۼd%^gI H"l uT '>۲?O|HhZ*`g'OiCxJ|h*ER{Za0a( nPJ ԭ. 0`7ݥn$'; z.P[kܔ"BkR *ѶDtq:T*@p`uʀZ*Ĵs{ۤ(9) IqTEU_Ql Xt* 1](] */>qZ5G'c3xa[@=AYAV"<Ks^x.GRp,K%4) }4leKRoYR!aH ) #,%_cg@y;8O-0! o)@w'Bump a.P]; F2놕 g{ 'PJM:[^[PE<.K{ή093!}MOlPn\GMK1eE$ SFD/4fS!:3iD>n8m/ HM^FA/G@A2$z _x~(^t8pstTݮv}Fv.RuƘ/yS.6`,$8Ee o`Ӝ}8<,'0%0.H@[twAC2@A g,`N+ 0Xmnw Ά.-FH}WX*TqM@(_z <`K0| :o R.k H([@AG DuZ?d `M& !t J <- VSrr&]0D ll~緶%R<Ә::kmڐ'Za(*h2b.t$jg@XC S% K+w [(\|8夬?:Ȯ\er8J 4J2KAT*]J,tJDDSMUY~`4F0e^0Ҟb \$l6 l&ف J8(, Uv8S0:c55RU`j&&}*N HHD@hH`s @DIq*(ϭzFQH*@)] "#WLbdIMu rvp+sgR P\@L*J,ibhjD*V-@\`P^wYy yruH`Mguv 4WPW\1/R4uؕU]]uU ( ',0GMO?P22Z-&ȬBhTY if( ݊HHZmOJ<C5VS4?fN`,cS "5A@hF@":"b5bݤH:V8X'J$1 dOv4i,0ȎS>Quկ`Y^z-I+VDk^n;IowR7 dʓBK4 WpA XS$w]s2E2€ X t?7tIwtMWtQtFيz B+in.PL+EVsBt tP# `6cѮrNfPUdJYڶvBP 2kd?x9|vxP!h"c a=V.x1S{l$iu<+%r|r`+ %9n ˢjm%Cj<1)L@ ڬ$Xq2M@Y7uNZ]I.\ 4xB .E5^R8?V_k_ϋ(EgdRnM^S{S2.bPKb7@N`%KV.Q ka28,Υ3FFH/ԙHI,׎@F \2J|Řs:zẬW0s\麸亝Qs/˻|ع/uTU]dAXh4o{T ‚t=j<"Q`/B3+pFM5̨{ C`,Ѱ6A`c7{xz"~s2.,Y#[=[.ۼQ[ۛ|W;y}Q:v_fXg2fGD:\ jClLʃJB<P+j,Jl6@ #>B@ `).9  Aj' LэJ@`.|/5cKɊ}qsgv<Ւ0ڭڭ۹۽=}ɝ۫<|㋩待BEI@B1Zk8_@KfiZŃ zEcW/ZH_dQbH <=nKPAn$x;r`Ћv\8`E44aRaN/~4QdN#?%%,za.D Nux >~ʹ>~> ½US-ͣ|H@P$)D\[hz؁qc}|4(A6HPpC(r>3b a @F{qނ^$SJJ 7F#d)< Tك9\aZ@@d a'' R`a؟ -ZQE c1)\脫'ϠCRyqHLK@JXh,U{0Бhh`mƸsyzw %Y=cA:q"L7-kƳe#ƞLs-䤘GE*x#x=lmgG = G^_}G"  > ;Ӑ4vڕᇍF,nlK$6scYA;9C|hVsW0I7dkeB 1Xnp^\W`M9׌H/cSN@:ahGX 8 hAbIs[c(gH9xD. 606`/Q"{h@#87]c DRA $E vЂ239wR<rA 6  `J?-r1 a 0Ĕq%T q*}$(= $s4r~፿@"hB*Nԣ 9@:Rԡ% IURԤ)(L_R~tv$)Sw} ]“<8xDYN) F_<0s: ) `4:Ug+t-.-g ^H\rThT ZU1n \ m)-X8\X(, @ji| b-s϶qT3"J?4ⴷޠ(o;\o8z+\&-pKQ>׺mnu]v7uwJvZ9Cvy QZ>^)^u _( 4-JV[@I+Ue dSԪմ ےZ]e0 4 .DDƪb0: VHaH8D!!AB8FaxKVUOS춒Toȣ6pL:۹fIsHw*pW f(%c3y p0091> d$M<(q U $hb`ӎ%P61-81Jp%%}nq }Ojnټ@N@J@'䙺v)MtF h mnп_6z8n@Sb*4K8x*8'.S\%vۺק`.&ņP vߌg@!ؠ{2@n;+! UA" l d,$2X@'( ccrDN 3x n0ۣ9LZ@M @ ;ڑ F܂|u Jc~غ V2uAnb&[VClOOշt|E)䐇-v ^VBDhA E}ȐXX1*QkvABaWK3@>ݼ &7>d=2~t})` w!IQ9 Y6r/QH&h)1pCSAyqL<ځ{'Wr!:Shx-{+"H#_{,&660T?#ZӇ0L}XL`'r`txw,k$#\!\i'B9E0`Z.!@s5y=x1"p`k4Yjt%Z"vK"`k_1rzy DQh8uɓm>=f THwBVeORWC7CcØHŨ*~Pe0| 9VZ@rC788{c?:PVm]*Cku锞$TIeٕ;_*"ə2;ve|i#fu`{yQ==rCwO"0ei5yqP6$*iMA %ژ!po*0kby)xd<V ^V >!Iy9QPh q!Y$xjzn arh,_q0&@@pOb€<}=i(#`kX5`'* 8tp<`T lhcO-੐ZT`JJƭ߃TjjV(R(!@g"ɪr3[;(g{8O^@I`:P}sTҍzZGb _%kAQ.~k剞B;F.ah'6L񎹳X{ᛱ1EA8@udByÚL0&?8 OazSs aдV1 `yс}D3Z}-VpeG p[p3F>|ju$I(o&Sف%p;[fT+ UL%FSP;3K3v4*Z_}${QOFTƄSv _' Z*V@7Uk}v}h* Or1Ӏ> Ȧj(:a$ZG7+ڇi-0t*cY @ct¿nZ\pA[k  ~ Ȑ 6\:l;A\0&;\s=Le\HD<AB<>,Bl7|WJ=a]U N5HeHL_L@R nМ,3aS&tB;>! Py6 f HjnXSO!pJ@50_1h?J †QS3B@76uxYDJ= ɕVɚpMݦ/|0`X1!`{X__ $f+Pbc|\lfl|mliP|<Ey7SWB'xȽ '$̔xKDU Vr`0ʷ4jN^`Y1 * e' ܶnX̕ ܑL|:4}cd^C҂1G@f{q=ft=v-s]z{tv]xׁ=׃ׅ ؊my؄׈-؉ٓc{sf'f} uG*EaL:1>~QwQDx6X16Nx]#..-Z%Aba +n^%1rP#v<+V-L[4"`m3P'2'ٗw(J%s3)`#FdnAc ^hD-b 'rvTj9ap,O3􋡧R.nn`Eiq}^>ќ^~ᡮْ#Nd@*N"E5&CwKAyؚQęBq|_] ; ھ; Xc.`u'C\P>~Trp2H^2.ž('Y+`+))D4]0'c#>safc`+z剎dCmC:DN;ٰ0*)Qx5"_˜+_?n`)E?@9R,EE_~-J/%U?N`Xp#(3`cp`` `  0 h >MxIATA80-6=~A^D‰h1rptO @ >fAO@`@"[ODoA;d /OꑞߒqO&f 8@7ױc-krD &XA FX)<#:SDe d]֪U.ʑS„`yBVĉc^1r44+Rg (]rgJAʔR]i%KXFIUװd ھegF/gɛgV5 6iqxXc9h Ɓx|y\_{x VEn V( ;ܸ>[SϠ2Ϣ chAȣC$֚j в`ik:E49eKh Ra ,H!pq4k6ZEe)Fvn zHqikNađF$GI$ CZQLe(9fȁ3Ts܄Ӹ1碛&3"<|H tB6|:_c5XdcY5f{nWpg#lk6c};b$]l!;c d&@G fE+yxcǏ'cJ:->D(sc{ !Ϟ|S/fSRkܳ-+0 ~G֤\$IE E4r+wuˏ65s9S 0NXZfoΏiC nkŻo ofZw\X"g9 <c#UdZ #*h\X! D(HHx(فR9OoK!P d{p[A'/dZ%8^'G GH"E#6NBHD(M`S^r5+ߧV&lɡǬ:VyH2j7CwCBa!&EaC$ЉFL"D,21Y)pCQ=H/v`!|0 Q@`%&' R@(xQB* f*F)^"PA J*/P!N@[f 'xn?X L `H1R$ 1\BH'zkZR,`@ H'*e-B`9N<d@#Tg=dOyޓЃt'BϋZ4L<:4z!hc,/(I2   Ѩ}4N ><#`s4-cX%f$=LMP*D(DL@i5 I3;ExB_Q I.EZ>9uq^uVG.V lg=YІV%mi=;Zn6Y[FϘFq@& d0@ 0@VtB#wc{ [S*} c)tג E[:izT6 +fq]bX3qm|cX;q[ڐ.mOp[C `@Bʀ@%:1dε?q3AQ0odjXk(xO7WNfbomvx9T0xLeJxBG&Qq\Ø%$Ldb@|- cB']h. 4lD-lsCֳum}k\Z׻u}k`[< H-H3z3dcI,i/Uڃ!;EMiUҀW`P+|#UX8wNHA(z0ThXn?[tp'$q∆[15tIzk8E}s'mkk0ql\;y}s]C'zэu@ \gN`LxiR#TY]\ϛT vCiMXXu 0rZK 4i:VAlfcu}Y4؍ c#ZW3ЀA Z*lYIF'}Mzԧ^D)mD-x6e! MH3$o 4GRM{92`FI4*m -@4đ >&p *ۦZ@r!C G~:07t9@yX@l@|@@ @ @ @ @ @*c{- ?Y ; ww:Cu,9@v)+A>**Qrؗ4 0ԁ{ sk Aq$?(BRAKۂj4C>C?C@ DAܬ45y'"d跞 8BBӞ+˞+1C +#{`B}?), H w.71Pd <;SDEU >Ђch-(G G's$sGuGv,Gt|utxLvGyGxyܧ2  0C B Gr?#+N+ E!H; ؀H @}'ȼ3H#ȁ{`ȳv(ŠH lDG$E 4Ñ+.A I\!aZa'wʂ5H,K9ٟ ä=7lHAq) -#<0>KCCt{ lVLɕl+1V;@[قYNNβNO$O 4LdTOOOϱ\<@: t WJ P )W,B+J9*/8>7 yܓ/ȷ́hH 6/X 39(,3* ѧPQ{1dX C:vر<ЁCF딡p7X9S;S9S7S75:S@%TBSCS:M; HeT?mTHIuTMTISJ=GTQMETK5LmTUUTDU(dc+p +*̥` ؽ!/0j< 0Q931U] 1 6/99M( D#ф@g% kV+8&XڮPBsz%p6ixYYYiٟٝٞ ژX7pYZY]ڠeZUZړMH@Zӟ1 xF|zekLCȅm P T\P8J+Hѵv>9/Mh39BPH\WQJz`X ^X@^(8x^^^^5^ _00rQ^Z {U4.* ˀ" h .h:&X@V4xI:/hRb၂-x ())3n?<VyVP`lr`V %>1`PX_e!@3:b+b,b-b.b,..5b3>c4b0 ac6V (_`{Ɣ6 E ҵ4_49 >W+`h`};c6#; (8ixy/n E`Pd }`( qps`oQp2MLm< Ԉh9yQY22l.mfmfngogrgsgl0 Es.gqgxwtEEiZtNx* ӝ9>L%]RB<5e Y/@p9`8՟/Eүhu PBVU 藨.6zP`\;V A߉@30f){l)Pk{g0(kyft0Nkfvk{tx빖0k(؎N߯m=1h O㉴*LTI7 P ȓXhdE H@j ས)kzhi}Әl^Φ=I Ҟa 1hfE٦m+(N9(z5tF9ufjMIV0 2@ko. 88oOG%3l`P(p> gZZ렗:~- اؽ|# W0m ȥ∨X) >v8JQ&.XQMM![ʠMqqNŽ;f$" b` 9oxe+)+,7-aăUS#˸a}vm)pg&  x^f80uTWuO(Z^f ykuO72`TR?T'PoXwYR)\')o^v_qֹcwp*),B-H|V*a>RQi<-E )LҒuq*pĀzȉ7% )Qqex `qH>YCzh$ Ql, 1PL^hW$ `@um(5{ZG{>`v0tv2u2Pu(}E8viNW{)'0v/&9>q'wX ;fWy',?LQ} j<>CXQ?V$Bρ=a8Q|an8-Y} 1aG W,'ؗXp\BoW+T" ylS @iCoږ5ngc<>r+dVxJr\ь2pʐ"& e4qC8:Agџ:IY(`l jC̬ppfW3`”1M'aBeUZҦ:X;uUZp c<"C n9W(m1\b82l')P9,|3*H-.JA}n%]Ya( دMo H5 c\@d&Lj` %p@d@l{dH7" BfHi%)3VO]UTųzQ ^5/ T`5A`hJR A8f9 c VY+\G1X ^@Gc ԵOϒ1Lq+<3Q^q<ⴜQ\`ID:}r-7z!t?_:1sz aLs^:.f?;Ӯ:.ӽv٧j\}39^^_#/y;>򔟼3_c;/z(Cѫ=aٻ>};PU=/?>3>/Ia驯s>/`V>ӯ?/',? u.6> Fߛ8 I fn`& ߡ _ ,FN,V^,>R^-:mnm؂Jmbצ׮٪m۲m,`tmBA-.- ..n.F.NVn^ZnbjnzrnBn䞮z.n.겮r놮..xztv" /./*o.o>BF/f/nv~ozr/no-o^B/Ư/oooo0/;itksnap/ProgramData/HTMLHelp/Artwork/ttEdgePipeline1.gif0000644000076500000240000003124010534177570022436 0ustar paulystaffGIF89ay{1B91BBBJ9JJJJRRRccRkkZcJZsscssksZs{c{s{Ƶƽƭ{ƭεƜֵ޽Μ{ƽƭ֥ƭενޭֽ,y "*40aÅ#BD/jǍ;)dȓI4%˗+c s͚8iϜ?{*,bѠ.ZT4)QF5ɴPDh)"\+Ea$lK[ÎIdlٮ6wBڧԢV>Em{hP .*P1f7k^8p\j2Yrm5Եe^mYamê o;#}ZwkںqR{ծ -\@{ĎC.g;}?o.S/LC 40Oӿ_/@90 bG;1~ 6z GH(\p;m*jʐ6 uC?a{1DhD%2qP̡ 8 0tE.] #(F1zfL#רF5qoFVPesg:# )@L$!HF:R$YHz p?v(GIJP2%*;R|+IZvҎcasAA0 b&2ies|f3)Mh:sϬ,8  5I X(@ьÌixܬb yNB (lC̉ U;)_ r*t?HA:QT@ P"hZϘΔ5 ԥ(@8P2DҔl)QN& -jKDh@'Lh]2P s/AYPXs'9y̝3YE2cZRk.5M hs %,P3Z 51JLV,Y+Lq!|kc `*0ZVTgE;nZAj SgKLt,^гfE&iiXs7kn[ӵHe^`~7:W V&6EpS{" uAnN-h}t@=Q4=qj 31 p_ɉb(` &fVɑt8Fmqc ’}+'l!︱ @b #WF2<}>HWNβqCwԮFBKi.5KjY6Ε6ts1جҴ׳FbU`z>jQH i[ػ%u}k'MvvyɺmQsvvUM kf.!wmvU}jBvkK375+ݨCf:6f]jzխ-ЀsUioڟNꠏu޲Lz(QiT*Dҥ[T7{\빉AShnWMuǻڷ]wɯ'lplc9"|ר˱_HE7w6 GOқ%D\fO.{cO۾s=[hnS|1Ώ~?}7?]̥| G~ ꩘sPf'w7g==GrB5{'89< ~ (>S6T?:7<:s(w#6=_}2:̣8~('(h>y'9A7 (B=|9׃9bC*~<0|H5G9^< 5:ckxq(j;c;vX9 }[}kcX1}IL=-8: 6xX9x(jƘY8bh|5菶Ȩܸi*y1َjȑ8S~_87i9蒽؊6G)yHGɑ{3^-S>F8>#ƃ6$3{(lȅ|ɗ=uمj3M7MuwV虉٘:{}d?Xc\߸YvWݷnxٚ35m!YH~Y#7C,X<ɝ.h3=}&)ɍ)32=HEɋ8N锳x Yk83 9 8 (*8ylQ4P)Y86P7j툑0ٟQMy}sz!zN|0ih)ʡy6JnӝȆ,f(H7"R>Z}X?z<)J~GL*iz)ʡ Q Ȩx[z7H8bhw8 8nة#$MLj.} 7\ͺ'}}Ӫܵz*<<6 Qۊ{~C-ȄޚRy:"\C, J̵ʪy р l˚:<#pMU]#km)Rc٦,*EWª՗֛,~|BCT-ӾlK͜`{c}vӜQ{S9 M۲}ҁ]й==-k}ۿ;Yw77U֜շb-`ш+ ӻ;ޜڢ uӝ=ڟ}:[<~H̱lU= U~]:]]=+=k9y#nƞ͎=҈܇36GI[湼ˀ9چJ]=\n9άm;`M'۬Pm܀q>>"wR`^Y9l9B)hږǧ|o܃k ;}D )]ݯ_ |5LșG! do o:-)XMW*߻*\z~ |NLvn|N?y:y|3x>K~S9n3/}Ɩk?^̿zܢAn$ݬ/<621,7xqC  .LXP`ƒ ѣ<\ ң#1z!C ("\9Сˈ,S|زaDo|D)_ )fE5r$ҴGȑe R&áA|3Ճ_k\9±=\XbFNA$y%נ]mٷV_=vWFoptcSPNi+ٳa\g33['Ao~(Y*ʴe/҆"Wpc{Ȩ4$䎮OR%]X3@tk7G㌏=[roM?~g`g[b!?-o{  ;x0N$6 AC}[nP39*C 9cO \L`I?JŪG\O4kC,У\DJ$->M8;TʜnKM*t$"ɗ\R %WJ4Cl&tTCQK.ń2R%) MFrJT 4KUU]QXqe;{,BVo 4V4 J4,IuZF)6\UD]7ZʯP'cisYhorV};zR/H ?%l3)E%Q$8ӖUV-~a4zU< <3<9%F+ѡ7<:GT;n?#4:41;ĪM^m1!Y# z,dLhODXoNnz/"#Z7ړZq=nӾc,ƺ:7o= 6.Mܓsqw˝=_ a˕ ] #P9qտthrq/U]Ga}ЕG6Lu%ufVmqPG6XJWY,SXHꁆV[R7èjjI:>H]+Z*YX1]+c7+_-ip VQAh]# )ڟyy^Ws=d\/ 6 < bMr3&0; ~)BUuKZ_M&q X"GhnIJiCP= %d7U_Ȓ"M OJ0Xłis2s==Hl32̍c#V. p(̨U/:LUZ|ҥ@ѪhtbNto*F**SEB rYP;bRE0<ٴrPuR12}9,m9ȗ2f=h70gX& t"`β}s d,gJqvA 2RjŵLSvf:MѸ"TꎓF#+gmzk&˭Ec8b"߾ wwSsrLI|]/F\W,l+Y+KⴲwSN&@Q9e|OtENHp4g2Ԭt3g|57 dY[re0Yc&sQt>pfz;YufC΃>LF7:шn4 =7K1ITK7&M]jTZթf]jXZֱ>`AlĶ& |_ؿ]ld[^lfG͆l[총~Al[&w}nt[fwmx7$>@ wox ~p'@,2Ym7F;x5q\!'M<%Gy~Md\(}s;yysCzщ~txg{{w|{_]W=vpxEn׉雥|_77| oÃS>C?[@=SœʳcD@@@+>c={9y8>={?K@ÿ̳&Bӳ> =,=Կ? <»7w+7KC6lt[C7(ҧڋ C>l7JpX\3 YKDZSDF\DGlDE4 @N@5H|DMDNM$#A28ETLEU\EVlEW|EXEYSI=#ܵ8E_E` FaFb,Fc˿< ‡?9B̼ B]=+?Y? >?#S? Y Ծˆ SW?L񪣚IQH֙Q(PK:!2$-I1YB9)>JiI j2CF'R#Hq!Z #RiA)mb!iD2߾!)*"%l?r"%R""m^Gep.ڢ&"z3B B# ig\EtB*$Ğׄno6f)N2c^RqzPjK&x,ixї0<%LEehe;gq ukٞ$g;ޮ^^2-EO};'%iO K6j/;h'g oV-ͱxJo.Qe0Oh^-L1o1Mή)ݭ0 Yzo]Q)򪹮k,B):amynll$A!*CƢ,MWɘOmvo:#⶗gImrh6tynؔ*j+#.Z r*|R4rצʬlqަ-""4Cv,:QtJ)"q¬iilo%k'Ro`!7w͡J)3=_m:gV +tjZ&v=JYqЯm5xW xÇ.\ѐH=! #F abM0#H *U R$H@scE7nq#T8gФHjXtiСJ ㆏2$Gĉ<.f#ڑ$ `0̑q_6mZ$ly(ޡB ia 1  5aد%RA^ٶUa (Wdr\8oѴ $p0 xDXj]_jօ^h1#=n6ekŏPm\z+6̡@b L0~GZgdVeþW .|n|<.CޱY;2O{=}aɹ {髏>~`]=WƗ 0 @/w DȹfW&Wʍ/\+O|dOvAN;awžj"A>a{hBt嚠]x?+ݛf΄jA,D1J-hx%n2ta{JGȱ]l`1 Ԭ΄l T# F у>CY U"4g pfA zgY _cuD10 "$B|Kp@SG ~V-$$A|MY{4n44 L T|A$wp7T ad8NVB[H 2ApPd{vm0lMҍW  ?ZU:@'}=D'uX AX%8HzUYՄ \ 'Zp]T# ( -!!.$$.''+**-++755?00@BGJMO@EW[^Y YWQYY@99B??D??ceijclpuvq xquyx{b%%f''B@@DDDFEEOHHMLLSHHSLLSMMQPPVUUWVVYYYZZZ^XX_[[a]]aaaeeelllqnnqqqxssxxx{{{    ,$$ HP`>y gqHq`wH4WyNBE(aI3I%t3J1fHIʅ50e"`'1 ^#P:R 3cE>$.RBq20f@$iw쳏HكS̎ 4@ &] !D<8I"#P=Ip2p-s # 10MDES@Ջ#=ypAg\kAb--9CP5/Ɍ( ?ĉ| Ͱ=x`1-R;M4N 'U%&6zVRrE<%CDmLʌ2!Gp051O@V+/4A@TS5 cd0$3+BhLTO7tdQPE,<"QMۗ4s0@ 4;ĆMAc:uGt@;itksnap/ProgramData/HTMLHelp/Artwork/ttIconExpert.gif0000644000076500000240000000276610534177570022116 0ustar paulystaffGIF89a$$!!!)))111999BBBJJJRRRZZZccckkksss{{{,$$=HP` 0X :H @ *pH6` X Ē/8@ 4dB3(ЀS\:TPB3T`08@jժWfP%\4Zg5XtLxޱ n؉C^VH@n+譐u3XٳcRA0@{&|m 2$0A8 :$hAk*`(@ld$= xnF Z U,@F=7J*@_见u`rT]`ADmXi [xh\ 3YzCdabTaЖG>p;V`T@{tJ 5G:V)0ژI{LaJ@py `9 hA|X[fq ,pyT]Bj@\ZwPJ]uP@YTA*|/)A@$e DP%A ٲx )I PUط yA[ X `DE R YaG AW;Miޫ0ZkYN)o}TQ̄1N:y)lTSQ @PAPU]JA0NPG%@<-oSuG>m3iEt@;itksnap/ProgramData/HTMLHelp/Artwork/ttIconTip.gif0000644000076500000240000000257310534177570021377 0ustar paulystaffGIF89a$$!)1BJRZ!Zcks{!!!!!!!!!!!)))))))))))))1111111119999999999BBBBBJJJJJRRRRRZZZccckkkkksssss{{{{{,$$HP`&CjȈCF"NH+  18HRJ6(C% &P"  l|$t0ACVP*H41ղh@׊=RJlݻCI0h`sA%hR tϖU2ݫfG Mk'%w@ &3`@%s_u`NbK%0.@A)QV0(aB{uB(CD_Mn`P }W0S2L'mB^C :8`Zp  V@nxV̐5w l %8kEA@$2A[N8q8Fa QD!-Y@dǘNp@RA!W p&@nU}ApY.R7O Z!lg:=h&^f+IQOJeb,RH]♰^jIMV Ptpz%E pĒ FĐSEq@pA >4A-DGqH;itksnap/ProgramData/HTMLHelp/Artwork/ttIntroScreenLayout.gif0000644000076500000240000010120310534177570023451 0ustar paulystaffGIF89a$!!!%%!!!!!!!!!1111`J.{ !!,-1-1-1119999!99)591191999))991B))A1+k11))??Bj BB9::JN9=h9<$ +91939J!FFB3HLDLOWT&[UJLORJJ1GZ1T]KR`KRwZZVZZki[_^bhnEon\JiknidJg{[iuomomljyixx{MR_w{{{#WQhJ0qJZd>wN^cfx3}Bgkivvwesw|txsypxt* vum{ͣŽûŭ޵ֵ絵ŵܷۧſӪȩߣɹΔ֠־ֱֱֽֽֽֽΩ޵ڷֳ,$1QcM7LpCF|("ŋ3ZԈqǎ 982$ɓ&S\MK7lbL2jrɳϟ@ JѣH"ũӜOB*jԫTZʵ 4^! X2h&p)3n]xw߾ ;pÆ#^1ǎ#U) TgPkF}DiРK9fy:d튱{Svtqӝ{2=xd!Ƌ[sϣC՟7gܬOd: ?To߿s>Ԍ \Wi&MNrJ|5a4S}s5܅pa|F#([p+\"as^4gcm8֘:8J'$urƑk#N9bA?%iϖ3W(d4?aAF+tч);Gd(R>g B+(aÇ(! "N  (b(\gF+G/Qoqݵܑnm F;$?諏8P~>Hp(Z̸(Mi*R# z08N?Ap6#DaErЅ qK}tݣ<#4E"\#)?2\$&GjdAd'SiI@+eYڲ H4α z5yH2)dnˌf4LxH]#pnv g8م+`QBNs B:N+3 \B>y|ZQ@)Գ=?ۉ/|ӛ'9-z>fqhDC*QԣX- B~'spNw3x{6-\Hڠq _BC6h/ka#^&%eH~ iD{ߐ4!o,\H$ZrFXwJ7y[5 :!90 jA:3uLx0tS~XF'j nz8lEݰ <5=@2gLLXL $@ULŔw" )XF@h'~j?WZ vi@~0n`ago 7  wGTzp E MP  p o`x^U+8PxW&bVug3]`o{ qeEXob'vxYX5\Q bGvUxegX ؀_La }}&  4Va }uNWsbP`2xV@2Z` bvxPZGuNpwW ɰ;ʤTX ?p } o2ex^Ō%x }6c'ae=7WA`_3(tEa@b@X{`Xg\Q0 Y؊^`M|p p 0`Uy^ʨseo^`ej,_& }eP`0X_3xi0`v(ZL8\W Hcǔd  8I{P P ` ` IY Ґ[vowFy׎( Z `Wwp oŜi &`)\w7ٞ唝]` `i ɟ v U9j mEMG~9(YpZ}ɒh{0{0  yhS'tyC \Qyՙi ̀ ? @ Ƞ D BE:BJȐ JڤL` ކ_)XX%Wڍ&*)`)^ڔV ``'zuzFh.j5Uz8― * ʨZz @0 ʨW擄E6x 3ؓ`֜VGs`PYZ ~p`#n1N`c)ڙR j F ڭ @ p @V [ ZuXUgɍw^W{heՍaۨ~}FFP6z=` 0  P "{#{ )À $;/k PV&&b䉘6`_קf Zi yKYВ 6`pZ\3:QȞ:ʣ:P z J 0 Ԑ ykưz` ̠ 0`` j @+5֯xugpJXrZ3&3=obeZ1[ڶ+Z 0( ` + UJp U `U`~`p@+\Xrū~fhz}p}ʪiUժۘ 0a`&aG{2Z秼 I@ ~PR`\ǫ~P ~@0` P7X=G`WgMZ܉@b'Ft[` JXI}N0PM{M ixaxszsvf9YEnf|WV{a^W]@av0xWաFȉ#VFVz5N  Ɍ<#F` t ~0) "  ) k0 @A+ûƜΩ` F,ɴ6̍ `_W`huјea=ܿe`@\X>PxXJSx"##p"@)pj\A>: CP`p U)PK ` tt !`01< ex` ̙< `cj{%y`h$ V盒Df_kQAsm\ZXYnhK uaZ\fZhL XsxujVE-}lɍ(q[ [Ơī0[ 0  =]˱yh`s&WgF ].` @N|6(l ge\~ne_Pg ΉZP儀u0eMPG͹˴f:xn-nMX\ \4W;#We^WpB  ƀ ^阰 p= @ 'ngn>XUgیAb&֎;j|}< @sY zЄ4[XهX ~p#qp탐y 00ܪ{9ew0ׇǦaFs}؁L%` 〳# ᐲ@5+`*5KX*`@x3Vhx >ͼh͚8 b}6h@S`S`^M^*i\@!`ھn>QLw}.Lǥm+5MM4#`Y ,>ɓ ? E oɀ@ F D:>\_Uy@x~-~ X`T ;uоiUǡ(ݽEJdž G@톰rn*F үү @@ppP[YY'njmoWŌu}5-X; ߨA%_%fȐIaCj2[̢.%"c F 9"3U+Ld% 2ȦMI4i4*&SF %:tU3gvJTLdNfVm]e릝6=y )ҭ(:N>F+dǐ;Ej$,P]rQ^HF>U.~QHg2Ŏ!2 幓';cbVLqH 0cʼnkt;E6E4:unT؝{w$2+ߗof#F1VP^U);QG6Jbl2,4Lsid;Q$ 5f*/Šج")D͉) 7ܚ0!nKB8"fQBN AQ}\ J3 uV$j4*o3'*',B{pB_aY TXY i+;>N* 1PE 2*!":=1po̚[!P"(b(bv֪1d)ZNCP!ٌTD 1Pd43(! ! Nbq9!.Oކ O]YWjxk+`1D`1ZtcZtO D)vΘpjz@:? X Dwp>fjD)(JVš :1Y#s4PapOY[XLu$1QFE * M 3=0+hcLsl] N YPĕ^dҗ.@P^,O.B0`ç(mU"cYXLf;śXJתX :"Wj#oEAY$  J]~%?<& @sEf- l5yӍG@ HV p &&^7@%r dpO0$ B 5ŷ%It=;hocgEgQ$r3~ք&nH r<'Ru+i2:hgw"q@Q VQ(P+(+ދP`AP A`*#7P<%Pܔ EBE0Ù$ IbN$#!^Q?,MA >u-e0&`X⚼cܼqv4͋ݺb: : 0okMK 0CWT @H/&7fNk@0 4j,?5O1 @\xcX0B@gB9'5<pнMh[ JE,dsP@(p;M`nc) 8@U>s` -4)32 CrS<2,C3<ÖžKى؉&Ty5 F|i!y' ~X`U @k*}!Uȃܘ 2-̂c3C@zt PB/#.<%N0elFgdFh|fhFiiXC/i̛. "P`*o@82A#U7: $6&'p=SUбMФq*A%9зc3cs`7&,ŽX^;-njɜIIIiIt;[ bЊd1o DVi1@Eiz \&لf.= mRpcK2jjK(3>wu!( ;ۀ!@s5y@p0 ^T4aYɓ (Cm`LmHmXDMtM|Mdٔćd&#Q &j.BD 1ӓJB /Cp:iH 8X.Y*m0S@ J ~`7Rs@U̷m* P pB0y j=BXBhLMx}n@TxHT}DoXTA݆k MQ )Qu fXQ].E.R}"YM@0;D| 525uw(C0YHmh.(4 ".IOF8!& ܙs@nPItB x B9P PnwІ|p_0_(FpvpgȇWPMIE0"ʀMD\fQVJW /1'{ӹ_ BWȂ^u܉ظ'TS ;s Lk͂k>P@xsX:MB;p# - r/3Mɗ@MMMM}胼P>؄P_>?[0y X )OmjbQNa90#. Օ >^ȃ y)^})MuPmP9KA c!(i -࠾{?qAP7shmI׽جh 6&o8y)0 ֔>>D>_D \FDhMM521jX$!#CCP9nU%1 Ƀ0.ȇՄKF@``fO |c0#6Iɥ̔zD0`{;33>!j,)`?,;P0Z քFPxhc7vcyxSMldPXoffkhnxcCmT."x'ZJXI> "rg Fm"j"rOӘYIF|0JB((<98h<wFŃ& DmSClf%PqK'YD #%7;®&AH%_ƃj!#P8PDh=H N"יr@QfgS8gXiNi^~in^FE08ДF雞UHri>^8jF CLWn)K9.(~P";~e4M臞`EC8~۞h纫V#0__Vp^#lj!$@1yg۝m~mdxXF.n慓nvE:icMx8Fg%%hn&z>>jMHE`EX&hJ`k|.l”6TԄ0+2{ > }c'^⡁?²ݍtO4+KAC.xK`y 8Y^XXQqQq"q%g+ruPR.C2⚐ 9L0ݗ =d!YX;Pz؋ Є~XTMM R ?{m}=XI/p;](S X' 6!*/ !hЅZh_ju[u\u]ruvau\?\P!&ʸ5f9M!Fݸ~?#s?DWzAFh`| /4FX>FUE 5pGtOߦ2KO tNt9y: )̛;R@}fub'vG8{u`y_Gh_py^/aWdzX qBox*7xD8XrrFXtuSyƢrL 6ˢpꌐ- ;X&^*s⼊6dQY&ښE'Dd; 5Ʉz)yx"y&0L,7n;CN##I K&&55U$7N؁`S8XO{Lۆ*&(!02iJON[$H2FiGB`Q2}e*XՃ2ŏIQbd`fymZ"q<8d`̅7ZЃ&,h9@} X #80N"%3FY 0#4eNJ"T1Z$៰ h;^!!8Fs.F`?+Lp~٬ &F+ID)2[ e$4A)DLQdSCrn*57Al%4)JT~$T)-ȹhLPXdbzAVd?DKf)8c:A+J&P  dM# V i0Ӥz|[ED&p YLh,`T* xcgK@3-@A3 ,DAҸ#3RjHcD& Q԰FoSISBLb-}WE(EHb#ȡ:Z]KE-UNJUEi6ѕ meA!XI Uaj H %< 'ХSXѢeVu ( jE'^uҕ5VxĬ&GE/P"`%g^)#˄FJp{ ms\%k(P)R  \xGqbiR _ˆ e3H h^皇Gj^(FYP/Vp&Z2brQKSVVLamP.VAn@5 N.DF W[ &MkB!`*ONXI\/}C 1Mq&xt,i_z'^qU6~ 0(`/@^0gevؕJqL$TQ:"k_VŇDZCЀ[dj!ࠑJ=SP̐2!ޝUӕ SPRӹIMP\\M$np0FE X7WhXY\d Ɖh&\@HH@H F $X# PנL L"0JĉmP<5AᡏTЀp@^ȈMDTHAIUB u* W.[ʠ 6x/ @BP3dLM@ FnTË́3h L NP\ڇ>(dA d"Q%! eI@ V pEΙ*Fd!2V]DdI P&hȅ߲TԔ]ٟ ]N%Ϡ،6L>͆9AWUj5P H! |LXaT%2}gB#d0 R hi$HdM4dOѤH&F5ϒ05t!\i4pMݙPTVYĴ;C`ٙ !%Ui**L#TNli(`pV`HfܬOR|~IGvd8@F4E U4jW&& a$^XAPԍbͼAL I5,!,KqYq-IL XOXX(hFX,J0ub nN)CW>0LϐN\(,vdGHj !#HUDJi2$ :d1AQTETog!CLP~YD_@haνB#VڝM-'N>g0A35d%iBFop B\bpn<\XN>4Wh"?ħ |E[R]&b)Tf@9 i0]j.?HZ4Eep`vf)0PpdAmȓ&h)Mͳ\Q#&9p2[:&'Mc& QFpH5 !Ld|K[&Y'L0-ڦ!PW0;\ tȩb b( KoÃjƍ$X5 SǓET#5Bͽ?:TIm!9e]%r(,j88=|yۈhylڳҍ̐ L_)}JF&k2m.@!Q؁"82V"FZ^>6) PP _0`R GQ4C4o$ȨkH^])[4cp!38Y0z^QtkԈJOt dyAg]-m`pȤ ю|E%l$G.NH(BPdDj*{ģ &(BE-PF&̊?T$$>֙aT^,e,j,9VΪ83JOPܕl8i#,k (ᴲz 0F0B t" #Ȯ*f(&ޮڜEky̖Jđ$^d:A3.!(@AVE[?4E5 aS_oª&D) :!0KƊd1eLp3c()NXmK mJ0&=ESQt(8K)nU35U ی9phAӦfeVP Vd78l3H9+9&& IޤWDΈhuQdK%AP !ΌAJb.K  N 2g-k4P;$ ]Hhyִ`RgE],B5V4%PKkBtQ5xrdFjk.Ӷ"DFAH3?Șe3Z΍#8FSTI -TPp&(fB;H'Th`!PS3$ࣀ:3iS j񛷺wo`9s8:ɐ̄QbEK}sȘxR;'*|s/vCq誤M;ЦІPz\<#[PTLϩ[* 1Ano8Θ+Qf#?m+?:7*3vPg2 .!Dҕ?&8v,vmҤ)&IR#C3РQ#H<3da$H9YLd"ENJS/cN(GʴN:qOWkٶun܎L AQo`&L8|v4F&M4iSNRd%Yf1$9^L 2F΍P6zH攙0yP"Gfw۝}7w:[ Ȏ[0'vG7wSi,wDFODJ*O6Rj'#cߦ",xê,b Cpˍ$E` A4s²A$U\-:YhGZӑGwidދ+Ep{$ bɐdq:j%|نmIGtiQE%}RD]RG'Q|H.;)ژ̭JϤ?*&h ,H3ul3761C0.rJ^BkD[huECMtC[p#6r5ԸjjM&ipWFD>ػF@i>}Y>0'R$ wKL`)& N+؁S9L Q, &V /}#}L(>mg   &)U fT3D@ J$jV嫦HkFǖE9ԐG{-D$"~CBb(x-0HCeҐ.a2[B؆4am#4va Bq6T FP9AMh \FiN3"&Nxb7Y3AL&\(`8$F76J%Z*b -*Q F4D)ZQ^Ԣ&>26C!HEj~ (Җv(ZzRB(gNgҕ”A}iOzRz%%RE*gpZU^ժYVU~2Q(Kh~y_ڗ>íԀ6JW5hk] ` ;y#݇`|,6xd' "ͬ`%[vhI+"UmjY E-d=kXְ-a[[v-nY ⮖:Y Zϝ)raJu[bݮ)j-@&һB@*^wG*^ޗơ[_W\V:L ?~- [D;YH†PETa_+Kzbxꕱ{Kr]XQle|ANp|'!IB( P< хB %&*ect_30 .PsQei1A y qFI F[„1aq h5cDT> ORzEԅdy@B^t jTDgF y7su`($Ç#" 'Cv)Tp؃ S&d  ,X+Hȕ-g#%'|f8&21 FL w30$HL$\jF$^Wxȃ#TAPO,(Q ET" FAgܡHfü0! 耽!Ն\a͚Ά.o o @` !$M & Bn&O;Af` ":/ A 0 la l`*a ",iR$ԫ̸La!மڌ QnGPoHhA! 0a1a((AeQ&N!n  ̊!QHAOPqaQ߲QݎR a \0! !rn! a[`l*8*LaAaV !naX`A / %ߊ%c%_%o&,n&m%&a- ¡a!ān.odf$-:,2*)Rz'l|L&y%֊']T\*K -)C-"e>i,aeb*! 03733@4 6 T5I3 *"@m%6o7wS6w37n!8Ռ384 oSe2:::'a,S6r9فQe:'|G9Qs7sSa73-E6a:s@6h+8L.<|A5'c D/\"UaސaZ e:w~Mꜧx&(C::'y3! _h#zz;1ٞZy7`6xu!e,!jqAč{;6oMT /Iی:C{Yo%zW&9$;-ڝBzajXZʿcJ< \v9I`2!9ۧKwy%;++k Z)zEH|+-99ژG!!Z=;;I,0x<:3\gYr/;Z$K{8mCI498ܲu: ]v{-…cB:zX͗OA 0  gQ9Bܯ~ ,SS۴wٓcAA5ܖ;sY䌻-{9c!ژWBd&AQٱ5A[)"$qvoV=Y3a\P`E$l*lہƣIE^j#-a8>Qigo[#MA@5#|h `<29=»m% D`^^ YJB&`db|CIxEB}zC ټa\$x!׫{'k/[aaky!!wVɝWUٓ3[y DG`R@T&yMwXM tM55 v偺U| DGKO_Y =%7|]MR @StG~$zWp#WF ^ҝӇZa|! _ן\ڧ]ےb(S 1" LQ*b3Mc"^`̘ǎFFIQ)P1QM6ڤ L9uySӨfuu;L[@TZ5'鐙?&ʚ=6ڵlۖ6ܹk ʧȉ)M?7H7JR# Gt9;2W5[Q+Y䑤h,E$J0,Lh /=G܋m&EK)SƯ;J5f_Î=;[{?ELrkd@Z&fq A k /fL$b$sDFXH3\60qK 1S3ŗyن&%ċoG%TSO1 5Hc6ވc! -֑]t)НFe(2E3<`W`ˆ$0RBBKA0e#x81EwHڄ^^*cdHj'_zebzhSԊ%0L"hR̤n*ixK>_\wdi >ye҈|G|G` j"!  LI(J" I MTQ8܋")3Kͻ.kZZX@*\j^F#$&QF9c(i:ڔ"LDZJ(@~2bhtsaRnb¹F E}D+Ґ@rˤ⣿զ7^Xb&EZjTkE\l 44 2Zf #Ba|Nz#$F/ZIG&cgbi`0k4(_Q 5~ pYrP2^gID=St&B?*1@ &`)GMF҅)B5;Z]"bHĆm9iD! y`A V07'A"|[$@n 05<"LD' GQ,t9Js`0\ !` t`$ ?LC@WXъb5 ~G`r]}-LX3fDm",M$0D? FK&vw!F$t%url'(lzsHZ;A9tU QfG (]#}d+xVp07e~TR+ R"t!+pveA\JTwybseUlp ` ` iiͰh,^ ٶr;ub'¶ie7=Vu-!.8aBpMM s%Dskv~"sdBGep0Q@ \  `PT9U l_3 jAmn8 6yVALd\v`1ptVHjH$%*_~cCZH|#@ `٘`#*>^2*h*/w$JcMC rbC}cC+|&D@A+0V= V %B"yShyAdBJ)*v ).bb)L#*hɘb)p`!J89'8%""PS(Y0<2wd_2Th(W`b7}c.p}X:h ~@;p@paz3q|wA+`S18s3 +66gYhD ` TjUWYʥ[Ff}C c:M*Pk,}7FEdJ{ %FKt@,w*3xw`{ W_v.鐐*J:Lxprqʪɪ m:NO :!C)s#pjfbS05&&+a+TiQsRMcvv@w/Ɛ)+˲+ y Ъ2 :As\@ 1 G@LEK6QE7aMPKY0DOBXgp ¶J˶n{ g0pbQ !p'.i k嚸1;Y:3gZ$=S\C E[p l{ I{ ˴˴кv0 fY C kJ0Kx+PK۶{{0 kz Qx pQ`L( IP ޠܠ +K/:i;E 9s CvZ9'hT EA@JY*`FDu2b @fIY@p L ` ӰNaWY} ? }P P |@p yb :i*8~\SPP hbP&779G!\!7)YY+i•G iC{nS+ *꛲Ӡɾ` }š?p y[6;: P0 ^x 9'qe{!h1JT~+BJ 3"5-jk(k>ڊ0D | }}@ġ| 霏 ށL˲ mf̞@v-JZnj`%9 A_]yD ;YM|3JP : < P Ӷ CР ` Ұ9:- a˺\Ce X;|BX&%G# €ރ3GB&7 H1Z̅TZc@c؋؈}c- ` 4 thAfnk<=FtH$R zצ {A3- Bl/V@h-h}rgr I2m $_V)|2mSXv"(uE۩y `9I}Q ӹ00 N N .   m6`,dd ޳i2, g$#t=poI&zobM` ^7A O4N 4 D p`RD~4r]h-G.b,]/i)&6`kM~ݯ%N\:+|.\(0 c  E衃W ?#~e 05 ?jrn|m2n-.U\+}I6NP|A"?.LN?p qP@@P zeq` K@@RgG %{p t7گ#D3n9TYBկĬ ZF Zڹ^&,g|lI9) BRŴ> p~E ԱQV4FU NNU%:x4 P& T|FgP`ntl72-:-rnM)I^ M@R` @ a ?Q"0 @| |x#oWfV0yaFw h# ˃`ȹl9zb7`F!eC$? Ԑ q nq?rϕ0]ZfCVS{rP|2v0G| rXsPPCIN?$@U԰ `~Ypa Ucz`r,X0Lҕ?&,^ĘQF=ZD H%7"Н4ag!EIj$I;uV\eJӦ VhÈ&MmTmښU٪QxI:T^[6[TTXR 7۩=VSZߨA%_%f&-WEB8"H,8?aNsm"5N8C0-dT|v(ۣmVTȞ"/)bPEmԙrEX)^)*qjYXgaˢ tmдlҕ{]u/`Ƙ8 & 12Ɍ1GspE:`)Hphn#$h+1ŏVG,ԡ&h0;8M$Ë!D;2q1Ċ)ϻJ ŔQr, r//|8Aưb##0ǘblp,*УM?;Qq\QEI5 I/(7|^#iE#5IC4QJ<;0-/3Qһ*RΔL6YmV51R6'sq$=T qqМq!!T'QpAC$z SL+RN,-DHU)j:F6aDLXeQ$rIm뵻vW-mE/pFױ>f+gZE K?jk'd.Zh]RiRH-\>#%D6Wk-~%=үS:+|"gE7~6k=  ctqcu0w^{#lؔMEH@հ*<ԱƯ``~<.VZL! f}U V98 N`Bb !Ǫi"Z2#:)h   U E F/rbZI̒&^VFQE |S":VG" S [R?BRWCc(ҫecp)daǁ F,Я_ّ@iRC*  cT!C9BLTghEWF5~63pkIi&rlS7Q~`eAhC`(oz o,^QhcgҒ`s`H&b\U %EƸz]ZT1 *k>XF?5l&Id :q e 0WvȃrtD>g 4BQ0:jT3ԉ1*A! mog6l p?; oDS <b O`?40OPET5 F9r%mSg328#ǐ]1 2(WQV#'BO9 ;o'΃ ^Mo~R7P'2Ny! +L&˫ܘJT$ Pr8y/=(@ s z-w)^tuaԬvHT+󊧚0+d9LH$`(-Ái}SQOV49xIkdSI6~Z1!&axҶb"7zkؚ,b݈&khca2d1DiUGgqF`ƹQ"f/L^ ڟ&J8.woxd|=xOR֣,L A_Ks* CK=aOtdEUpeJNAd|P yG2 .#LPqڤ:g̲,4SpDZ^Incpn%UPfC7"&a`8{qb5c$ddHc<5IJ):jeYɂ` |P#q YY>H"MY>;US  $!4"D#⬁ )҇kHfȌȍeTlYcY[ LdFHH5 ˂/HHc36B {,#^q8&ڳ @#L;==>c*hLjJkȬ&IBoܔC?yJ|MpG&9hf8:,-A/'+PPI"IpT<F̂09Bk|DdLHF5DL FBQ(%` t$0%IN69q>CUme|DdHήIa ',,&Ьhd؊1Q/h#Y̎L[&FYb, 0AtF̌Ѝ QHDa ,rI:p,;!2zL G(6q0")e> KẹhbۯNҪ̭RR-O h X&`0@,9dmA=Q|$$RTR%i4&NUF -N IFKJ0` pUhͺ,x,BIPhmu|&.uwWx 8(ۄ<$/` 1&RɂH'PPՌ-P%: 0X*h@#PX#XOi4c{/x?%'x؊mQxMX` ϖY't=Y iNLU0mѮ-Wz%4TTU}.Ї,T{ЯTQW1W5I[~K,LM0eڧ-U3K3CT.wSܼ1m)FF :"@\~؇}+x\ΰȼWd.m; # B(c:ݢ[z}]P\F#3%: mUj ZdۣץP[Ϝe5EUeuuEH Kxx^T^yMFgHH'̇|Zѥ`ҵ `҅ a6a`Aya,`Nkѥt}ULi„())b,*/HvTHU0c4Fc3g4>c8Fk\85;Vc3>0Lx/%^ݫMb%DTISJNFH,MdNNPyHTQezYeU&TL`eUZfpnpBfFRbb;dD;fdf`FyTdWeifaV!JxPp8p^eelaxtwVg^Df]ޥ=ZR'ćUxLk`P`0H0XB6&HUxTp%X^Hp%WyN Wy1FxiR@|XbX&mxc~bbNjy`pT("NFoyhHH5q!Hdx(L8H|hx8a8gTD>lZH4`wkTF,*"q: $:0by~ c *8!)餆 }2c&, E8؃&·.DFIP(ic޲'fš@nX-E⶛LPI"E@h^>> B )h)M.=h0fyBB.' SJDe$zTHUgQu]T 8= 8 C0qL:.I HY*?:Ѓ=,p,?8:hBEЃkyhI L)LpG$꧙RrRpkrR)/R`6r HonHH,=,:`bB&ka}肶YYЅ(NFse]Lܼds-BxQuxQuHUVWuq3sIlxѢ_]meccpEkPx[y@k7nGWWrk_;IJI@mі`iwm]|b䭤gexW}&bCng-NHqHswxetxxmxxwGlr:fFydnH̡g^x_yOy~>}zz}``/zэѽz߇|{{}{{_{绿?{/BO__{{'W{{{}H*ڧ%}ܿ}}0XGWgw~$~0Gg?OcA<0aB B,pC #6qaE)n1Œ$;itksnap/ProgramData/HTMLHelp/Artwork/ttLoadImageBrowser.gif0000644000076500000240000004575110534177570023225 0ustar paulystaffGIF89a+*VXks.^RVRsRWbefcdpc\kugygXwbkssky{g_kSorpo{x{}{k{i_v`jqpxzi!!)))191B9BJ9?NH9=ZTRZ`^ G N Z Z9^!U>Tkkpqks sw%s'}!+W}h팇؇הϔӐΎ؜ΔޟӴΌҌ痴ծӣҡӾظĢֺӱüֽ޽νܽн,+ E.b(\ȰÇ#JHŋ3jȱǏ]Ƅ٥X50լ)7ܬMj)SPoJJuժXjhM_s96Mٳ^&EVٜ/ĕ{0S, \"E K0À S&`0Vl0ca+ Yr`ˏ'cΓ3_<tӢSsFZư'rڴg#mw޸w8p݋aWY>)ϝHG"}HY )U6sz `X ~\JZ~Vh`~!9'KJ aċXp|v>!tIdLF7@:>JeSZ]X^ɣ_v$dJaehRIlJmdi)'ht*Ȣ/q\`D,Ḝs裎F 餒VJ饕:yN霞6)Bj*j_&xtnj'Z+'J~*!hfhQ%BJ-2imhV[f뭺Jm_;M,LEk(Uyt=kj\lZ‹F^Q$c\qns/K<3ӌϖ BW*ݓ:31pE1J ܆Z2Sh! ۂ:E,.ˣ|v]co Svf~L[o6~GM;xmСE/~ԱoQ]q#LÜGamCGSa0l^1_(0yģǰh43poE9% MHP!4<`h`>t@8p>bAnP/vB !d(.x Æ= P5L H@4etO"4 !d1~ $ )H5R9OP&8ґd,0(Y +L`:ݵ mh>PnQb|tm i`1 ]RK#~}h:ld h iEXBrS:huqTg:yYx:Fw:PMh@ZP"ԡ mD:NE& \!Pre̠;n lE! ݂i \ba@+ xw=oxk^Kʗ/~{׿ 0K`xkBFV2e0C02 e akZ0:7`4L [8 kCZ6 5-Q @L"EZI6ՙX%_˥n {wr\nY t\6ؿx&p xs~}B|NE<ʰyIN{ZG NԨSiM2җ|cMkXշvsk^ֳ'#-lyF6=}&ˆvmjOT"-jQF22LXrcN׭vvMyNor=x#nn%`aő#X Z)V1򑧢 )NS,Y|79ssӜ;zYsFѓ#]2?:ѡr\Xy֯s7zؽcyHB@./?9itwS]w{Us}Gu|~C^5C>߼ "] uWr?Y>>c_y/-?z_WK_g{shG ({gx.W{ xWy{wswX17W!h}.}7xh}}7h|񷀺8}:s ȁ}zGxH{4W{Nz4rGH4X g~gz W}VxwU-'p$p&no؆st(x؆yh|X~nw8kh؇{> x؉2  hP<l7v~=H8 X؋HǸ،Έ8h ۈP5 xY{{툎ꨎ踎耩Pv '4''Hh}t9Yyّ tGw"Y= 4GpDH~c8 9珝s+s9)ttGsȐeȂ?LٔN (x rw7tG>'-z?ysz3s@!~, w7; 3P : P!, w:> =/Y2p K<@iP  5pw*y @<}- ;1 zׂWwfr+ .Pp `0m| 1)! & ` @P?9 ʀ3 2@ )5?};& p!~60|` r*)r 4 ;0?`r !#  :@-#P%4`%y @ ?lڦ?P5A$$0,P5 왟45< * +Rx— r$W;; @iBWx y- 0P(#0 r r+ ڠ2 1pp++p0 6P=*|𣈀1% 4p  1pP}) =0ܪ@x0*170-i~Y)4pr&9+כ#p5 >?~6@?@$7@=Y_ԇ,WrtfIs kwo ȁ|w  4p5 =@Z ,@9 Pz$+Pz; ! P-` pɚ- >Z9p0}-3   %P; `rys>3 , 'V9 ?06܊'OK =` `>@soZq> ; >`=@ O{,00g<"ڣ 2 `( j{v Y+ 7 p*P> ϋp ;C * 6OL+;F9rVhwr0д! 2<0~| '؅Ccr>PorA ]p[{9pyۭ!p#?| =rӋ* ;?Z,P) :r#(p{ :[|`*pqp: 3 k\5P( ~ {'#@ "*˲2v(X=I#`7 Ȍ| -"p!0` \yyx asrYs!'x7  `]۩Џ' @00p0P u P{0 0 @ c pp P P |ǰPPؓp 0 m{0Plt3˛\s݋ Џ '`? cɩ ,=PPIrYfw9m 50P 3- $]y狕u~ ,S'w lgvry 5nluri` GL QR.HY 8}6X9yJOr/xս}DާJ`(Hhz>|^}~灾~^n^5ЦlڿWr Pn陾~.ʞ>''Dž| \r8[nhNŎĞΊή..n?>^~.5W]=窶N\y)7TxOzI )$ `o !/&?(?r./*Vǂnj.rGNח[`v6'8HG^XK~mnQ?t_nwBނR_F}'Na]ba9j[{vΧZ+=w5O{cYd-> n~W{}!8x;_/.N݀){xg Χ; Uҝ"T#ts(E F: U)SQMRW^*,հ_v ZZM6k[gٲKVX~:*^]no\l-XœB\d7c4)I2Qi^aؑAh<$zfPDXT@ఁOp(ņ:Wxi\4"4'd0ٶrׇ|PA|pz7|EDJP`Wr2)IM6l;Ap|#'OzCV c@Qx*'btaD9E}euBfh 7`dTrn)?=sQNHOdNvwljjB}vw%sϽ:k~xUlgBQ~Qw)ǻ),z箝B {7 M>iavsҥƗ,@`BЂfHg>s1ihO4=1NƂڂaEQ?>-F+ԎD0-e?l99 20X<][6)NYP !P'?dJC[raO, '䨂d,iɰAPQakW*<-fP7zn>A< B!]Ѱ Tc|R0 S A, C LyE !@dXIlʎ{dH>dM,Dn G|`( D a @=, Q c,E)ԅ~"8:lAC DqlfpY HGK.Q@A~P `(qbAኖ@o$>;rBpC*j*dP=C hWnvo:8H.;!^`=G9OCoc:(WU{<Uu`÷rB ^`Y^Vf=Y~V%hM[ZԞVej]ZؾVemwEE|܁@ pP\&Wens\ ̝q[]^W.wKJ7nyk^񢗼eozۻ^be9BaSxqA a8#VWlǘ31,6~uc X8ޱ2`L~OyS\^j Ѐ&2`  8` ``  7d[SXX)4 9-4M,CZщft#,FCыtI,iL_Zӏ43a7fdB.>?d`؀80.H /> 8$0Peo[fmiFv in/:6s1}nt{, RD\9y\ ot!t7?L=D?0@dDBlGLD JTDJD7CTD*EHDNLVDVUCOEFJ>ʿyprЁ[H~ awiFFIN@n@q9>X=HD؃{*'ky8DfpD`Pgu ^Fĕ dž޺CA@MQ@PxlHz(؄TTHIP‚;Є`X~=n Gx >o&aPZ$ږĄL7QRHZ/2rKO@dERAAxxvV(?hAhUIql?x@TL\^|oJOCBΜyG(OxXМj0u{PQN<n| ᑉʹm|}0(O =, C@;Axtސr` PzPaLUO Q,b>)$)Q-ٹ!8,r8=08h=ЇQp0R/Ⱥ/HJN GPMx>هQ v0bRbs(`},r(wPx}Rx切c(cPI}y؇pG L@@ I#f@P̹(=``@R` J_f397g $jUa@B}ȗmXFK>˄dn@耞uMdx̗o@LjOP- IXB+ FTZIsm`PK!ʄy*Oߌ|zi=Eh膃x(aâ{2~ !xj=~8P31 Ta\ %Xn)YH^eH NQQ}xQ sx8fxxxrpІL}z8\z[yrIķZ JH,1N]}‡=\5Lr"u]͕F2 n]]n/%U ibЇx(xI=v 0^Y @@@8X6P>xͥU稗jު?dڕP@HG@xDCx0@"k@`O.$X>NP5N -z y=؁HfPEE{XѼ|I?0hI>!)N{ʗpHd[Xɼ@Pz| ihKx?&uz$-JS1JҔ(3vh ŊQ10P0z%c0r(v(RoBx}@'H?P]|xfdt rg8c~ L-N}CGVJ0JhgwʡG P ށ^hЕ]h?^&&_hgХп;DSFЖTlWϏ^NtX7xS1Hhdd@@ȁD9P!GHwMB*& )v& X?8?88L`xx!m9nSh8!W(IHSOlkσe!ɖ,c0nKh=/L$xi%79R~@lYQTLILr==7,m'Hu@PΨ/N-i1BS{tJp|Чryo9Їh)~AHvfA u fZdXkNNҸf: Ue+xpc>puU5qƂq.N֘] ,ruabA RxBH 9qJqGn@?e==؅`/̡Ax@<>ȅ8!3iL]05AWh@98@цUAjzR#9=8Fz #e[!UHUudAQ0 da_hصlA0p!wzmlSgEmk6WAh#X@XFKh"8x!zzE= =8_vwixV~SP)1`Q`9EgjKP*Q:yep J`;h8;xBG8@۔;V%]dq&8E@6ѱr羁hk"򺯤R8W|Ϸ?R$(Og>%q˯7-h1韵,Raώj}J+nҤnd&dSQI}8~IX GpH%1!ȩ} ⨎Z2qqXPl$?_qJs dj)8u(`*R} „ 2l!Ĉ'Rh"ƌ8Т ɝRTgE8Cưbǒ-k,Z>ȨIaܓh±l2fXfcόdVгRZ{+``5`В.m4̉#1Sldܽ*<v`Ĉb ?^wD`\:.o<?eH]Ltyr&CzˎDC+3DOz : %[ cJqW|$B8"%nZHnc\tQKg"5x#BVlF_^#EyzH;Um)0h>55s)LP%\y$e:8a-"nrLU3 B? ? "1T<  ӹ3ʝD"< >c&{lq.nW\u 4P ؐ + B xb.M }@ !pBF'2ЂZ{mFQHaĒK2T~}O2pHQ*ԠRLQcT#4'H - KV5ד@bW+33! 9$G#9?.5Ptn!- ҁA |Aښ% eŷ} 6 B BAo5.C&" 280jgOfL3И} RC }tA_66 'DLLk.Q ya^(~:Fh1E䌨>CmѨ%'zR29dtD1bCL;)~>[ħ*O9<漓;s)4 >PA9Nt4y Kڣ&6JՇu4'LR9/pD JЇAM 4`DO0`(FKHHD=SKF!(N> j2X5Q^2TbA%C 3 -2@\CHQP"8V=E(]\^71kIA :V{zє]@`'%W`b zH>"K{:;pxY`060 ,0 aa0`5Hb,Y@r? 0Ø/q. )'<$an[pcǠ8!IAaIT&aObǣKz0<s8?r 'J:n$ K@H"Ud6MKdZ>yekb 9(M/1u*VmԹsb?BROydY)H:P p:Oʐ V-J%jЀ>( B0 ILa7?>V6A0iɺR-FH lGt2%Ȑ1<pB -+G$VfuBA 3T/W4|vu:P xP6%2X ٲ[m60Aެ"5qo_Ի ;X.Q!@ V`^Ź3`s}*AT2_B b<(N GEA)7\у-;>(яppG(AA:-) r@!}3JTFXCGU0N$s;;DES,I":RhƊ+F!Q?A9x {h'rPO0 &e[KA˱e" ZPl?p@CLM$NX0׹p WA2 j?,f0lgu pӀT]u Ap@`_,r` )8jꁓec h!y`|jAqIyЊtq{@+ l Ħz^XzC`Z!#p `X b_;MvbEw7#?qԀ&" F! Zbajr\}Q  !ơ  `aaZ'0'0"&'a ULD Q=P$ $Zb$%J&f"&Vb(v"'~(z&(")"**,b,",,-""!\pQ1 DVƅ lJcRN#bc4fc5r5v6#!]'$AH>I@ ,&pB"H)$l')tB&*tB" B*#'u!fBna}C6$D!CFERCbD*ErF 6Ĥ '. '|B  JB  d 4QZQzcR6R"%T>Ta'@!;;£(K@ $"$"4" ߄@ KB$)_"`e`%aa`&.`6fa"c*/ac0cBJ&@܀pB $@ lY&, @ 4 CR:%S#ofSo gN&dfJƄ'&'#*@'",~]@*%eFFaGGgE'|z||NćU 'ZӈD'x*  #Ѐx \t џfTp~nFeohnާ `9z*Y]^y^l*@̎vePH&&d>bFfJ&ci.*i:ib.I`%Jɏ6/`(h舚5 g>R! :B}'fd}j "g2$⧓DᚢRꙒ)KE Baq~bJ璮j2)>ꔲ**:~n#Z`+) a.p^Tްd±Fk%+NNb!ЂJ2k+k붆++뺺+n^@eE*:Š-:=HBV,vVlĖ"Ȓ"Ělǚlrl˞,,,,,vBjh2D^ ~CJn}tB8C! $+vRe@JRږm-۾*ܒBt~_c:aB ^BB'ni@*S8ibZj.Tfӆ B ;\"v caBj%G٦m.F{l 'Ɵ>n!/!&#,=>o.B"4,L/=Bf/#R/". @":.".%,d/#$",bn/For"D"֯Z"Fo0'p;#0?GpO0_3po/b M ީ`Y& ǰ 0 װ 000 ׀,ᅧ`RR&#$@@OWq[S1c$1sq111ϱ1߱Kq肸Xf j c"RbE0> 2SB?82$N%tD~`#3T@2hr's&g()2*r)*)2+r+ϲ,rȉ$Xs(@j 9Y^יd%l%(pqF`(N9XX[OB\I' "dB$! IL= Ѐ B|Dx :@Z=DQY&> @ d:@=3D)BB (ß%1" zBHIBh>(@c|1\ $*܀S;BmBx«8T\$@ @ @x[ @\@?B̀( J-0G+Hā'@Ҡ0 4 5 `?  q HB@:_TNv`M. TvY@ Ԅ 6N#*GJ @3QA 4 , "@]"w&D$ąA >X1Bq12B , C * A A;$ +2` .1(Ԝ>OA"pF&<#@3 (0iL $( ;>8xx 4`B< >| hC |K:D+ 5&lB*DT t B@v\ '& @BA\(8C CCj@2q5dX44 @AjV,CA 8CW/(d: EC0Biy M$   $i@N9?g7 S~\j_iAb[1#@u\w ? 1<` ic[ 7A LC&< g;9)1C:‘?B h ùgGB BpNO ckLWhu59ȈNqbӽ,BW3Aă uđu '2<Ԕ Ђx=R{rd:d:' ِJ.Ѐ1A+\M 8ҘɃ^I? v3tlm E.<@ @ 0=4su)ngL*쨍4~ \& 6t޾֛Lx*l >fxRCz1l"Ɠ$^COB]=zo>.g5ziRHqKu:طk={x㿗nyًG{oO|g?Un"LCXSjr)G ͡B չ 50APM,U$q]\Ro+f2ϥN%*L\]E/b#:ˁ 99WuT-quOYDp%V\[%=G#d9AF~!U:AB$ɝDj芓@~$GF~ dj:ՄJZh^ǹpi KAY0Xy/p ! x&A,&(EZ qte!@F@9Pͱ4yV9hq*!,ށz"䀷)P!PaB JB"5Lq-$X $ cdA qKDsX5ZrSeDRj['J8?` P0D QI#щSh&*jTAt@PYE'A,  Pp,BHnMXʲ@eɁ%ӺɩY*IF^8c!A  Cǂ>T,+KuD @ x @(Ӝ:9Ѕs>yύtQ:SrW2zMDo:aN7uPv#DI0B^'usϻ w#Aw߽Woz{~y~)xWrOx3^}?'N_| ȯ|ç_7?O~o[?_o﫟o׿}⣿w_3_/ ;itksnap/ProgramData/HTMLHelp/Artwork/ttLoadImageMenuBar.gif0000644000076500000240000002042710534177570023124 0ustar paulystaffGIF89a`!!!)))1)911)991999B!9B1BJ=FRJJRRBZRBJRJZZB!ZB!RJ1ZB9gZ=JPAiRRNPZbZJRZRNZZJZZRZRZZRcZRkZJss==uJHZZZm]R`RhZc^mP`pRo~WJ]WNcZj{R{RsZwZuZkcckJckZskNZckZkkccgjqr{h`ymspUjm{c{oq{ !3DB9JJJ ' $ & AEI6M!M6gA584GHCoKZZZZRRZV k kf wZ `oy:b^g3mV!s'Ksrzq}zȊʀ|ͷͷ´ƭνάΜ˭šͥŮˬԹνֵֵһеֵнѿֽ޵֟霵륵륽眽諒ﲺ,`w#&4頡ÆJHqŊ/jqǎ ? Qܜ0nּSǎ˗0cʜI͛8sɳϛ c5jR*UƍO: ԨWRukVZz+lWaϪMۯpתIҤТEՠ_3,0Æ#^1ǎ#C,/%M˙0a> Z4ѦS^ת[ˆ:6ٶsލmҡlƇ.|j͡?Oձ_wZzwߵޜH439(-[tOsgG'^%h`ZK8q=̓sh~0Ӈ~aNf0ahuq 108O4:bw8 w0)x 0ctaFmM?lIf MI$"aE!3r;pFt) WT1FU֚^̭jp̘sTdf's>A<]8pj<3Gw# fPePQnfaJkhsX" 2g13s\vDvcHi,3 FGeQO0ر0HH5ܱ2~Lb2IƘrZL2,ǘv +*sQ 3 0.Yя?&rq$G=pK_@24V2%dqnqɷ aL6qIWͦpXaq\̓jL~(oq+wDĆᶍs^arqzAtG$M2w-$tQv҅6 '3^c2wæD|NlQ1G]ANx:`8Tarfp'¡ͻ`h@z؃45k|m˛gJh $p9 i 18_a@OL Y=A{''i{f< 8̩F$! 3"! 6pp.mi0?AD=,P l@dRK0h,n1pKF%@9C ?HBLIi#!\\A wh!rNBiДSd"'D8d^8젌u7лDaӛÜ ?L:I8A378UHJR4t + `8Tħ>Єp*]@:XAoFPvT px=Gz;DcP?anZEPrGAEZ4a{(C`P8ap.P50RQְ{աhZͪVk[ո5umC׵a^P^?4zh\:XZ ƨ68FZW +MY^2ZriGԮUmk6,JBdۮ IUXX $$t&Nɤ2R؅znxKZw[2"vZ&`.mz"_' NPO;`-`7v!`>xK!\_v8 ް^~pb0a8αw 22q,C2ud|$ %&A*[U6;v.]e/crl2m379p~e ;E R Cy Y]l]"AB ,XJQ˩3'Oԇy-$g0e# @z G 0=">Cq^?0kFjym'u\q}]wlr )p8 + {7zxzx^m:s>x>p@ x ̖iGxFH}PhQZnS F\Pu ?P:GpG @ 恢 @t8   fն w(yxqq@%i` 05e&kd yl6 Ζi0Exqؗn6o&l&shsXi4 Vji]}̘qnMHZ[plllxl (q،m @   ) ِX Ggmxd6Б2Б ! i%)%y#),,/`+` 1<ٓ>@n,` I LOOٔTi N)TYSYQ]ɔ@= ДPI ]^ٖnpr9^9=g`ؗ~\ < 9w nyYPi x|Y_`}Iyqs } RhFq 8 Y Xʼn8{[vz6@VG@y`\@@}`@ u$ _pZ 6< QRpqR ^\ϩ@0_#P9[}X ʘpT P _{ P@_hXꘗG Yvu#R C0uF SF\uT*^yU vyzpX%EQ S^F0]ЦyyG *#7rs WYWgyp*\\PS Y?j@@IS`uX@4$pY I\ꥺ= *DaJs0 @ϙ:o :Xʠ} Y ^} 8:re:] ZPP0rCH{UФD0useph 90zuE Z@i_~U*{G0 z`q0oq`uYPF X[T 4J0ˡ*J0IW 7K9CЯXz[z|  x:PYR} :q[ JrCPHq*W e \kT`DXph Y0 Zٵڗi˜Q }pƺn[ io[bZG ̙9ª m  ֽ˽{۽k뫾绾黽 i+[[ȿ[۾KkiЊP8pF~{kl|\Vj<µ,|7ܦA6G«824 (7<g=5i聞KkF Z0GpPL~= ?V5 pȍp kh f ooG t@ 1<]ftƻ9| m Ǿ`~x0 m8 *PE|Y@ V`ɪ Pij y 0j8 Lƅ pL˾ pfɤ ˹p{l@ L 0 o0WiIK4 0 0> 0~=@| 9P{ ]6̻,˞o0  @m P< +e﬽ 2M< !݉ p K ҈ v8 Ѭ `Ԉ +fh?`  ` jm|=|6%p<@ r 0M1Ԕ@ԥ͇ ͈؞@ @ʳ Ө  ԓ@W@1 E}Mt2  KM Em՛ɩzz`4@bwְp{q]۟\ 2 PƸ Pͮ < ش0xӍٵ`۝f Ӧ ˉhe=ޙ؉` Nͽh< \» )Ն 0` : "P|y jLf(p|@ ̶|Ԅ t aH ح0XGN@ @"يp~(m҃t؍p6pGNӧ`fմM*KL =i6 ,׷FpF B*j}Ɖ fHfzwȆl_fos@ǰ}wx ls mox^1 ”v۾펰.N~> n|/?O^fU9Qapi&UnUZĔreW]ܥVr)w81VYOeٌr%YZ/=QǶŠunȄ́4ZF٩e 6s ϟh .]f)AZp]&ʟR6~?q w8QsE*ָ<.kJ]FE"IGPx`5_wʩ4zkUfAZUYT~ZꤥSq:WYz뫧ƚSfd'TTnQU{oo p Jii1or͑;s;\rUJlUe#AAzav{e^gvu JxswyF拚mtN:9XDܙi|7ӯ} ۏ_+/$S} Mhze/Zx/D+Ћ gTb(ZQ@8G-f7Eà7Jb< 1d jBFzތ:1`E i0{ YςANoyыa1(|H!*% TJx.BX:/ \9c#S`" rS0D`,A 8A`E @O{ XC5'F'O w&!Z xN-՜GG1N xp qA1}LFt`! QGI*iR0A!2aɌ,C6E f`'~(#fQh'@2@Di!hH r_-\j!}Z@>nfH## =@% u6s aگwz8F]XA&V "P]BϱE!84"8s҈#Ta)< ,rT,;G :@ qSKb%NLaGKYـ,F:pa`5@J0Lx8#HKM%W+` D36GI"@pDj: $QM,f/dS엾P_Њd]dM-; ȓr['[fvF2TxȞ1M}E8y';`lp[F5knBQA,(zw=k\4؆[1{z"6>v{'\Z Vb [q嬱V ;|h=rKqgycaxLyMjCf[eD޷q\:ݠڶ7t׺'cn|rG]. ot[Wαl$a{uYv`n|p|o{lN[snNf⵬Ԡ赩δʭͪҩ³ؘƜĩƽᑮֺ˶¼ɫzow{u\previWhiZh^exyuoxj|Ⱥôǫz~xyyvĚ(((55)((6ƶhf[;;UTK`[XGG}8z'srh**%888CCCMMapn`FHHX##W88GTUc׋N_`IWX:FHcVVVjziijZksͧĸЖĄyʵֵsq磖թqԦ˷氲rZ(pp,NN0vvGmmH<ß>ka_~۟w򱏀),` wsтD8xZP O vpT*@[ gH X+ VbwȆ"QlxC: VX h*V Fԡ42Dׁ*T\6pTW E"XBx!LAHhDI@IafvAH uBE \(ـE+nQ E+AWCfEZ򖷤CG4\_(ba1F3t F}]k$t 8{~k;Y=#V<VJ$Rqu/)g}4 lW @A 菰zO@ p/JT{?c]=b->5`Ѻ@'-T@BLxCps u:D-td{D@dGpYB|_|pG!P} Ȁ'@J`@HK`$`gr$0X-`bj\4exrVEWnu/fDctOx5xG 8y8@}` 3ȄG<<`C@=F7\kfEKo=\|7c0__KJZWKMUCPGwj)')~bm@DIh;$j8zْ\KZbCN(<*7P*/Z)HQ9J[uDAUVDG$Gkk0ɍؗyU[tqW`fb[K+_㪽[_~yBz:P%ʲ *Hn;@{P%'okзJ*`UrX)_}<-ftn If;_,";~6`ȁ1`6/ EEx˻U% U I4hOڍLkԋ&yD#\eI_j @< };rܪkdt>kyˊ8ŽPYFn]Pܥ;D qJeUʽZTXi 1@1L9ǰ*/0jTptT[MDX𿓺+kȺ66Uź ,Ő σLK=;TUEXԓ ͖D{yϫ~LzѢF`f{ik0k*rZ2)ï~qFL|PCQW{\jeӤ`tcDܗqvL_J%_܍ =K` a:˱ڶ٤]mڬk FeˑбT9M6cmHЋLEz]$܍f`_0Pf挖bpxM 5jdJYM0ik6 d7`;Wsmm3w`؏FOquּ+gJ+ MP|PCh&¿jńl۴DͽEZ$%^,J#$N& *e78ky7PӬl` yX`YǏUEդ,\UMĜpM>8ͱ]MIdSbb>tEO:[JV$͊cz_~c׫H޸:PJdkۍlˈ.̉XRu^fB<_^mmlZLv>U@]_\q_%u)JHJ~IJLۍ܋T+v&^&V5DE<$Ԩ<}KȒ 3&%l~0PZ9KGdžbMmP|ؒ^O].lT 0 x]NuIptKPU3N^nEؕD~/8ˑxD T 7tDľ8^QfpdPP+#7:SȵMg +X—*_C<]~9V-{Un4Fh 0KP ` e `vN45yo8[EM/pSm}uI5Μzqm|IU[J\zCipSfJ S0Sx@W`LV_^ohA,X`#(6t4H"6mV,E+"dJD rň3KҬ1Mf8p` 8h!-QhZ5T>kTBOC-~ma[:q⸑+M:tk]|)in~q8}tΚۆق#RX%&efc: )ǖ.e"15:d)ʾ&09 g]ཇ;FІ=7jl`cǞk>o#6Ģ 60s-\p d`HI!+@!F]`?HJѲZUckv#E)cfv] q5խ[ykR`*d4>51^AflFĈ]ZaWvXvM  {=z'bt 'vleoW` >rQ?”`׿#J[Cl:2F]\JV3Bl,%.< +dt{3[e/lmqʘb(>vf{R<K>)(E +'Qvȼ=ƌв u0ta R6Ugܼ)_@z3a=^Zc.D 1ՠTR%h!uFOΞ{J18U\>w=@M֌~gK-/̅FA): )BP#rulH6WSA 0 m&0$@i8E":tjϪyvgNUoG 4!.nЃ(آ《Ɖ$m2v,7AP .zB@Wp.$P!AC݊5nF{{BsWAWvp!͐ButJр25^3DOͯkm`,{m]|'QzTڰ  >E^ |OV{9 "; e~6R]iFh4W~u:`aZMZlw""v IWGҷN83]z N2\p+fA+Pj4;.g0ACyVꪑP~x.#Q;VQP Ȁ @ P)@ @Y;$;8s Rc[/;WB;ղ ["7sl Pq kI+-1 XQ1- I1 P @3oZPH4|30jzVY7qRb!Cc" bG-ءڂB;/?lY,J@ 4 HX#Y|C4x#:%R~Z/ [A$sY HlDId|DcCY=;?67L6 "{s @Y2 |LzDCә;P/IX0b!6I[DlB2TS6@/A;/,WŲ 31+ϙz.X X@YȀG9̢:$OQՊ;tDiHiF=yHl*zSPBK1ĖX9(ɰQ;yy@r)Z  :h+؎:'vz{Qe9 ~J:'J<2ќcķxHK>mU>VpV%2 ʓhTh6\i!jW}UxDp\ŝ X$0)}ZcJֺ=f9N}ʖ@J#fXW=>T00DE8ǝG-qU&VuepVQӧeFhYUx"1>]GZ HU^Ռu˲U 3-!{=ե[G {Z8LáT3t7H|YXŁ X -PIѰm3y!V Z7Q&iJJj\܍ֻK˽$ $AI]-M{ B85K]Wk A; _E㭀5%WIWx^4ǝ6ܷLA^&rU\%<p` Xͻ[;Zu 0 ?&+6 nU 8X^$kT;EPEx6~Ch ̐GA)|1+Fj=BEOa#5?p6`(01耑]  $8 n10n,0n΂n^n9nn6~n6*m-ȐHnnNofnSpo^n^onof1^k)(O( Hd p ge O>; p ' DqNd 7 geVVq-A`fqqV_d ?D&!/m0xr'&r Ga&*d/r? /,'3('_s)G># /0:':s1XWA>dEpDGtXPt V"XD/! ϐ70,C=r HtHotFH `psh%u- Tp7WM^a`,r3&uBv0 `8%ȃ]v  0E~W"H$_m$u u3s %!` %p3(]"C9Rx*R*`>XR10YHn[3O" ^j;b xWAw?b P`ȁ&h`ȃ0A^ *"HHtw/uV1>uA x8P&'01Qp"AEk]( s3PȂǧ-8"3@y"p*?}0viG}u '}ٟ}?@/ r{qOE?O9w~跀*ׂt7.^{@' 0@sGO7cC,wO4:0P (Ѣ$h1G3jtaJ>d˗2aʜi&N5s'͠=s ϣ.-@HOJ:'ԬC!D\\H%CD\h-ܷDc'e#.>׈_|#~%؈+_ɚ/挸҈.ЏOOF 3EcK w[ ,ēxէ_Ϟ<|O8OϿuP) "| zM/yWIHD1JPtpѤ^85x#9#=#AnS\y$I*y8Y \Y:V,^[JPaDܥ$m$&]xoBX@aMJ.r(*(P1AܱDctQiq])4LF(Qܲ**"jsڦ"˝Kct %XMj:+D-H&yH FbE(c S*KK-骻.*Akz $Hso0 & DK$ 0/p){h1k<#)F k1Ls"|+r'll'QOGUKjjc[縭al}vl}kmmve-labEhmwڅl+w\\@*M hISOPSp3zcק/Vq˞܆.;xN6XX8W66p-|JtXH`)J`Fcd1K@?-JZncǹC+8ݺm6xvw;0yd9s QP`B%Jdd1k}C*R<ݮv۟G6ONxɓaZx=ZkBɯ+cҞnܞ^4xwU 7=M Gx3ߜ8YtV¸MWN+iIhLsiOK6| [vdžfMC0(@>ITzդEZt5!5!J&3Fiݭ5MZ͊e` h`$ Hmq@ŭP@a!g.%Me ۗ=7<VlRiQ"0 Pn @ Rp[$5ņYAޜEkh쾖lml4L Ǜc.T ]}fCS 4 @` Rpi0`e'gCB.R2šk)Ldk@l=Sې;HRwm2'qo@@AJ T`8 DBprUQh[2R5lfc6,ljp-Aw 4s{c႒4pSo} s)7qvB ek2|K.|l?&<]qפa  KA @@*:[& Xͻ՚׼^~Y]MXhX4ﱓ V\-ؐU) Դ`VMԴܼ 򌍦Y`FM/;Q QH՘Y ĭI]9XP%Uu`QI@^TpP,YdD1L ˕`ơPly  ٜ8Xd bdމY/ mTBƷ"2ηT @ xCE [;k0A HEv>ەXk bۥvՑ |"po Fb8}7y 5EPU63ZX|0x5RD0g-1I@/o?:>O0 3TSX `z] vˉ=91J_d,s;61Ux;Y&A sY1 z\aY 0 \Wݏ+pl`6 031ÀaJyx9 {:7[zCVնQڨoBx<#z?# nfxqd5Fla}@ n`Ѱ;pNtR̸uAWP@OOG"[P—SߍeUyԁ{3^_SR靄hD ؀ |Q~t {/-x b7Mq<'i b'*B ~G\Ǥ%Ox~ТT |jZJ GR~&{n[Mu0j |,G""1IPhB r9v4 SJ[C 5QMEf4,V$Ɲhj *Î=S>s=Csσ,J, 4I$bJBT,c NAuQQOѺZ국rݕ]uU_wmWbeA\'pɅ(=2 00*119D$ = 'UT 9K:CIÝ|ݗ|؁G5n҃p0׊"&qԡ-q4tK6>$:]q}9_Xf~kT"9/+#yO&5@ LW7iWɊ5F#dv9r^{k-o|thȐm{bzbzIlU1F UBݖKb:cΘle)s*\CBulD6 T5|sr5zcFc3|X[٭[5 w5elC(#2;PՅI;o^Df۰bh6rDosfoVz ednSRZN;i,pYQQXg)hc*r\*<l8+`C5Cv K UE&KfN\9Ҡ"B0*_Go.b0/!g 1 1 P3Q|IQũb`Qǥ hB!"FF2dnBC&^&{@j$(IId)1n I hRWwńo{$:…a> 06lDdcnA,5mh |A B˝M_&#+.!3B:0qN3LNmax:PP{F.s>zEtY/T@ ׯg^fviAI 1%EJUҔ0uiLh \ MLw ,q͐5` VG9咫a뾪Pe_huZVZ_n]kVn]^I}1|W #&@lb{X 5,c;YA_Ulc9k#{ݢhIXs,g9 њ5yE]G9vifA` mB H-Kp[e.p [=mj/ۻ-uˁeu_^i՜_]av 0%"% ~ h 4@&8FЄ%  ְ @&_`"( 6aV+" Lf &!K@k OB)LTPCnW=wћ9  b@)s@$C@3 vu4A`Y;LP4A]]U  68 B0"G@B " V<^z_-7U"HqElp@0Dm - 4 q.omcv JpxH 7W-7,#XHgk`.c]u.xAVp\rTߨM_`ƛ@XH p`%v7 )65V7׹[ ctGskB"2js:i]کKy@ "I Y;uek=j,=ثzx'6vc&ӫWx3@)_y[>|'yw> H@G_W^G@ѳ]_~ y7O W=IO_o_}ؿ~o޷_~O~;itksnap/ProgramData/HTMLHelp/Artwork/ttLoadImageWizardOrientation.gif0000644000076500000240000005404010534177570025245 0ustar paulystaffGIF89a""'))=:-688-55Zg/Y1[5[7g=t0@>/FD8PM;GHFKNVCPOJVXQNBXVHZZXANhJRdIVsW[bT^sVeeZfx_qp^pna]J`_XebLgeXrnXwuYhhhjnugyyqoezxkzzwP`_))99GMRJ X]_`+N.T+X:U5[6O,^5]=a-a*f4d3k;p3n9s;x.nH]D^@@IIB^XXMaKdWgUk]qJgMmNqKsRmQnYsXueut}jtffdzc|~~xx``GxH}k~ntx|yljzs}UXOmdmt{vmtk~{[}fzi|vkvmz{_…Ɩ˃חțЂ餭ĪǪѴʶԨӦøɼƭѯίҴżֵ˲չúû½ÚӝīĻͶڣ׼˫!, bO!ÇHŋ3jȱECI$@Cv1cȢ#6f": b4PFB5ZtѣF":5("Cgׯ`ÊtٳhӪUk[o-K+Qwu@3XM6Ox*2vAlN.t֬&sῚ^y͜ѥKϑӦs9nZ!=lئovCGolĖMmvxsʍKN}:سW_ݸklN7vk9ϗΝ;|log?ͭ1_8ͳ YlFDs e 2M1cLt~QgVa%Flkh3xF#0X >xBhFH(O"dNZ6a%deJfG:)min#iNi[;#z'}9z;1cF5ʆ1biAOoOG>E`;:PV> |! Q`;Ўh#'X/ 70Z0Ģ*,bl0d`!v b ACk@|Q9, a6d N짾׽/ t>oC-CK. p8^>l Ǚ+M0Ho1IoX1F_u4@|Ad((́A J6V")ZƲVJ64VXhBVtQ&+@ٓ,Qep؎QS1ࡩMU2 &7i#,&˱纜.63k.ia2gN!@4b<B3sxC9 3: vf>wj~`Rdt2;˝ skZ5ԙ2nM l 1X>Fcdc7hGsY( mc.Km@,K (t E/EOm|/ ~^ -~܎8#>e`O" Ww\ x@Av8go% |xyw-/C.x RpVǸ +Ts^ .vh{}e#@(vnO;W+n;+@:qpO:Qobְ.Fok8MҟOz[c߅E{"'iύ?i?|ߋ^O>>q;=]Oz^g>=iO~~# 'y 7PP ؀Ѐ H(ȁhX!(h$Ȁ/X4H(ȁ)..X,56(ȄxJH /A7(?Bs lhm0uXspxЇp؆}HuЇxlvyxȆrhk|XwHx8X8lxkhX|8h臔({苡Xm fPXt֘ڸ؍k(Hȍx؎縎  hh؏(Yxِ YȐy IIIّ&)i$ɐ. &/69k8x`9$56)CiMْA)QL94ْV9G JyN I,DTɔ881yR19Ofi(dygYr9b\ZɗOvVz)^ hX镀阍9Pi~y4)id)yX9)=Y2ɚ[i"ٕyɜ+)霺 %YKl ؓWɑ) y"ɛ`IY੟\ٜ@Yyȓ>ٟٙII| ڕٗZ)m0 *h 9Љ/Ji穠:JX)@ 94YɤٔڡV:r٤ Z R Iz gNɞix:9vڹ <: jIڗJ@|9yZze۹Z*Xm٨8[Yښwz9*٧3z~ژ?JZJj ]ʠ-juʫsJɡBZzΙIkS 00ʐpɒYY7y鹠*NʰTڠfmJ; Xd*` 34 iZ ٮP xY^Jjj5+UI.)P ys @G &p 1@` &Z꠪0 :0SXk/ *={~ʝ)`J %zH+ 0P 5ހ 40 40:@`@1 & 21` P% 15`@  א[#Ja[ ѪVyJY)K- 0 & %06P%@0 % ` m 0 `1 p@ 5p %@ mxZ ›:*k,OZ143%p 0 ɛ  :ذ%P@ -x E\Q@ 507  H 6 0e%<&. @`0  p p Ԉ}0@}0P} } \ "+% \9L4L~ͷycJ{*) +ʘ .㛐,2:͹J҈| J::ə L}zl8 6`M0 ڧD iߌJ:;:և{i}:0 *ݰـ[]ԋI؆}h)5ܩݼŠ{ ؀0AU٩[(k{^'Zj{m幯x6)]ъ,*~Մf͍Z`Y E*xj,Z={-4\ZͩMꢭ+4Z|y-= ; Mt {9&]2Z-Qy7@ 卓x'n+j=zi<.)RSNV܊jҼ* < UykP;ݏ)~ԭXP苞薷臞>^iynJ*l; ؀:0 y  4@ ݠ Php   P` д&p 沰PP> ݀켠p  '@e @  b> Ob/ o /?/ ?"Pfi͸7@pR 'P p 7 : 7 `piK 0'p < < P" p:O8O@~ݛ1p?_ ]͓M 7 _ ' O -@P ;0 X 8 bP4 0`<,Zɖ-ի<~  _j mtʛG<|쵕)ؠխN=vցx~OO}nyk)\|ЭCtAOt9(膲G*aK$EKP41ER1'y1EQ,EcQIV1H{wD\RI&u$--4D/+2/0N[j4{:T-u;4PA%ePB,PEeQG}^)u)S e,Gʓmȹ'UUWeUWW<$-Sn,;asJ='%Xb@XeeYg6Zce=l婡TqTAt=Uv݅]Xe LN\sxʞxZdևd]نEf~Yj25ӣB'qG*m$t$Tlj%o]]uoNUNmdWC~-5϶gᇊx鉣}Xi/θN/SގR'>(N:FoȩL8+GUyץ{69F8K|N}õڇFaX 86/# SÇ/&ላaK'jhBkGdGENF٦{:fyoiN5V1&_ƄӢ id0}H01wizN=jǡkw$E%0<#ƫD7B/]ҫ& tr |{/~Ovh\?ҡE<ª@UGPop:t@R 68Lt"c&2a zqYpBt03E2~pha}_09" C/"@wHD%X@%c I(Z%̂85ϗ[BH[1{m>T $M" ;\` A< sYTb:D"NY|5@e,83ty4c^j6P^1}cёο"0`XM(F}д*1LcE/J1N屌XR"0[<~M]9ƱT;O T4F 1`Wդi\j:Urju*:5{T>=j4`%{+h.sA>N-űGӁ'QVֲlf5Yv Ѕr C%Ep*a)4rc='vibY*bS%+MGL E4 Q\ 0@es\ G4Q걳)3MjBهeiCIf(6p\(=O׺`o8b]:54?y tÌ+;zauLh)N!Qbݼ赾= X!w(>Sףݝw xO8[hI73ǽ+%ɤ56t<$(`q }&.zrX"=XyaZa3ڸNyóbq_ 8Ӯuzޟ?,B.oяy_ovJœNpهiΔB9 oRq| _Y_Ex0wO,~w_8%,wyx)vOxOxO``(0:1rPꨇP} | zuX(x0  8]ȋttH 4 oH H |؅n؅xt0jWq ?Y(]w@\p;OzK{S=^nn7+ӛ[8r(9ɇ9ۗ~ e6ZT2 ð^B=txR{ c K8T2  O97؃qW8(I^s`tsCx]=s9X,{Z6a38[ S"X<0` KQtsYV2osTN5MENTR;=lxWDUD9xp7]9 S :{Ze$\C;U1H4#{\R!{:+Z*u9V61X7X2`;0օ2XNVA7Y]Cx]9|?:WbE5?+[34 ^l^}l^h^^%_}͖^X|Eϵc 9V1h (@XEeY}]1(9HdЅ[`l(h,!"B^ō*[(yTVaz˽danav@q~aaKlaf{0Q|]OX h`o55]>3N2``%2cR}L ^ZEY-}7\-8qԕĭ2v5z>.X@SBf- C}nrcb1h)@h/8Vq6e21hsV78Ƽ >E7'.D-]&-ӞuJn ~@pX7FPDD%zƒЇu:RJ^m80eę(wo yG@.\ktCs+kw5u {thqnxQZ@ C֓=_RSXhfOBK(ŀ1p@POFg1Trv87]=p)'txQ0`3n·,3YݳUC4D^Π]\n bTG @ & (ŔrHD6̀؅0$W©6Dڑrdmv@`6HL5>gS.l=H%)BQKPp78w,^[HQ;u<Tا4ğ^InD0PHy8+ ? R+ iAJ}PĶ&xkp z몝 ĒVkOf_sL @oNĆiu4tQ**(:uB)l8CRmG^W`J|xˇzPįTŪtGkuX Q{<7VD =a+чw#_@x1't^1,p[8(+&x'@2By5~8ť ;o ̨~3?畧:PCrLf%шƇ(I!WlkK#dN \ /-,7 >􃏃x A(%^&#)&=ei&iyq)XNrJf*Q(H 0b#IDOB4@1Ï>$\5yfu'~qy^,]g4erWz]AГ1Sπ4SOږc1C-‹.CF* {QWYdQ!0O+p0ܲا?K=r>;>ꀣ<2\8t .| ]GdRbAuxMYpOj0`0>,=rfCA o1[(e%  J Z_Y*5:c̆ ws-peU+"g='!SԂ^GE8n, H>ӏnC86#4%JGF:Vl}Y,>}ljYTG=y#(C2nmOz x9%Em8/F H@A)q b*II.w#[ްmsfA)܁y䨗p8} ijj5,?8R&@w|s^tį 嘇zw-G}Cx\ixCBn4 }j |;tQ+`Q1 0ҷ"@B7:fsB-/ rc,-`v-.Ge__!r?P㨓80,8$N9DqKZB. ^GV P&@W* p夃M|LrR^=lkH[nb-^ќ29̴e3O6'tOSD\ap,4fqdrxs80 :.% 14a`?:" $FCHA%vL|HOj uF=77C) 2.P.=[e!tp tC|"Rtc->3>*^ǓPa׭#YUiܥL!#X C(x‹(x MU GM._7&_t8: ?rMh)hY Du~0Q:шb7 M! "Z,:0CZB(CCZmcf!$_:>--O# <$O_c]Y!e\ʰٗt% HErR:$TWoD]eW"-(GD"#'@d? T&!qf,CE\&%9)r #`wLX Tx֏88ٟww=?B #EENfPL~T<4`n tA,bY Bf>YݫĜXVYmlBĞIª!:`y>R9@ 9 X\~*ފ,Fil #`d_(ژl-%!\ Pļcy;ܒgZrC5i݁Zv2揕S$M(-9D k@߃-l>n_7H%C$1:Tʦl :< 9'0MAk6b-߫oκ.Ej?T)D9Z.j>p(*Bā&X+Yki]-m׬ֹ+mR/-U<ʵou\GvlYq/ˠ   p 00pÞH`< +Gp/=)(xfWf?F/Za|^oq|^~q|ܰ| Ʊ>ȊLu{,UD))$H?*ժ.]>b%2@p>,^"wݣ))% 2DHȪ. jᄅn!@D@ ?@)kL2Br,von`>] *&b:f <@Ll2 &4V<5E3Vzk>Oo\b3Ł83y@] قܒon+kY],t+>\=+"I3CB2j?Sk3uM/4PB֍R3ǭG'OLY(?|Nl TgT-Y>n(ƭ!CYW,=W}GtެP?F9d=t99w=|y997PŽQ2x5( `CE6Bp x?t9h >C3)'Ȃt/@{R-:x+tEnoE9\8|69d8×W[+JWPZht8‚~>~~˾ E54gz ~6:'lBl7tB,dB-h;SYS/r31RZszDH2Ý5f?,Ll9sB6d6t|C-@pV 8L8Uv^,qȑS?1fԸcG/1K~wJuL<9LjҴ&O= JO6g=gΜJ=zs(^ 50"{EڕZ$zu[EoF'[<{AJ(ƦBZXNKw%T(d;(>qe*;z-YҧQ$uk:8Ւ4%<*N} x0LIu[N'vUwM6o FXױ_uSv߽cҳ|y&Ufʽ*~ߗ_xp3fkle[<lpEBnJ3E]<>,[B4QEAeC` I6&G'zP2㯸"㭟kLQ>$'(|+/ ĒtQ5Ht(` %xg1,S9$+Ov=I$`i/\4qN5OE%QEմTQU]UQCUVZ?ŵ[tslS=p~s?C I=.FE8ꉪ)yDumWI܅]y٥w]ܽ7_I_~]w-xހ] ܤ=|^R ǟ;l433jNDۼ /GaYgƚofY}桉.zhmyG&aAo1MYdQzʉ |'Q|>-]hЅXa~[vЛ~IgO|ʡazqw E*|DJYh1똲'n@2Z_#jߍ;{S2ʠf.(qubu (NPJxڪPx&'pJh令ǭS޴1[LBC>:yN83)[u|< %Љu𠂖(-HVYG(`qlSA\B똇]uyKrvT]M?p)c"=ԺN12wS g;vTKnK?:U1sCY"P((QXP^* ^E< ^еNT﹋ tͱ(Z¡P_ 55SϟrJ`cAcv6Ci)$gNvBhl]~ 5\9,Șdrv?g;\ IEYcbIL봿%^h(xCڧj=f&xmMYWpHS$ԾaL:yl '-b0)]YqoQ)&kEӰ8e]cZiA1ٚTNjhYgm>J .X`Cs>Lnv80~A@lc"h5LkÀAm!\͋n^doƩ:w[ǟVڜ$X6UWl"ZPʂ#}`M>F6~tC .>nC%Bf"px}tx.uU裋t "'Qd]PuRG/πQ;q], \~̶ٗ9`#goa✘W'zyu0:(|`,;xT\8/KtTDִ+fJ7Ng6;[/ʔ*’ᳵ2)A:hLZ`25O@QDG=G{a'MBGokaADA4Xd᯲ǹ&t=a(p$C补RpMPLL0>VCeSi0klo0y0Uggp %,j˜OJDϱkJt`x',!x Æ(<%ľKIJa>S=3>?3?3@>AϠ?A@>?A?BAށOa>A+DAB3@3A'1R/e..(OGt!HHHHAI4ItJTJAA~JtJKJ!N]+e;g5i)( rGkaC/tONOtPP.tl PQBQUBQ UΡ2΁R!P'QN15R-BY-32%R%g GO0AAWAWwW{UwuXWWY{UQiUYYX5SX.Z[UX!ZԈ+-]2MSNѧ$D^Nu_5RUW^5`u&R_^_UT/_-US-[t_v`!aUPOiz.-==IpabPNOܴ8MMݍځ-zzS­1[l0/V6\ȳez',-{Ie*۶}³m{pm0LN}3=߄:*M }|)={Ur!r;[~C;[՛;KیG֡IR8I8#7RuA%pTbYTb!<\sTA#N,|Y \\% q,&Omg|'1<=<@WΗ4Rߑ,a-i.8s~0-~ Spm˂d m&"RqއTvCsέЍG1rZV,1,ӍPv5iNП3|:lOVI;v| 11)щ !})?Sҳ:}ZrїU"MUbrml=ulf}om&X\wlk}АFw$|rCmםHM֣݁ur=ǝ܍mٗ,rM؋!j]ߧ&inV &/aAa>+W07>/*=)^M~!~?/[~?!IM7^S0e^-^g>Kg#^> N @՗`Љa!վm\buTb`>Ua0ľ _a_׾~ށ>^% ?~N7_GU3y>?~YsݞiiTB @~ı?ÿR܉WYu č <0! :|1ĉ+ZFO/ ;itksnap/ProgramData/HTMLHelp/Artwork/ttLoadImageWizardPreview.gif0000644000076500000240000002327110534177570024375 0ustar paulystaffGIF89aC1,E?R]]NWbgZR{a]bzwUItqrlWgxbx|# ;1G])R)Z)Z7MVVImkwzuzz¼}̾˸Хўԭεѵέֵֽнֽνֽ9BBJJJB JRRRZRZRRZccZ ^`Zcckkkksqt1R)R#]1k1k1ss!ssw(!!*BqCSnZ{޹Թɩõίҵǽνʷֵҵֱֵֵܹڮڭҿ޽޽, a [(\p !>4Ċ/jbnj7z ɓMDYC*[)fB8qބ0'O=w*hУ@re˖+O\R )dFM7_|׳fӢ]-۷ne[ݺc%Wo޾x;ÆEX1XƏ<9Y]`V&-K($J,Yp%Kk/Ud{vmڲq}7߻[xqO~9ˣسkν˖EJ*K QH(,MNϿ(h& 6`!!CL0D^t~I(z&bx-b3c/¨<@)D"bdr|X_zQPYb9Έ9fIc1re_e,82fptY{w!H0|OF9Dzq!A[1MHH`yf+.br AQv`ivJ6ZkvkaLg}k PQ%tD`m l饄Z|HaGJq0&辊D&1ke)p9`", G,bXzoFMa CġCtt,rL33lb)!$SQka9v8^wtPG91vp[Ta8ݩ kjͶ 0Ka{F`,~>zZԁNW0A/AU(Uׁ\P ȑ-쑁9$q9w|`A0pph2rKЁx̑D|^ ~tA@ AJ&b_o}ڋo~{+M)/aQ o@24` ;|\P@÷F,b(: 9ȁL`( vP` +Ea PBbpx0Rh0@ 1L&:P*Ud%n]TF( 0LBܣI^/!@DInC1+p ! @2 TA@a "JQ)H _Ƞ Asp(FR 8P#~p0K db0Hb&2SτB4j*q 1T6~d 0:0 L! 0C,H _态8l@ $as 罸 |N@?` t( :p9 0Br8 0P 5YϚ 6)NwӞ> *PS,ԨR0RԦ&GjTTVVuuil&A I(B@V!H ABp*5Z8* xy%`U0~y+j`M`h"|bj{ C2ⵦM-jUkZ֪6]mj[;5-Ps[Eo+7)+*J +{r .vc]Uk"pzAz"=oK%.lW ;`iR&ѹcZKF ^n=cϤrqo8%CbXG0ScLژ$kc2]L D#ft.KV"~0m 3,f0h>4yn3 f:z˳>πFb-~AF;ѐ'MJ[Ҙδ7N{Ӡp2ТDQ= VeMXzֱ5u\׳[b[6c[ώ6-jS6gZ u*pBFu[N=vݍo~{wN O8p 8'.Sw%]J8!p\''y8!r|%[s|%?m.|0y̓])̃+Nzӧ!Ϻɷrk\;.f߹ap{PNxϻOr+8>#񐏼'O[X~{pOOW]O}{Mȧ;з+[;O/{oo폿U=?q_7Xy{uPw) ww(pxw0)`w pwX}|.Ppx8~Gw  pp% !4` 0#I!" )< w @  <Ё'p  -p0Ɛ/ ux y-@r*vXw Q( 0 0,Ѓ?8(W'w/+  0 0؋ ЊЄ Z x pȌ (0+ P8 pP rxV.8/ Є*0P&G p  8` 8؁r8% %`鰌 Ї p  Z X(A  @%ЍDITɎX GI ) !(09} {X7w` (  +ps Њ"r (08)0 Y p 7x9 'p"  `' ,yWs'8%Vi ` Y5ɌVٞ 0H #@(sX.y`Zy("(%lp[y  1ʧy֗s P*. و =  8* wp ` ! )𤛀- !(! Ox*0qؚ#GO 0ix'r8ivjwqg鐫68wJJ:wqʫjw: : 'Z⪊暮Jڮ2~:z~zZ~j}k}:|z |K{{ {|۱W"y;&[y {*kx .˲ [/;W4{~8uײ<=@;ugD :{4JDZy' hM[2w0@0%  ۰  `Ǩ<j{ P~ {k Ǵyb~$ 8p p ` )๔x%p 0( ,P x $  GK0@ @M8; xy0[ }I[wȋ;77p* `% p `P{|Fwk` Z >h f M@iPp , !ǠĖ ¾7}_w(ww8"[7@\갬V|}ƛPDž`P`slxLz\0uLǀ {|ǃǁȇȂȌȋɎ,ɐ<Ɉ|ȍɁyɞɠ|ɢɣɖȤʦL_xXw 0 l˴˹˵|˺˾̸L˻\ʌ̺}%'vHJ3w x:T]VA x8}W`~7y'bjmYC}k=td} wˤu׀mo]΁؊mΌ|' P ٵ Pٵ١ mٟٖ٪ٞ ڢMڛ]١ }ٞmڝٰٲmڴڴڛ۟]ڣڶ=ܹڼܿ}ڵ}ĭۯ-M½mܽܥݭ =mD=ywww|w߀wMw~^xm/; fP ^~@ Q"$> [ .02>4NP5<.~ Ess$rN+R$v7 0r 7G~WJNe>UnBM|( -su wrvx `煮爮 w n6~閞阾閮 唠>&P %)$벮빎벾묐[Nn - ^+뉰AxC-~  qۋ P P W%@ *^ـ철-@I+ * P @ pn u@ ) }"y 0;5)^ ` 8   Wʆ( (/@ @DBKsI*_/  (`  9; : pفzl;pN 0R\j.9kl^Ȯ09I-0Ϯ A/  "PP_ 0@`nNw -Vٖ\ " `0 @`2&XUK\XQΡI NH+EP$(A P@ LJW^:H`A,jR9u e*YQI.eSgkp_j}9ժ^ʤ(Έcef eWBQBۻjÕmOGP: aĉ/>`[ֶE98m%GyfCss;.gְ! O 3ÕL,Z䟏+/^|*өɴL䛱k!KgpСկg^Dϧ?˯DzI/+p@l"p¥Ω / z 9̐:CKjCOԥ/­ (qFk1#F{0 4H$FtI(t(vJ,pI"K0,&4L4ʱ4t),M:,rs cO@r5dQFeŁeELd`jr57.4j2` ]d-A@p@F@@:DQ D ih`Pi50aS] OUb5YG6D^hUA?TAYA:Q'PXHUXJ@bwæԱ)V> r9D`Pq A:d.cJV&.DāBaRA )Ёx bP 9A&0{ P`a ]BP3a mxCPVIe|"V@x%(xPKdbByx-h +B$z"c$cZT!Yq)?t@-|COt1̕hÌ!?A hZURGX]1QEL q9-:QY},A9R0#"`A9 b#$1ܑzb86˂g@Ā1rG1i"ĜF1, zUocfnp7<8d\x=cbz^1fP`7PyQr)_yS\( j0c.y^s'KWzә]'8e\]ZO9׳u38*n7Aخ=7>ygPcu\zg{ow};g  zC>\^5??>@u`[r~99o@ z9΍'_]`*Wy{?|?}tW =}?/?O>۟WOS99?3|?&9s0뫺 T 쳾웾 Ծ <\ОU/˿cpAp0k9lA cA8"<4@{SރpAh:,@Ӿ0>SrQ(B> :`:$l@?CcBc#08=<'tA?8Kp8SB/<2 @+L?h=>%A&8J,?=(t@ ?="A;7&FcĪl>CGE(8J:BmZAhH) ` B88BS (Ùl ;ǚ@L5Z8 >^C;Fl4FeP:dF=[DHElDPsI{9d98 @ǜ<؀,YKE%#ctkdJSFB$DɌþ&! MtLK<ˤ,<΄L״>d8/88v=? @S>TD@EDTG-TATHuAH5qWx\p\xLTOTO}PNc8UPEN5UOfsQRY uUU^U_]^_U]Vc%`Ea-Ve=Vf}deVjVkujkmVne% / WrՈqErp5v}WxWyWzW{W|W}xՅJx؁-/eMX]XR؆e؂}XXEX؉؊u؍XXXXؑ5ْX]YMW ٜٚٙYٝ Zڟ-Z=Z5ڤEZeZmڥZڧZZڬZZڨ ([=[M[][m[}[[[[[[uۀ;itksnap/ProgramData/HTMLHelp/Artwork/ttLoadImageWizardPreviewWithFile.gif0000644000076500000240000002456410534177571026040 0ustar paulystaffGIF89a@10ECWb_M[dj]O{[^Xv{^LxprpOmwbtsz# ;1G])R)Z)Z;ObZHtj|{t{xx}|Ơ˾Ͷs֐РПӕѦѭЭֻѼѭνЭֵֽ9BBJJJB JRRRZRZRRZccZ ^`Zcckkks tsl*j!1`Jx18KO]ras޾޽޹ԴĤŰرѿڽνֵַֽڽ,҄-^*T †z8Q"ŋ3FX#F;VdH2aC%G<2%˗.c͛8sɳ'-`r *H@I.aČ)C իXjʵׯ`ÊKٳhӪ-j1aƅK-KoZ<ՒE-UKQqʼn3X2Ɏ!gn,˞CWܙhΧA\5ԟT"ڸo;wݾwYDYeI%H#2== wKOӫ_Ͼ˟OHO2aHaFy^y5`=(a:Ha]!~v ($hbI0 @yG^Icȡ;`A($NK&NFyTRI]ub.r9`FFlPQc_gPlq5>FhgKI8j!gt^a(HGy0 I؏GE P/)T쀁U{lDb5 ;P?Ag P}l A$ At؅?|  ! kF)af8Rt!)JqLa,]JV²,q[p 8"TJ8 Ld ]v)`A ]ذ thC b98 a *lV^Av2A 26RP@)An댁  T6A͎j HCJґ(=JSԛXK[*nt1iMaӛ4?)PyS6 "4DF)P Q_- F9W0*$1ROV90a1߬-D k +]MJAL0kx:uW)YVŬ`5+VN,h5QZiUպelMkS)DX"iBڅvr d`a].85PZ-mH?[Zr7xK^vk)n A`6 J ަ0m&0BLFN0', ;3 c pqZ`F))1m[.$\ AWϨL"CFr<%;Pfr,*Sa2e./Y^`L2hNTIzX'":Ơu3lg=xgB:χtF;ѐ'MJ[ҕ3&@#%0a Gԥ5&ASԎP5-jUԫ&u%^jZzԳ5m\zخNv}fv6-fS;ئ6ns7-r浩_Kl&#N\7{\7o߳h#[޳x'\ox(OW򖻼'@GHG8u<:|sAG΍tGWЉsu|=ҵ\@ם~t_?7џsGoǺczkN>G+;;}GCϼ7{GOқOWc?B/Ͻw~gh{OOc?{;Џ]/sL=O࿾O۟տ#X~7| ~8 8~x}灠''xy g9y%0&&y y$(z$& ',`(xH{ ') P!`!p:'  @ ᠆5%&!0 x@=5`! ## %,Pz%" ؉`,-``>8-pcpx'~/-p ' @ Ҩ ̠ (}@p$ 6`pH & @  G(.8. 8%АKȌ7yF07  ДI } `O$ؘ I G   ^0 n&(+0 y(&=G/( +p 0pnٔ @Y~* .0Y@  I gI%P fɍ_y\`I1)/)pȹ)y)  )@ iȹp~C Y;9]%@&=w:`ɍ0xu8d" ~^]h;;+y " 0 J p $ /p! Ѕ"c  &%`hj w]0F) Fi]h $@ P_Z%ps P 舋JZNX%@IЄ*? qHY}Jg`yPw'[7ڬګ'G0@{J{׺yʭ꠭P00z}Yw P&:~Y;{ {ȥ Z(${[(~θ?.k̷/;A |K8;}<;}۳@|;Dk|۲E7J۴NwR[wVGZ۵և}^;ǵd{X{jku1[k;` oXzɠz# (0 @ #`&*3pٜ"@/()P #8à Ag  ;wP5'VHP4K0P;z@3м*P$83@) $ 0P p|k1 75` "Ȩ% k$,w@4#`H)7z@P , !p  0@Nfwy Pg , `jG8" y y |+zPɖ|ɘɚɜɞɠʢ<ʤ\ʦ|ʨɧz~y ˲< 0˶|˸˺˼˾<\̷yJ0ݤ--Ձ P (pY2p0  w0-P[s⎀ `rp pՋ %@5%HmkP(p ۂ! p  ٠ pp` ^r8pj^)0 0 u`͐ 0 `@Nڀ~ P #0~ ͜` @  08^w@X/A6̮, P 5g9Հ 0 5 2RP t & )0吠,8 t0   2/@` `v P87!)~,y,Bq_͸`YQf^#p=|0-G MLWо_#=(-,֌ 7 6 w _ן ֟ ԯٿ/o?Oo_fu `8u%N8& qǏ(dɓ S,ic|9sM8] O@Q$.S g+SQNZUY)^̸SaŎ%[Y\ $][Dg :a(K.^|߼@۸nG~LpSSzg m")өMSV}zkԧ2͚kL)dڶ[W{MtG=V)Xѥgu)$^(.)ٷwx/ 1ʮh{p_)::N}ӟ?" sj3AZdZ0NhB0NC 5 D+4L5ڎ#δẖ8j,MMq(2B$GBL+ ( GLLm6M3aE:I-,L3DLb#%SE0 @P6@Y AC$9@< F$GPL#9Pڄ9#45BGDYNV9!6B)aHaV]muYY$L9M@80%ZxPD&!~0AWmM q5\ZMn1ҨSN2iEYN9rXD0LF$HaYW ^UGQW PBĄqx@UEnwJdI Du3EMpuW `JRRJa@hK=`V ZQ%fBhFJ`E`f`^$D@ՁOwJw |M1,5jfgR$@![mOe14hNV}Yq~,}fi]fO6)R|{:rydCqYY@Yh(ل|5Q'("F&X l5@ PV"|_BiMyw©,!P FlP"ZDxp2 pV ,?|a."\˃ "d!I{($cUAdžO ȝ5=@LD$^FFbC`D$Y$@|D2D$!áF21""s1B"3eT0 B.2<$)EHV%yL`"%S+Q \n"7R^[:y̧ЍYa5tkDIB7;DL;BDDODvP6P,EDT3M\EVTDWSEuDE\|ż ]VŪREa1 b*>d4lF:#Fh`FjEk*]FZ^FmB5GgtR$GFt)i\ǐFw|GlG:Fz)o4GfGGZx|(ȃ4HDHdHH\0ȍHHI4+I4IdXT,HIɛɓ؊yg,JK|K>˸˹˺˼>>Ŵ|TL\LdLLʬ;J8LdLǍJMMܼܼM|,LXԁlNtN|NNN͉@NLjGM͢$LDMJ嬈8LONNL_8O$-P=P5PK0[N,Ψ\Pl$P$ЊN4OToJD.U8LuQ5P DQ]PQ]#-ReKk QЫMT\RP0IJAJP8=J;-JJ͈@7M `ҿQ8$UR7 ˈhGZD7R-USS0.eN T4]͢R*U>`:?]ODOP45Sm H$4ܜ\ 6VRYPWd ]SS N*Ձ8; xV4;%MvENDUEm6xTpIґMu uTQT+X´URYR,ZlSߜjMT@`U=pqӑ?T5J]Zf]YqTMU+MUxƶt[ۼ %S@\N[[㜈5\ȍ(HV?\[UΎENLUUhHPRսLpқI:[\ L[]^M\K>޹^̍K޾]I޹[Kۻ[Vǂ4ܷK__ =>`6`˻<f`MT~``n`>`` }oTH9_KۅKv__6^˻ab!a"@1ua!b&~.b FΉMP(4P.N0cW5V6^c7nc8~c9^PcdDFHVhg GG΅HIJvYdNdOtXKdS΅NNO^eUNg0G1dYVZ>)1A$_n$FeGa`Dc&ffnfg~fhfifjfk\-o+pgq.gr>qNgsVgt^gwngxfgy~ygzg{zg|& <Ɉ 0 (h NVhvh~膞h舾hhhhi.>iNiFVivi~镎iiiiiijj.j>jNj;itksnap/ProgramData/HTMLHelp/Artwork/ttLoadImageWizardSummary.gif0000644000076500000240000004157410534177571024420 0ustar paulystaffGIF89a%++-55:8+289/8K19K:ED:BT>IcIIEKOWCPOKYYPOIWUI[ZYBMgJRcIVsWZbP`_Vfe^po_qpa]Ka_SfbNedZrnWws\jigfzypndvtiyyw6O[F\D^B^MaKdTeUj]qJgNlRmQnYsXufvq}dzc|kntw{|ljzsmt{ā{^}fzj}n~{~ƘɛФĪǪѳ̵ӦøɼƫԮͯѴżֵ̲չ½üϼҽ!,MA1D0Ç#JHŋ3jpa5II j`fM<693c#KAhQ&E"AJҤPJuӦOLׯ`ÊKֲ\nMUmUpʝk.ҭuZeR7 T̰HN6kz)0ЋnС9sϠ;ߠz70m ժuY ٴo϶w߲wÇlu 'μУ7GM{7W[߽9m9֜Q0~hz1< 㺳/Z/=cl1&C/I,mr2+x= + ua$0'= aG ]Ѓ~--6(Ю.mZ $5<+ zᾘ:ήld.a_fLA;ae3Sʟ;ֱu- 3]x%G@_#SEAa 4*K'~ ̈́ a")1!u;ҷݡfh:J@m0 _As3*a* OuuP+R:}"P&)*SA C6"QD/^ӦFa|Ӧ=͆53bVBA #Ԁ40AܠY@ C% !G@[ !4ed<`btpk| . ^XGI˃d[@@|bn*NTذL`S3^= z7BٌĴ B %Dz|`)pҕ ]l`#hF~p%8M>ݑM8E `@#2}@S1 bP=Ah F7c34*`$[I>N LK0 4AdL `Ntvu0X1w/$ 1 I0h"#C |w6z;ؐ'>y'#K>x?x? y=QzƳ^} z?;Sҋ񉏾F,b~$"v'8!~_'߉W?~~_GGw h~ ~瀜 w'~~(7X7~懁 8 8w ǀ$肚~%}0 IL8 OQ(QNNXQxMZKXM؄ gTSkVhgȄ]XP؆Jpzxi(\S8}}HȈu{vLh~[~lhP p؊芫(؇8؊X؋H xȨHX xQXو؍ܨ8ܸ⨍hݘXȎ؎XhXx Hَ PFx"i i$숐'Iy&)#*9ɒ$09>)ؒ2ُ)9 CّV9:7>86YWɓY9bVِT_I^Y I Nٕ_hY/)zym)!)[ɖ3YG)? g BaIȑ:I=)(I{y((95Fɓiɐ hz)DX`ɘei}yp TxI])CɜɝԸ@9k ©ٛ/ya ٜeɒoٟiəהi)Iʗ)ɕUؙZ幟)x"zIJ꡾9>ڝ%)ɜI+yI*i) MJ:8/ꐵ) 韈9+9Zy9Jٙ퉕Zyʹ9>RbNzjc):Veʟ٤;[jL:8ʧ~*:کɢwʞaڦ ꨳ\zY:I9 )ZD0z퉍I]:~zA9ڬ{Zj) 𪩹 8ExȘ(` %! y f  -Z|92ʁ*R w  8 @ 7Y 0 1ww F; 6K 4 Iw 0 m7 CZij)䊬9ڟ Z~(T 0 `0# !@ Z$  g P j@p r@#S Y@p@(){h6 JKIʱJ[J:Ƞo(ip(Zp'PT@8 "Z'qU # qp뽋# #ʘ5Zf zu *LົEZ jǛ0!Qi@ '@U#k } ;i)0kp@ 0 $< gٻg+(*y ?9 ䷞ࠌyU[ P ( P h( q'0$vk0`P kp}#`h0w,?g뚶SjW _[h[w @ċ l KˌG }{˵v̚ x XM~ZL *cLLa81k: TplZ`|njH鬧J:j ȕK |U| :f+*m  OI7|[ҥ k $Z*OP[[>M3} Gژ ݿ5 2^)-@~:Y*]ɛW|z'mܚ `隩MTmZRMפiIЮ\뮦ԩ<Ͼ/ͮՄ- "ϯl;i0ɠ,j՚ Ӎ˙jٞɅ9왩gZͬZ z=(I=ؔݜ)D;aMt]ůپ Y{ڽ-m%KQ*}g۩ nIFMнKژ[ ,M}Z٦\". љɯ1 ֮[R,KJry% Yߏw-M P>n,ֈ 4:ͥ`]LAMGၝ֫ꖺ 58nת`}ݠ<-HMxH x㛌 iC= ͵.Y 9lx6n6<8=:8MNYF8=쟭P'``ObNP(@`hjgln_mn/f!,sesb(PttoɷI &+ gв}mG7mmWw?߲O _Voo_oo/-ѯׯɠ V ' QJ@ tPB >|8)(^$ӧ%>}D$Gb4Y ,iL҉ Ł4!t%+r  aI')JPRYiTSOgdSP\(@,IE֠ĒiGIښ9mER(DщKD8JP7h1NIET6%,exSŠHabB"FS -:D6S,C-PĒ EbHRH/ KxʂIDJ ,nࡑxpK&4Ă!LA+! .?E%ȮHL0+@"JI!QH 6,hR&AѱQ;!(A -H&lc䈂#76@hO# PJ8*@A!" GȢ&8*dU[H 8DѢ4P 4 2= :2 bCld`,:R( D@Z"ebFP6E&Ȋ8VRPUU ->&X 8:inNZL! , Yyh- B$h DS 7528 J"'ڈ8DI4p3O|XLt#y(*R>K9K<%r$T?R(%$NIF:IHFdOQN:!e3>TJRHJF@~r=7CsLHmH"$1ySċS:U,٨-ˢx,d}K>aUbp'b9c{! \FlRLtɉ~>>{|rgx !590o h`)H%..Lł31pB E>5E"bX ,B1TԢ),VQ$8cXE*~Vb)(EC̍я}Hш6w $!hC2n"挨MPcZ;A胙 "E9JRҔDTrete,e9KZRdIF ] ۤ&NI<1ob΂%(̙L3ɻl6IMY3cu@2P(є~jx S'. y!95}S 1Ds$gQM2t\zzӞmg0dTE5iM>gI36S"V.ACz"=u)I ɒ4fTiO_s8UiD՝T>=*HJRr,R)Wy_ڔ&8Wc^T_]Igd*=ztTɚV]^}miX%z³bhA;ϖ5i8Զv@h-L kU2zRU^gGGOpdÙЃޔ&@N0]#(u]Jܵt[ Eo P uX d[NڴFl%M洟?3ӨI#9N=>qtbD+ Ll"& a\"%D\ፔ36D>S;i$WŶShNb N('3L~V9[dZd3YKe94@DQ[8N2a"Q EcqvrK~ VvKH\mcn Q$y&2 nxf3'xC"(\ DrWE"l S3HC P6 Yڀ3nZ E&r9\% h@z a[ V]ESH Űck{*,ҏ!$zՆ4 aQ(]PY>"L{ChR8I (Mncȡ9@RF`C WGdGTRin FEIj&M:8  "'\+B*mcy[tEf P B [Dj%Z+R-\&2Tp My@:yd "$ODՂȱ@#-C{ Պ`e) EnjX^HG!Ei焂f,3"u. 'aO&U,iS87OW@"/'2ً-8 1QHRHCwQ3Mu9'((+!7+3)HF@f5+4K?*2$[(u{2r7wX*0##FD,@H!' )gK'AM5pS`6ŃR+-8(P J "Pu ۜҡ,RRXK *''#S+9ӛ;'XKR$(zϨFuoO|D YѰ% +؜%۟h՞ (,Qp+#4P(>- |  +Dû816PXRYΒ-M@p#@,0M$m>37ل{ӂC@]8 PO+bL<I\Z+6)Z|&JdT5^̍&uO}փ ŷtQi]L㸈'1վJL= -aSٳ]ت*Ȋm.%5ua0cN^5bj%V˰L]b:yq~Sra$Mf֟.=K2n[I\HIȊ}nH瀖MȀg|舎H/!u;HCK@S*ЭzxNb%.<..)貽؀L ^hX0UeTcaUl?i: bj;H$%&֦% :3FHahHhYEYbRVkc<>Ů-MO` i߈P؄َ  Q!MZj+nd%m~%U:@-`0U\!UQ\d,K(x_?F(TvTR>'>v:p:h&Q4xEт40,Oh+pߋfbVb5MXB=f8c4` =ёĉX1[q 6p!E!AWmT.MM@ [A8TIph' 0Y`i pdz[^eLPgWv7=XVS %bn~ƂYuvc,V-,'jjbwbbyjGkM*vSbNX9J#Kt6"rUNUmB\[&+5FC (@uCMtPVI%l L:d(+T/t]ӝe'2()d>Xu&dhvg*VhkϾdFw[&히׺#Mr+d/6fg+gDvXKyF4EyFE/yWtő'EHyWgFpEFW\}(0=;\].a3D 8*z*Aеzzvzz53oŬH Low|v$ԏs} "ZdltΩ6 ڈ,fB/G8l:\Pu/*7R>㱳5tH1X}!h4cF{IS'h xMh8Ø`#\ /tAN8-yȟR* /vt[ Wi_Q(;ܐ(8`$]8tY 9R,h BOH-ȐCOJM"ƌ7rA$.|ذ(M@(&O&g<9-DŠI("0H͉66b(鄨F%XqȊ-R"Hili"jҭk.Jn`fCpDXɲi IIO6hB -4(R)I(6eHˤFm>aq-Fx5Nx^rw/n\mﷆrpL%|⨪!MQTUNuĚ7ߖ2=E*oK/EWt(CޥtL&Ug{4BREz]"tBH &uub/^XM"I(| )$z8DA9$M*)$IIɐ"Yr$"x`}Iazi`[je[-' "#uDj'`ffhȦ4)Zjإ4Z7!_im} "l=4Js..g*mb'$+9MMDE _r`x竒yR mN匷Jz@JcB5X㣋:](i*ܷ7 if,B{ং 0-1F /)/sq(wX0 ׹p"$2"/<.۬9'r".ǡ3.#1mI#v0u«**&XfO?=Q}b`Mv?]vjsy$ť4_";''? ,.$I +iI!7& DWf'L"?7^)'98&M'qR(W$2(V'B?Dq^4A6Xi$dT$v^^PRiWɍgg@#,2KE&ѩl!т IdQHR XDm" 0 gD pW DA d*҈O ("P1h ?+\QBNp*NT Yr,gy\2/a20F-Do5Fʡ m$W/I¹_ '@hD#8>ArK'ZJU Wqfw HBK #m <֭"Ya'Ϳd,|`2-N2)VzTQ7w4V&վ.jQV'>l(8C$HЇRjF+״@Z* peĊxy 6hᠺ#zIʼnK\Qn'(4hCRR8e" (b+%h)Cf44Ԃ{Zi ]F+GUg!Cl)BI<)@r8D)\ra ҐR4BPܰyH$|sl3o6TiVԜ< GmxB }xg<)"nbOvQL" O7w'EJzcp}^o}_O'IH~W_>yX82j}Oc: }ޯLL|ވ:˺ؿ䬶&O.n*DG ߉U__bM' 4`^4$VrGմY'Пl`qr 'H`y  J`` ` 2ڤ LԄY!QcA9YTHtn^a:J [ȇmuYJ{(a4Z{aWWw| (̂IX_P)J|I *awb(vUQ]\a#,a$s̚t)bt 0o"meqЋН,J#NqWՂo! iɟZ)b<Һ8.!:B8|_#&bXi=+n">6<Aډaa#C#l2vbJc721d3"=b#,#C~3XY< W= N6-z4zPf`Abi5chEw!@׫L#oy#*"Bc`\C&$H;,~Q~c=#7bh5F3r63H6"p%J֞Qc;D,Sre`ݡKK-NK6e^F~V? &aBb"O!^OAn@YeH!YW"+woo p◆q'2r6o^sE s 4eh%%bdH( B yyxg xzgzBz<9z}'~gy'ԧx^#]x'{(zB({JhI_&wƢ5)grc#!ƨ H(B(!訏(( [b\B( iꨌBBot1WNe52jC(wAgA ))iAtB**&A##))ʩJJ*^j6*<YeDZ b]&s4*)6jj**HH'&.+6*kt ܪ**~k~+*FmM\b%ze"Nœ+k))+i '*,("A飚#j~,bjl~*gKjgwwiU6W!:5z++!p,!蜪Ar,Al2l-@( p@ ծH&#,l,.-Z})KRfvn"∾z6mBA"ޖ^*J'-r A ,B|mTlC^/ϖnA0/ /A4ZmA뇭/*kk*cm^Mܬ-,x r<02-3mB\P.-x (/ #A @@ZmH*0pl@lp@rqp&w@z,jje:6mNrp֖,i @,-®FmCnA@p-tAL.J:1!- x m&F\֏Dc%K:>l@ ++ GL@p@00At11-@(iBh 7 r" @ ,B*-//Ns0lA=l^`u~%dE"2^%AJ.s^-!44 A4) ).:)( xF5tH32*i/FI71K`PZgfJ&4!!)_u_ )N6b6$Ko8d'6bn!ĉ5_uv^su^]5^upgeR^nƢWKO 5ov_vop5_^v! I\ itGtOtuG\wt!27w7q7"7ywzHC_T85fo$q~X%Kxh%uv6uV'4y_ps7G85f%!_ x?7s &dfn_rhX&I㭄8ϴy(i/9xH(<69Wy8GIL\&"hu%$++B F FUTuTAՏ9y(ĹD FvB z(`B99ӨUCգ`9G[-}Hado@9Z?fiJH%crj~Hwt~i)Tp '3XKw |Yp+;b>Y8NWIml[dl3;BInNT -f9>nv{w{O{;_qzq:ۻ-9& Kϒ0Iz!L^ƋŇaIqNu_}أ}G߳=Ͻ=[_`OQ=Ԕ_d|c&P U'XP~G`~A {BW79OcH{P?{XS`3C?Ⱦ%|% ?C_??p$$[BG׾뿾9 #+??@tN~DR /} AOn(qA4,xOP&/atf͂41e K=A͔(.ugO6:]fԣ0|TT[e0Xa_bS)4+\wŻwnupỀ 7w>&nd%LrT ;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentLabelEditor.gif0000644000076500000240000006553510534177571024711 0ustar paulystaffGIF89a)(7897+,*$Xg|t/4SH`<@q QCf&[lbI# lbm40#>Xc!x=Н8s>qpC=jrv=v;lWsxCY\;ك{knpr&Y7';jr{9'< ;(X["js߁e_2Y$BNK7 @Cfü#c40h?6L>(:qCp<0 W >pހ=Z :mʎ˭+貛.y ;oƋ־pWo鮼+qҎnz쭔0X ILyRK& 0Ғi@C{C;.aoa1a?S;e 4dl h# hC4- ^ƃ4ڡ1w؅gv۰y,ҠvJ+*S:nɳHӋ2*e:7#Mϻ;c?J@Ð6@M}Tgܣ{mnط=7ᗷK8췻:@,fgN,bp~p_aXG-2r(m~<ЁыU@heXo{ctnmX/|[D|\P;,w! p;}F~ Ay5"F/q4-MzDh"kio7!]pL 5=Ѕ# Uw"V{;eF-CVR&-"@uMB?fB$?PY@c011];boKU|@D&=&FҘ6s)Yu($59ej< mGb vr/O 1]/x`#L;24bG 8  ]R,Te/z1 Ɂ>r0 u"5K/ЅkS cHDGʎtB GTD^]3&kZͼλ{ǻw]OxNڿW<O=|o7w5?u{mTýóV^Qw@?yO>>,_g??~?ˏ}~ѿG}?G;b vp/BX Pxx`H؁(!x#(ȁ'h%؂)؂0/8`#"%80ȃ1x5ȃhHȃ/XPHHHV(PHPUȅQSUI((EH6]Xgh`xHhlvnHȇa(\H|HfbmhTȄNh{}c(v聠؅GlHs8/G8yX8Hh؆EtXM،)iwh؉`XǸrH̘x蘍([X_X_h8HfHȅ؇؈xx( هieȍH|pHE؋Pv#P،(鉢8H-(gHِ+dhԨ)9l(X%{ȍ#帐d;9s鏾888o5Ȓ$ T9`QXiYvyWX͈y99i{Y鎃h4xqxozh,?xɎY@) h8 v[ Pꠔ  h89I) 】 x Yy 蚺 @ٔI٠YXP闐،#p P[ȡ  `  j: UP&jPP0z Ɇ( b ( @ P0 6' /J P`Az (ɔiiI6yRX"P0"v  g! p ! 0 =z ذyɋm؈}ٕOY Є   ``  PPP@p"0*  :y muȚӈ9   `    Ьj`2 jvP%"P`˚ (9Mi @v0@찮!{  Zv ۩8*ڬPP- @ؐ UXcH㩈j+ z  б p c jˮ zp[*i   P jЩ`v@ppJ/%{ lj1NY29[h8  v  P ڣ{ ڮ|kv " @pاqliJ K+` IK P0" "@CKG>^n^ nB  ^ ۮ / jp ;  FZ}I^ @ +v @ӭYY|걮 " P @`*/ #:98b RPW"¼p  ! Y}}F]P  . ?,^xŘ갞$$' 6%0P`O& O%` 0 yH ` RЭv `-P@س B k .O]?  i٧lS؈;.iax1DH$1PDI2D!'H£ :TPİM:41J-]DA/~`g=ل%& 2k]q5QX؉ niaMqeNMygϜsa9 %"9vWXyk˓ɔYf7kygyO~-z>{>{|4g~G?}g_}|xY}?w{d:yjGbFd hQ{ <xvo ^'tVݏx:Xx ^`+?ЇGMT΁+A bO .CU惡!4?^!G>t?#/h X^E&! ݃x 6HrQYuE g\"=HH\4$qjHÅ?;,(]O!Xa!ї|/LkQf.Ke,-:T4Y3C&$BiH=Qm~ 1`I9# ]BDh3y3p>3i&4 oN\)! x(s R\%mT=R'KG.i?]@0+>^xPʋWB:c;^F}e;Pom" :͙)ӫi25ZFu4C&"1VD#0I}و_E=wW" # Y!/Xp;eU5۾>kHIֲGEJKƅP&j-Zɱ T9)cuSf;\v.A]2d3jZY֋bӤޅigsv׽BZyxZT8iDF g"Xp@ WVV |p=QRv| i E{Q>('V؅ȑ e!`Jz8\AAQL. "9p!D`hgzdfH,fV8^(gھqhR(ZX**߆jFf"r1vvyBK.s``P,`aK0\%ArVI[͉NdXB`,1z2/:_o.gW[>)=Dp {,:ꑡH:.=@ b61bynS6 ~){i>}կgFcs bД]_;v2;B,dWN^vQX WzrWDat I  MVPxLLۅdkz]QRɆkbf?06~3ׁXs_ ;q>:3\. ¥BaRh.8av p/:8XZ یzɘ~Rw$E79ܷ+1?0ί3pXBvx S-P*<BE]9.!A>fX[y:Oyu2f$]*,cq4*1x_k!J}`8>_2.@ ̑s [ l۰s8"i[ܬj0 |AMAA D! D$T%dB"A#lڅQ.bbs"v % &;逃):C>JQ1ɑXsK2d@41\)v~PSC䇹٩FDCGLEDGDJHJDN9Cą^^"vhi;c|P*8Bܩ $MnDĺ\ɉi`mb cbś1VbacֽdEVcVKjwzSч2%]8u\Vd]ֻto؃g]ցeV훆=XXքV}X؅}8ҨsB."!!Lh[e[GC6-L]eÿ`/"/0YҦyC5 Yj4mJKX9 (2};d(XؿRqB8d{}ELa O.TzY )k:+ʨZP ~)WP0RQwŔb <=%,]<:Q ڦkZݞ,|˴l8<~`wbW'y!t~=ѴT("e 0K-R-z[7UuL] \]Y8\[Ȁ[ 8*Ѕ P҇[ m^>$&[Ӧz+&-Kϋo겺[ݣͰUJ{ ývP ][ruPRlPuh^0RH\-T+5Q#:u}.#HN99 AsC59F&3[94s s498hsc5ׄ{ȐMqЭ[WO;~zcBXFP0/ .y. w+2\81C +Wn侗Mt>Rn'St5>%@vr'$ s 7Pt3]w=M +ՊZ(@P(D(#rсupI1HXU#eƸpM Bg԰תLpfOY ir \2tSD' Iz"`+2L (`(l*Zq2et&F-d:O"F=B@+x IEPNLFÿ^VIQN`A<\0`ō1C625C<>?`B_m^ a! .y@@ȁ!"6z?LۈID}D}? -ɖ.0.@au pyzaTAN`3]<ă.V>>0v 6h)P) ! " !7*a6LlH<D09c;jD CZbSΰhd@AAAڢB6CZC3XDB/.fc"M؃2q@zA6A>BC6CJ/?ԤWr1a2RG>H A!9p=lJr$#Ҹ]dA^I CV(nM>2(@SAT.TVeD^eVCWaFe2RX HVB%<%\J@\A<menͣfaaf̈́.DnDcA@'TFeeFebff&{CWz%~60#%&!BiBAz"L`@l'}N?Yd'=wr<8|%7&!gyiz6IBk'}FK&h\Ai i.C "UXAvA6|S24C.5D$5\e<>)==<Ѓ8h7'7g&<D@>lG/b^jnjrꦂꥺU<O6=UiMrĄzAi鮺2:$5;Ѓ)=7^x$hyj!>FN|}FgU>\OFi0*Fi m#M(檮~ꯚ2,k+΃N7P+7|%*%l$tg n+˶ [_}̓%`бL.ԛPpqx j.Ӳz5x7H,Fk8d8H7t-%p HB ·Az‘l˲L{^ܛ[2aHȪ0eY^a@\A:.m0A0X_[=p'B/j6Z0cCAA 00 7XS%$0p$mo0Nߺaܬ[cA F;̸)DBA TpX&cl(r0|p*0P*{C7x8%%\B /$ <pUGĂk8\`2OlK$k<d&w2`A39(qs))0س0t>r7?p/%\$԰ /& >2-[B@$tA#4Bo",0H4UWJ=pE%NX `*㔴IkMѺ\s LMϴt_"" =+%s7c[%@%T4#DA0?5T$TKt1[5U5;jjCag082]ZM8\@]ǀMu_M`6a! >_b7c2RSv$`$DS3B"7ggoBhh?E%$W^D<>`BFmqƷD t w uc$x/7376wt0P7d?%TdW#԰"0",x7g?48z&nn֒]=u4awo4`= /9@ @C9GySxast1vx8O$tv_/>w~Bkg9$?Ol3Y;4X6}GG%8#'S9CG%B7[!t9Wvz$@B G"8y'0 @p@:zw.orb#i,Nz8%'u@ ,9 t $ˀWMr"Lxa{:"Dw!8$T$vG#;#8B$ez@:n;ī;L|_<ۮƷo;աl#~%4©#8#DBK>WlU;l^53ZFY6lEU <޿>? ?@`1 " IhC :PDH!=#G{,d#-+Z(g s9|`a୯}K=tKgTxK )=%(JעR:Ꮔ\uaA _(t P ІN¤4%LsJeC)t"ְ<X*bXF#Qj^¢ Ή\ . (C7 ,dSzXH-|(F P)C%!4SHjqcġ!"Hp}cI1DqD8&Qr=LT؛k [C{0d#4gYRh2#ujI9A] N5T22XA=Zyl^aqMbU'e?) YOd%m)O^mڔ@{yh9%u`Grсq+M4!.8r4gdGJF2*H-߯^rpAJ!R 80` ,EbrP@6_66UI#RYT % †ձ N5Pf6 dc`<̑FbOVPu8JR*s,[Ҡ~nSZ+$ԟ>j$R;0ϿH IB$tWhE z@$L&M&ΝB@Dū XծN=jYi^ך֮[la{ټ` 0:Q.]Vq vAms>4u{n7CsW~G#y;w= D|SLQ UM}:f[=:H@M#}EAjN'?x=t3:ڱV>r)+cqXuTnS R찋#M5ԭul]5dէ~m'6/0Xa*BoI$][ܩ*&x.ގW'}pl|.桷??}ꪑ;o^C@+>]JfR>\jȘ]VʂX5g_U@5 6 ̶gFB,gL|))v&ruv̡`<6\@ᳱtJAU)UrA6mr%?-icJDPWBpceɚ`=>T@!ZH92W[v0=WhM3;Ki~(ъtk zShs@zs&3ts@*bD)eB}XAD.Wt)}qC)pc.w~~g}\Pw6yԗٗ~wARoCs3r|00ڗL)S2>xH[T=<3k3LZ6.ls1x,?d)p2t.8u2g.+GslZLj7O(8.[gs,j.U[7?I[tp>!ٸ8xpxJZ|[ɘ6?Us)  q -%"a (yhUT%qN #cO/ ! 2 k A/s.[/rtyA- iy9]s9A&K)a |YnF U9QhGńcanVat$z&|p_k(xBį?:B|""O\.@ E0n XD3Y(%c@(&ԁaax!4 ԡ^F bpjBځaj*@*ڡoɹF'8Qytt!v2@ca"LNql͉kL졝Ә宧&bB2`Bt2@< <!8:^Y۵j8'ԡ cADDsn^A9 5`^3`AFD`ey+{ (";8ΖǢ):xγ(qnZvb@ځZb>}{! ژ CX` UAXXAs oa-[y c 8wZ ':PB8@&P<@!zc\y!!3uB0? +4!'v|x4[VL* sJu:N:ql3'5A!Z!p!Jݼ.c!Z]mbP}`tA|}ځb:AoZ`a'943=˝1}trdY1rɮo޿O KP [y1i=,'%rP0~27)^q,n~s@i25K Nٖ!;  Q5rc_7$y,Y~(\Li9E >%޷ϙp6aL勥T΀iӢe涞Oi1Y_Y}|SR~)P1->?>!>)S[yc(ztzall P"򁚻)X H]Ίm}㯮.6]>.O@H.CMY5h{^anaAX. a9OYb?~,8rꕫgo0čg~$K<2ʕ,[| 3&}&r`gdz&ʻ=LU?ujǯ کSG-Xԍa|QAKvaJmX lWݽvcG j>uBnrg0hd)G!ĉ Y@U{>)3լ['Ƀ(o_EKAvx CD`EUX0^uMb{a]]\.]{^!MP+h.X`VAUFB 1tNXtQh哓=؈P!jHb&CSCQmCcItر ZxK At sx.b,L'&ad>B?^S+CQ>&s )A TOxgXdaN> J@چ> &ěQ" /[1KXdbOOF\ Ѕ ,$R/#p`OAB&s|BB iH UHib=]8O:>Y>ӎ&&.0JU,!` /$|p>$K2+{8┙b-y+l,4՟EgO "rh!i8oVǴOl+O4I#MG@d/hbgf]t%$f ttmѢ)uu㎧TQC7qSO<`?>(wnnts[&YCXڀ>wߝ;f:aԊNMSmv|^6Iįyͷ-߷ۂ3Qi [l,u+wBvקeC4U~mBqiAyՋ(-m쁏n`up -8̀"#АD$r^| 5%' l֯_D[֎!"BG+b;49Ą_3adr G5նִe<F  8~ w-q[M.r ]zE!%'9ozݔvN eV<:'B޴ջ;fr K"o}.0_o v@=;aX< 'rc$1;J+Nq.i,c8b{<Y&v+5Y1vl!XNT1 d, >66ማ_f5qFkl[D9WcX=Wnd#K6L3ah9'ݜEYl#hD"GkAMY9C ;u5c֏."JVzלduiFzϧ, z֦>i;oDѾs==έF+@$t h3{ض|ftF7 k٠&E^|]C;Г4hyk]F}Ygk5nA}nDcȾ'iE |έ.86nkAZ?vg^ėhsj]2mQ]{~v۝j3Os54w{H#[#G:qW}K3h{۝v=r3>wg4a)w^~? ?l_JY{}C <}k?7YuÝϨOϘ?g V{( 8p(h 7r i5p 7wȀ)X+()8 ?qig~ @8AXE(IHȄKxwBH @UIFxwLVhH8DxX(]XH8ikAuL6q'Y7kTC^ȆW{feXMXEt}4(8xw^pgȉ^zX xlFinm@艹P4[!ʈ2(Cx@w xww c`̨xxըwhXvjY'oa͸7Ȍh@ ȠPǍ jBbXPǁ 0@ 0}|ӧ X; p@wG @ ژ( yȐ 9|7ڸw|,nYՆvu`YcpS wi; : c0P X P /D`q hTh @ pPTY P I BwGx @0@ 0T @ ~ @ `9wpP `y imi5qӖhh֔ϧ70y90@Hy }Gxi@c Tp+;jv:8*y  9Շ3x1i ~䩗 0cI|8{{aY$阎R)h !trWL xi;iy ey p P")Ri GGJs IjT# xtP0 tӰIz ٨@t*%m!sI)~Y ;igdlbm ؕ@ gw PgyPNie00eP*c .8<%j *x)iy! g8} 1**eP;Y*8`8I?٠0$ )ې .G8jv;V8螽w Ϛ+ W g w)^ c7Ug8-@~ ; cy-{C| ˹ ~ _{΀{ W^KW 樞੮M qxjftO&YY c0􇛉{ ˹N{F@j[ʷ~W}Ӡp W [ ;{[{ח+; ۻ{GR{ڇ˻ۻ׺{xe}niqҷ ; ׀kŋ[{{ {}djg뻱; LJ L |0< \L l }puwp l  1<#8K{: zkDgGIKP L|,G"Y a< gjrAnfV66@b%,ՀJ\{G΀$kꠕ l xw{ȁ (4j .țY9|l׿{Lk& h ؓ'tK(Q @ ~ɬ̼͜P ˬr <_7tGgQ׬̽@~ܮA86sczf5z }^t944giѧ&~unMэ(7ienf¦u/˶6l>WAUo9kuUXo.'R/moipԝVnItwgrhnVen\ Q}rJt˔JijԼFm2Gd[7ϯuhEV;3Ms>6Oe8oP mw7rpw{v]8 < l%HˆsοfLnhMtjcn1rVp&֟4auvos5ȆgmhyV7ą8-6]&"7ZPAmـnsFviOoUv%M' ]Vޭk]Wveo$rWVVw!^(=Nn^dWNT)>~!>1↣@4>mp(n4~!(<@G>\;N"j4!Y[Z]\a>angiejnms>pNwNpx.Nn臎艎 BB~.q^꘎^꬞n뵞.^^#~>^n;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentLabelEditorChanged.gif0000644000076500000240000006617310534177571026162 0ustar paulystaffGIF89a) /(*(897/2.%o~CC$T1p8N$~:G9b̓:h ]*餖]dINyݚF$Lutd50>ڎ@k74"p@1á36:¼v( 1!v*jK`/`Š%Ҡ 'p1 bp {$ImPm8Tnvmv n٦neakY8,Vb2Kmˎ,-l40Jn2$(>ʪ0 !jHU a:dNˮ^ ,3D|C j, ⚤[|,Q'ݴkN;Е4Ĭx1ﬠmD?4JSLҨfUWWK=53p8Đ}$ >9JK~:Kh"p:)ӎ>Nh4f8=0ʠ:b?#Ksߠ|6/H{=Ѓ 4rj#K 83ij:پk1ј `~y0db gxp?zѳ`w0x>8~0gb9k@!d*C^1f"i DULd*I K!fy%XHB)2zע]{8F ԇ4 h#2<Aud3DLU>|C4HCDE1Q ʼn_|ъ)ȼ(E+=(o ɂ5 DŽ(;u SĈf#&0rD+% VI΅b "qBVEXv x ؃|hF;LSC>d,h6p |"T^B+E;z!>@L)L,m"*&Ӗ JI5 z:}|h0:XY|gգ~V+D-ڗĂf3:IZ*u [גd(}"E ԷX$|B2@#cF( }wthCm7uTu655+[X(]n6+,84CJ W)R](ePUy G67zkTtF5X͋P'%jaҐz?GQTHLX>{YY DXH I:?"vx0!čh ~T戃0W?8Chg h0Y$2te9Bmh >^ =f3|]cYfY6>uWCiWP4|v4F^Dzd]}xAmqw5܂ ][d~[xt\oY˻׷}gzyp$ρxe-#\xAoAn㍆  `5;6mlB$Ȇnmm#eiWtPt W=5Vz֩{`W42up:.H64.t'}逷ҕ EAxLzx#?yS>򔗼+d^@?l<;ԫ~]yȳ1y'=z򼗽M?ϻ|r]^O|g@>8|G/_O~h~ǀ~7~ (%H(w!"X+h8؂#ȃ-#x'=H6gFx6(M6hIYȃP@pSH*HB2xOȁ5O巆؆78:^JȂ}xn(X؃ux3ghw(LX.>q`8x(X~h ȇ?8|؅>؈.H\mh0}xY"8~cHam:[x[؉x˜ȋ踂Ęr$|0XK̸8W؁JX툌؋IWxh娏 )؂h#(7h YXjȊbxɨz8o7 hhؐ3 hH6HL h- 71 Hh iَ^(=~8^hЄH8Y9yXȅ8ykDy8 @8  5  8 0M А~t8dyh 0n(9y 젇p 0  Yi"XI~ [0P~ @ yx9 xpxYP xٝi 쐁i∑8iy  Y`P)[ )I ޙ~ )IIlȚy>9{9P0Pu``y P`pP `u Fzp PȏH1-ɓq`  P0  ` @Х`PEX0`ڦ  ؈Y'i4hUh P`*P @@P P `) pPPJ&jn@$9z( ! @@Mz` )u * zP `p eYyI;y+ P 0PP Y*֚ )X@` `V@Jƙ鋫ȂEy㸑,h  ـ ʴ0P kP 䊗@Yp` ٠!V;xvZh فy$  Ѫ !z 0 k P~u~P@ >۳) E깍葹Ɇ姦PT[ S Uذ )Oڦp$z E+@jJp: 0}-k|HhЌYH!,, Нb0xiD@-2L並 0؜( 7X؁̉$”i6 bK.2@ИG9L0: (y}( Ȁ KȂ|ȈȊȁȎڇȋ-[/=g.K9ؕ.8,0E;˴\˺˼P˿l\̶@<Ȝ,nkiL\Ԍּ|ͅ<,z`LLʻ(7 8n(rzL̅_M`H |ٷzݷQhՋ ɌXxZx̖yҞk/HF}B- A]jY YPF]V-@F-RMҩȄp<2K =IJ, BS()ȏ2`Xɣ M\}\m؇m\ؗi| ,]v:~؄< Ԩ+T{N ׬ RMч}؁z꒧}>i퓡HYuHHǭؕ LLȲK~ ڹH-С鍗أݑvlMُ3h0MWݷz>@ n~) ޭzz.$->| ߥ={3ـ{;8>Cp8nCA>; ?^:W^DONZ( ^~阞难 b>*ڙ k@p PԺ`  Ű 5 1HxiP p.\;   `iښ.=5 P~">~@n"0@@> oM\  @P@ ` z®`p?ҘذIop  @֞ ҄#m]1-}N^3@ $@@0>[?@##0 ?uO f牃=h*0 k ΋u zi\pnP @P u3O`` G}ٜ WOl@#jNh $Ơޠ"fv/ _z?vnp5z PP  >oNW}Ů `ձAWLe5={+W^ ȕ)C&r z!9vqpK+yg/(GD -Qw4(|,X.RlÕ@X"@;3F;fFP;_~Ep/>䳌)SlY3^nV>wޫ\!UjLo>ʲ?ҫ|R1"3 VVC6S0D8 W1B!64tEq2ݢdz ɧ%OTP뙗S= *HuVǯJZk(/3D}=`[.򵱊'9dpEf܈(M Ȩg]vyeXYf,W^pwTr蟊ZR&rBj裧&jRnVX?X!m߆;n离n;oie{ H~'pGE8 ;ׯp)a^qEe|T|e@u~`yu?MGJ!נW9Y3{ګ,UHܣ-U=Ån i0]ꆡ~C+` ڵÀ]>6:tCw(@%!*)E*b>5DʉD׽O!1F*H%tUQf}`F`яvSGL\b ,0lА<_PBӣ!1W4xuq#Z$$jґJ&.ыU<7{#@` rӽ -ap,2;d2ǻrh$%(E24"QFy,.Ok\IIbb)ƭ ,oa.!숍\؅ s|x.:|2%}l4J/H.d&IM.NS)d(CQRҔՌɕxfpp-P0LXb XTe>\1BNԪ=ـfE9'yr+h5)ɑۼWHdU&=+WՇ܈frd#+[GJ@gJҶRҚ *#03~}Emjy2-'@ꐁWꬔ96Nqkmkd9zY^6.5ZPQ`t{0Wޏj?r/e[-;l-&tDQxuQ ^KfK*i&K1+*u.Lio7YVh*C'H[vڿֶ+T\T@%h+A^.,0 k^"cd-(&/L0S#?<|:;ڲoGNձ>_v:ax>l&Qv%+t7-6`ܢ 8,`d# ẖERxp$:Q\`(h3KhN]өL_=XCcc1UBBY:2X}ˎW1KgI-|Whhq rL؈5WRΖӭU%|^-L:Cu5ȏbE;> <$2.>fg'=IX]IEa&)A0 `Iz3~A`CߞX{)=nAyEOM7| ß븣+vvlk1L$h#zkϺTIO$vlѥm7@N ӽۅ\Xl+^(OtQ G~o 7χ~?}⿢`Zx/ 7s'~^`|`?o8?ΡZ3:0DsmzwН|`AawYˊр2:*?iDzᇭ؇ Aه@aA$ Ad%(D$D&,'8(B%T~hI%*0XA֩d#u;}x(cZ!96ؚ P{ '܇Iv`C&$uJJ?C] ^lt(VĈ qqFFDl"οPLNK$.I&d-:{Q5 Psˁڀ;[v =sˈ+E:g ;up:٩v)]29˸3^#,NK)||: 5 нKG<|v[#Ғ'\7ԏ[ԏ|y3OahCQl B IJmPF -RQLDtA&$R&% !)@*$W4]FA+LD)S{X+ӹ,:=AEBS1T=uGF+ l(35TQlљTO ը՗ᝃ9UQ]AU(՜DURŒTOWD1 1# Qe^A^QT@u^UYnTA?a}qm[MpUVSMWor(mPUy]}R5WxecJXbiqK3(S<# @9TLp25H:%& &5 Ҭͷ( RNw pd-98LPJ*ʽ2aXEԹYTzȺ7}V#٥uZ7i٠3;!8##ͩlg|},Z,SJZqY)*j|,&uG# G5w)DR|aCW;a;sX;ZX(.iZ58+P)\,Rr-,)R$a8['^&WX)>ϯ4aaݧ#E.]yڦC00J1CԇC=uFQ[ ܅Fl_\-]:T2(`*ܭ#z0E_²դ]sxY.Mh . {%dz[%ݥx(Cc4=F@X9%a_"+v,-rZhr:yAM\([@ַ+-Ӥ[_  4&J52=:J; Hi ZД"M֙}gR( fd0B z-:/cAVY ,~"+|pX4:;Jhv*# aj~P`;\ n& C&bt,})Kt@@CGDWDKQ؇Ll]lL( 0qdPPNu`lWO].߯%4iw 8%0c7#Pve/fwgG"@iv$kkMmm'pSS0sGws/RugwU\!|yyGF 8y0/ãu<3O0 %0vd_"`gvi))xHNnvpwMwqOtwv/S\H f5,!^hhՑ  @124]x#b &xdOe?jxkWy'v/?RP_QL { fɇՙTL(21)^_zv 'AP%?zwvk{wMHR@yR w7W{vggwSL`)s(}gvʇ7kQ`(|uϞNDĿ;" p%ɧxjO*@*@*pl M(T}}R@W}_g{S'M'/C \e!C`0BŒe^rc2_> `rW{pa'РB-j(ҠNeKPZ dYp=Ha"A *jN"% Mrn.^RP˷oSTĢe21dTOI}&P@]|r@v,qi8ҞK{6aʄilZު8ʗg^5kիZ1+#H$) *TVTNxJJ/߿ 6',SHA XAAA J;#xP@Aqd>Io Ԏ,a==cB0T>QC< iWygUzU_|l|U^iW` _bOcR0 JƚNBK1"TpzK#AL*V /øڊzrj 'I차)r)'ςBLmmvfΰ|?Or4Hk ۽i#x 0Y#%$JhpN$A,1YjEVl¥{w̱bfŪYr~b -? >93O=#$AR>~Ա9cFJmoOZzP5T3u n5[UϥbI'w  7)-q>8( 8-~/~ixe4oT":Y:sSQrq&@ V<7c^:US*'=P:Q %V4+\ a'dHU9 "TЗV!L`-l*?{Lp(@ NHV7h\[1Zpyà*68 . ?0PfQ@hH>H|c +s/7R^DՃhM>Qa_ 67 N^9:sCf\UK]BH(>g|8&(9Q P0!0  6MmcTG9r#kgB-<$ l`)MsJӚT=NoQMy2GN?hJPR o**P/F(EqQZb9T]4"DiR>KF8j>UQS#F Q/t 'J/x\1ސZFԋ=E!thtlu+7FQ—v f(W=r ,r[r9 LM%]@):qu d l^opWֶm:QU(|+&. 7mЃWbg+^x;G.0> XhH#(,$K>;!A%=zP9.z;U_e|QdMY׀2&ɲFQʣr(e!_V&Ȭk>! Ӄ1LZF}"+=7;TcO_,"R ex&=7ғ/ e̻E_xcԤ--G1eݲկ~9Dtk ?A Ts> G4vT7]|/G'6UHe)>w =tݕ~!tz3>2@o~N6nWFxe`v N!D<5c჆|T¼H&\+& mȋ^׎Tg攵gQŽT=9/C#?9L^tytҗoto6hun5V b/=Оv \u #>t~f۱?<6^6( " -&,HaMA`@fflLdDDĄ & bt<^A$] !334ZC3\)BNm!yaniEB b(Ac!JALAC6`>L2&2:G3.N`N3F; <%"2%Te@9##c:;"x':5#>lva:pCr,, .. "DR` >? 5֦qcd_DE_cK:c3c3MZCO6e( dB.SA<8bCO JSn܃9<IMRKȎJcK#LA&#M^ahwgxn Yo"jJe1bԘ'O҃p?8l8$@F#$s:tN8$vfgC&t&hn&(v.Y=X|dqN{ʓIF>il'9#(izb#5PC_>C2>~7Dr*g68dht.dđt(b..)ީ)ꞎC5f hH#`JhV''Ё$i((5,CZCR;^)==t)@FrV虞ijjɛ)v.vO PQUJ6&2"nLj`*A22+;kC~i~k>Bb(ځR2tjgCFC\#  "R; "ND;DYLlA~j*jF77뻆C7ljڪ#Љ2@PFbܱ!FM:T,;˨(!VMvaQ|!EVthlAkù5˾7xCzڬ~ꬽl$@#AJ kB>s%a`>l/ ,`([ -in$ʓ&#bdfm~j-0"Tn *Af,o>-An?\VgCi -j;]J!V^lsptTGSnohKVpo E e60<$/OWp1m?/BJ 2!/V ,xF?ةB,@.LhXP,kmLPoI'kHpl)c)gA*oAA@tB/(/q-w-wC7D/10S$C1.H#Ҳ,I7=4C?䩄-@I26DlӴz<%aW<mX=D@>3s? qA$Vϲ,CKDS4/S%Yq% 0#|Go! `vA<:kKX#QNNҵfV?C>o@|VAUkV/v!BC$$L$YFs4\]o^wJG@= 3> #2F#84cbVkdN[~WKJ-g>@? duVW5{6/|u0̶$u~u%Z3 3[#!v!wp۵ @J3xq7r}%a{eVN8ӜcMvs25bf 7 gx jvkK!ܷ$x~7n$C!,H#(O9W'bKyx8*{7 |::Ï٧#~;ýϽ"~$0c;@>tࣹ? 9. .F;O 臟I$m[뤸峀 | ?À ?{ُ>"4"(>Ǿ>@ 0@.?X<7+r{,u`mHd}+W^˖`Z=uy SI?:hQG2{6kʓW-` &N{E)RP # AA+w GwuԨE."Ê %N|1cH2(XpǰvM=,~4m櫹K=\=>xqkJԔbeuױ%N`^Yi۶u+AuW0E6!H0Hɏ-G2Ƃ/p DhgMW$' e6@rzj5 TPY*v6G*FQ̂!̴Mqr%{rQߣ1+!'$k @H!<Mu,Gie Qg%;6uIiiM@t*T+tT\S4NlWTRJT{wY4W05XrG``չ ~r@\ciSf[z㏃cѦ60 }rTȵ8-o]v xMM>{w_~brE3u'^vѬ](g[c ޤL[DلGOkG}\ gFw{qIƕL^_L\$LgrJl-fO#;ՉqPJH!A ?*^R@Ё'BMь>P)q Bfh6L.=ht+ݶZRQetTQM+:mik!7ڤ+] С9d9X1e"(OіnXu#9Z̡֧|VRTFՒi2#'c2tafTZGSB#P-c[9{s ^ہL؃&dxE=Q"@Pd%kSyRWNoyX|"#Sqh7o"N{þL^ @=0vopQ¥'_{Q+D}yM-ߖx"3h*1FXȆ ÿ|[:8 EҶΜ->9:]@v6%*>$Xy"f/YRFF,E[yX@G0qlŀ]Qv@tJYfY ڝ۽wܦ=JHJL1\B*0TF q ;ʡ d*n2Jq*%wmhsɊ?-/'4ۚBzK;rwjK):J.d xT#d1a^JC9&W~2ln5qNl}s|tfpЋtBG9ax Q).(]eu^{l.Q `hg;~ݞwgtX={K"lC1J4 %,?z,kzCM t:?=En>!G=n{ ?(߶bO/eqU2 o"~ϸ ~'~jHl@&M ZB*AZ/!B\؁:$jO/sgPS{/R-94mT)Rt2i2#^\aa\L03OԯLO/owoV0 ہ ꠉrtԋ02`F-J(B$ht>ځ\O O 9/ڐo`SO7)͚iMND&bA!a w! 'baR ICQ O0 ϐl `ᱎ)*rч"0l.~,3l^>ZD&=Q2qL13 LbrP*ڑt#@fdC C!;90/0"U #!Op'/q9H& Z-$c)%ٰp$ #R% k!Y2,ːPAbeL)99*Fh Og0[hNqp/%2'va22! )xKcXLGdlV5Bd5[d5k"l6χ66k6݇5[D7W7{7q`S}g90rRHl4PE 9ϧE 耘s8g=s8s}Zn}=}S~s~}}VȀ0R7;spz) "vd+A2BˉNl7DA*KmΪl6ݼ믬KvjpF]kJ&PEŮ^ (6L|,jdO)xHԏԆ װԯJ{I8 N״Ftd͵D%kdRLUeD֍F3jJQ$A5KmΔ2~F;LOFn,&uJG.̗b4`J&(@6dTC5E2K9DbF 뙠VlGVuIIA.ךuOOkb;9&Xr`$;iDGJurUtKyfX XD"$yJF+̡`|ttTMvJ V[5R3: @)85_t'_Û, B*ttatnơA>J]+Ȩ]GY%֦ސm_IB ^0a^006*4 !ph$R/6FmKB㾊L@DD6Plml9tA؁|plA0 m6,@s3VBKTI:63WDOnD2PK<@20!6|^>7y=ws!j T^uޜ쫬TkȊH67~vE^)t+!^4> ܵȠ]-TÆRHC2fc:F|Qc*RBa%EIK<TXuJ)^ OɺwO[c) /jJRa:/.`-.,nth p jCe{)kl8N tEicQhXTOxBF(8zFt[Ok| } -+zcVzUϼ8% %.Au%*iK3q@ˆ*7I=A$,}`c?Z]6\مnY|9ps=u9u=SӀ(>DHjQf)QH2y%%S(yɹY@0|9ϔ9h1O%'21'h0;q" 'K3 !5 )FL٥ޙƹZ'P's=5XHkP*PW9,::@<  H //facv S5#tfPs2KդF;a:dWj,kv᭕ nA*ph$A&!lAla,a/))>KMh$L[[@˺(v*-6hHb&,`A#!$$ r$H`!ݡ$@`!|e`rw$1,@6 a2b2 RB@u "$  @jBi2`B8\a̻al1!3   <5j00]:{](q N ejgBF9į|6.)+6Lbg\ ELv @&de `!DB`\!$^B ԟtHK#q͓"l&OFZ`"h\2` `!!.N,E#r6 BZvӐa Ghr!|a" #02C ```O:DiP(NXYojaqH)zwr^a~c  )1`;ݡM"aa^`a!\A)"& ߗfza!">·I L/&FUyCZ"UP,rU0~ P9,R[R?lp6"quqiځ10=2%#v%͚z:' R1ѐOZ ;sپA:=1.R)8Zņ(:ߟQKHAM}'g4W9%أ?m H.̨ iǏ-LdAO` AأNb6.#.x0.3<ZJ2gE[&3Wp%Eh)bavA>/Kl@:KH eC@x  ~mKP Cl A>Aُ=2l XB>T`@md`+}VcEۣsPףdc_ދ_5jJ-|:`BPxGbPAO>O` 6,,t &sH @ 7|D b9`<늹Fn}DJoZTP)XG0e׏˧%.dJ . bdAS@ziwȶ  ^xj+HX,[o\'ImֶNvQ"Ftϫ+y+`cA٢K2#eΦb8ι±:`\Kp;[+dXmX}KcJL$r]04zvTc"e1+R=Js.$5hjJ(0D˃&"#j$v). FDLT׶"Q2CxRY"XE,"î`gqZx)FCX .&X8.Q\8Ry]P!Wb4-)66"nbCC4a$ eP҅#aavQ JiÔtu4%#R?1%i œzb&k(Dn>&sl3H?>s;#a̫E Nt lvB+A')w'="|“D=NB,@)ϕ'(4 #*Gnpj&NPbEaGo"e%TE_jx4QPDӜt.Ke>6uI#uӖ.hCшF .(W:Rdm YQ:֯^7gmZ WBOs'။RU&%tH__6&Ias"Xv&Gct]9ژbd6@b,hE 6plhWYӊjiv<\ S'';׉w%n@IA< fW=rN*wkg{ڭbvIQlZ7lcyeU҆;f/([^fbg<a37ߥp {.a gU~vŨ}T=+[Ӣ`;qkbX3oxB)4ujsɮfHPQ3&gYSvrZ! !A)悔97\jXL3y!3Wcgy̧!f%f#D+z^hHё4)mJGzәF92X4ha&1Sզ֩S'jvlހk l@ؿv^yl` 浱l`m@kh[ٖ=h7[ۖv 5#a;x#]o|ۻw]r=pyHx}٩>"Sm ד}qH= HrL8Q~啠#'2]ne4^ϑsFcCβ>tUf719Nt4:%z+ĸ׉u{P:Q}G]#x87w_yOY|Ir#ZuS'vqOګ *wߍ<$]^j_{Yozo=5Ko;+w=}={}|Qϝ;F:⣏O~u.p%oOn7g;ǎ{@c'~!}e~"|{uK+# 鹧<ʰ& j ;;`DCdT y , i]0&j)V饭:Q ׉аX_z) Xڛ*e=Uw{ov^IIJ< P"(L 3@jenZj̈ Ӡp,q olt|Di< n` r|ǂlnDžl)i}lNjȕo\ǘ [=LŠ =t6ޭ59p=?z8;kOyÓ_Ͼ{ߟ\v{\{s=t'`nuQ܁9G\s6V p5\0żaWn:>$L1*0됁Á6 !XÍpPvFÁO;$9XI%s=d~Uj'SN)eVc7^Rɦ_Vfp`hA9栆Rۏ=`^7)8Ð=ȰHg #;,Ӣ>S:jp(L3+:>~ 2k <|:(}Z㵃j:|{-?- 'ߎnncouۯYY:/F.](<,:0er,ɧ(8p/ 1:78̰ڎ>N16cɖ 'OK2[R b{~2N/fx5Fp`L1#1Ϝ/p17_Ҁ64<.j5q;2x3l5 J4VWK-PYka;>_ă 2-c߆H3C 6 dw"C7 F<0\{7x .VxunJ7p`IV$YHh1U1x;}HDSыѳUjydGՅku;Af,萅dLEC0}Oo WDz9(Db!p#Y xdh?P<9 9A? ,!(ЎmH@7ln UwNX iLҐ4"nhA aHG1bSU+()-j6A&>/&{#-Gޱ~;d>Z7;.yMp}IR7(F;fJR(?x!<B 0u"`AX1aeOНVOti`uVJ;VxPYs6GFbhh ٵB'5$]Ke3 ԠFu}uf<.l-k^216ch,}b,e1,uHAKgP~yA3R\)<@ø#氋]\T5Dzծ';+1VKY5PYj(CXb+Xx6˝μ_V ?7=fGQ:I= \:p.C5 R *5)R$*iŐ4:F 갟jNN;E,2XLҢZCvj:?`cJoY_l%J Ԧ?ZH)FӦ̣"+7`lp^elѭ+NÜ/8ch7c4AnS8f sCg{h9ЈzӞ1|;&x@`C$a dpn u`?~7tSg`J 7ufi=iN} ; B!4~l? '8wvNӃټVҼvƛSXX PPՉ]Įݲg¼y{C@B~ | &!5iM2CoM:7@w4Kua(,辝խfu@ [R\Bɖ y)i;LrVa, KH9`PV :PLmiX2`YKPŞɟ>,ʠ\ʦLʦʪʬʮl>H \ؠNiTh'j˽\:Z)˓ٟ)Oh^8yjK\|ؼټԬj՜̥,]ء< _JL(ܢ<-LyYȏ Plm8 MoSۆw̄XPMJ,\)RlFل= Ewx`IKT(]V2}\:l (]Pb}W͠jdƦژ^ U:=9%)kh3L<ًYHqҍ͈)}dҔM4Ҏ Z*H͗̈ z9l:9ڎC?}"Mя9fؼ (ّsمۨ m6ڻJA"(h Aߺ݀] ](  ܛ} ̾丌]5ƨpYaj(bL W-.h@ޏLf/ 4-ˢm- ~YQ;zHJjI>LQ^V~XT `-BSh͟M.` `ӖY ?h,|" (cX x 荾^陮n頮 ]n_Ά̮ Pؠ p ջp މ 9nɹp^ j.y>?쵷Т:%ny;ވʍH6#[p > N~.NA@ ?H@>h?J0r @|0 |  n @,`P 6{ ` @^` W[ɉdIw A _ @$ A4z/ b`-< ` ?* ``찫 ? B ./L `>@y6}O[x ?!o$@x/$@4d04@@ w?ٿ|~b زõq]ueI ..Wmn-lYwAW]5Ν={꽬g\smtqrj€ ջp ë,啻RjTTJj/*UY曪꽭,@!߻m { u]F!hW (@Ppa ]@__$ o軣?WFZti֡w;X5^K>lU9eWrm3b  Ռ60/HD=1NrH!qQY 6f+IͩRM+[}lV"L&ҿ) *`d0A|{3LQ#0mTͫ2@ZvۿJqOMKp`j6abVW_WgրMU//ֱYc4+ AfN]`RG}QMΰݓ8MB HQC-&!g}z4gh/j,2뫵˱kz)Ir)bnb{vE. \E !323GPxTT6)T?;HE  zw@>!"/r;vk,='" A(e/R ^ر+v`=*ZL-:Z.\ahFgA>~@e"9|EljtMLt Hmmǒ$^LwTb+>+$%x=$>9 IGbFs|;B⢉ԇ+lGr>t"p\Ԣ﯅~W @ˆu2//3D.@ @  63AT\K`AWHA(j)!w8jtIdĉDnDeDeD+kCȺ,6Wj@sR [iq}YţȒ ȅ(0  ƨ_PHcLFgFhįLǯ ˮBGɎB,ɨ `:⺇rܹuqCBl|p$^Pa@ Wʺ9ۘ 2 ]( ,A DLFDTĴF|F͈Ho¬ɇy'*7cRlκMMj3s1~:]姺 /Ӛ;pl=3E 4#qBl!qʇ+bL02_5u2B\" (6r;\^Z=aT-Z' .z\=="c=-+D9">b]tc:NZ:"4"m2F3NPڻd[ddCcqTÒRIT&b8zȆ1ɱ:9%m%d;d+R*V]c)KI5NORBNX%`[Z[q(lTI;TA-)c_=-y`Q[!]JJmu] X!Y{vja^!DBY>NO \`؀ Z`~ |cC^d^;f_#][#td96의+> 7@6CB\vsrK!څ xQ$u 0$ eJSnvېp6x耏 1'}ax%e-Fabi #|P:[\hrlp!$KK4(N@Pz?_gz?}z{S[0( h2-¾/\:[ #W[2\1Dڣ]SRZ%|z/z/oԇw/RgB 1\{cw_7w nj٫gd=|,+Y2@̘uw!aӘ/ +uː/?^j]`mPaBײm}$]`^r%/YD `#.rĊ0f cLdL9L6o>ՙԩТO*mԟT@g(b?y3k_>Ew5>v 07 -´w/_L6qŶOKnu0;.C;bv+X85>u@S?팓 Vp) ]vYS=$~`Xb%A (Yf) hFZ)86@}bJ> & D=vF ,\PrPF"|@OR>̤M&O"[Q;:hK+.N?Kp tud<Cn`ᫍ=VQxk&l ҈ *Z'Ei EpE|ُ?@0vLO #Qi..cKYMS⊧Bz |^{`L޹t~EECakۺ;"ZABiH9-EAّsVpOÁNj4=ɮӽ4KT{-5a jL4;&AOܪGĝ#M@!cQh &k &( *2Ş6$%#l34dFa4#iS@Peݹ?LnC>aw.s b F -S9Cm*XIwQ,%k+*/^)+{%!\$'0DQQ. KP !'k KKwsZ6:51l0Xp.B ѥCI<@&p қЄP{:X)xo+[1S)PaSS'eY"ͦ5'7i-PE-(Zl`vtGAlwcK.`*#rHl-2`"!ɛU*Q!HE6{ixtrzx ѳ鰕 bxDNo-EъUEP~` @yA9ǃ"M6MKdCjzڤkn!a Dɍ 1 ظ@,#| E 7ҎXЊ%x}P%d,zQ@"6.BS^h(R2!CNhIu(&cB$"; MȔ-򡯧"PW PDNZ1idR+gN}*T)Ҕ]R$"^ I]d)F9ޜ%P?CGNl|~!(]XB< UO:Gj;D!aK^$XD*ڒҔ(uZ2BM;qӛ*m bA"Q PzD((%UFcju)xQ-; x;孼(e ,xkR4.| ~R{w thaU|l~9LExStRL X0BKC֔RPI[!X-'AVnoު0EjA%^&t4ݫF)OtӂѠPWV /W4--w&ɑu0J-|`! K.8(Tld'Rc ?a'la NPOjŝ818 5w׽6(D\7E&2(f6=AiS֞6;Qr\n{l pѤr^jr {s5^8Y#͛%Z!vaQn툀?,q, (ނbTKa 1:W9Qc5+T[_X9΍mcbI^mfcF'!L_:ԟt]\~sp#0Nî J\">q,Ԩ8q5x\k8Fa-˺.;a> ^:8qws$+ !DtI%&Itbkݏ{+HG:TtH*Ρ6hT.AHDutYҹװT;` Yzrĥ̅]fU0(C* :CB +H + *(^(Ua؂ .t> @.td8PAAT|`2Fj20C^"0#I#76$((KaJQ#A4^NnGGHzC CE /4[<ȚH臺ިlX=8[:[FdDe%3`F2C__~'/>#>y<67H`cBdA4MM~ grfO$ P `0d?YqǢ.#IhU]LHx.Z0FCpDp;*g(ttV'v%Frwzg_~w"ahz<_{dKRK>dRf́56vvfm0 UP~dtHYŸ`^ބ\W ji$OG!\AA~D$XgT$j30âz5e<5b`*a1nÐrCbrC|A(,R~FiZBfbfz<\yD$l`^wѦ=Wک GPT^Hg|*jA,j>*FVj8=imkڊH}u/_4p0ZZ,̳hn,".,BnJ."Q¼.*.oՒ2nIWj ƎwmGD.Zy@Ap:i}dv>N]`J\@,V/no~/_S<0жj#Ao/F(2*@/Tk(S+=lUUf u:$?_p]s:=L=0 8/rjAêNO2֎%X6i&c26q&V'(A3Y ˼xN/;?CuVD?fDh-*{qzqq⺁4CױR5;ă>d3=8̃ 6| p #r"A02#\o( nL@./@? 4A?@4\-Wm+ ?BuD}٠ /{ *vT\1sx10332,5,5T35S;OC77԰6B#$AT7_̳t6Ww.iE;`Tfw)Z_YvԚIW\3ot^_״MN{`{Cav8,QR?cCdKvAu[ЁvkwDw6`gwY?馌cp Z 64j )lCI][1[ALt50t1P1;x7Å1s/ ?7RGtSu[vk $w83>Pn;?`{Â% Ye"+`.ZvL9{\K/x0290HqO8r[7Úo888xcOd;#Lw#TA߁A3c5ycEpDܒQDL@jߜPq>2Nxn&+p4:\y(yGFDG>Co\e>G?2 )f[As{90C0y37Ocӹ#4BOBeAxw_;+0ov+x,gTWՕ\O6,J?@@%]hGFH; ȟ6AEI?cVXb*{ [,̟aAz/ ݃B0PήvJ?Dn |A߀BA  G{I$H#En%~.@)3}-K/0YG,)@16;34'vRy_ `EGxQѸ/E(!N,2:E6RI$ ILz2JUV=upϼ_Oeӿ @iKgVZJ0D{!Դd{!Qom4Fw+sѵGA DGHH^LuTCP-dIɕ(`kV`vy 96Y=ЭiAYSxй[pa~]LƝe7ޟIL%z~w'ʃ.Ϭjޚj&VD:GnhѴVlg+mvoTBkp{@!_dELD )Dsm$K01@uW_a] p)28YFቯC{NS6>1X]^z`hAF\sc0go\H{d)BQDsב{. GߟzT C  x@&PP!vX W.V @kiVBZ(Р3M/z-0a dA jԽ}q`'rPD"a*B~ܒM ($- KMkꝛ'#4dy̋Q˨Iul  N(pxC7D F|SڗDD~Ktb%)RhMq)FeBcR̕*AYꠖp3x%<ɑvDyD|#m=!b8$C.rD\D"IM|#?mr0=J: b8]dK,RrHw)l6\ Y( enYG(E-.1*( b@R"M"󛉰)INsu 8 {NeQGY2C)->s%.,.c5봺e [VF5Q ! Rp7$!X.4!ΛNҦ9-% ӠN xVEhQ{#K?^KX;x18mUe}r hL@-n Jԯ1sXF`!q޴8u,%͙IVﴕ<@;HkZ^Uu-c+q<Ʒ&[-Ƶ' qWN7|K]j.w]V2O{Abz|:qa",4 cU.,/yCԴq:a6!a1gËewI)>;1+v-9<3 9) B{dÃ`! OJ9ѧ ~𕍫lr`fe$/?ֵyy#a]g:r1PeF~P;,LKU5,RaVkS8į&1lmk\ Y/{5tZm >FT\h1~_8A wI<Ŏ>ID7tr5qd+^s*.T&/eVٴ_cdOPMdc_ػ66˞jhݧf&~C\v_KV#ݩfٳY86f=YQA)DQ^~5"΋@KpCD}Etz{CMG}oQ{_D6WҺO[{_}PO}c_g[߽}i*֡65:gr&7^Ol0 nlw&Jւ M(@P\ [Pb!0e/l9JOn /[/[TOMA;OQ 0(.Pe~Kby_OJP sO/ԯ0 ipwf,-Nzp !x0DSPPm T_O%O+; kP P cohO PIȠG MѡHѠO?a/؁pw$; k*P俔,q ߒP2`0oAơUݰ gpQԱ+oRC(ʗ@05p.attA6X6.&j!qs!0'!0 ͵.҃J#т>0فP؁$}(^!^a6`+6Vrnn*Pe.+q.o N,b *o# jj.`Vra\a5,17`ruq p/ /.N sC.u\&/`r$-A(tA-]& Q.3S$p;Upl8`T6AwS$uAq^!%t!2naƁA>'rXU(QwSN=Bj8?t+Ԅ^-t/+^ԋ.D.FgFqt%FsEeI|м.@/7)ɈC ? p4KKKKqKK4MLKKKMM4M4OTONKt3?`ZfKxt .P(1/@88$9@R7uR)a(S=uRMT)UMRSS=uS;5V9VMS-V3W1SwVm5U/UWuRgRqX("Uwݑ6mC *BD-A(:\-UCza~[\5]U>-nnȈT ZG11`ŨJWBtA> X ]}W6X.va'6>%WJd]+dqEbq%~!҃٨6@4`͈# /@n\AcΘhkFcs>$]boEiK>jAe`9Q/i6` N[[0vanbKvjMaDvd'jIv]A<kvdy%p!&ځ @.eDaX$,CUgoB&6(ՀkoCajvpaW<=8vpvMbu>zvokbrA1¢"q6;7 n*gm%^&v!o]omhaȵc{&w Wqk7jd?oXar#l NJZwZ sJ7oA 8lAhao|6i?VjmwqEb&}Va7~WM-x"*.gEk}pa6biib׃7k7wW|w=köm fцYr'Oh bo*Vh`7p_})xqMi~؋|!YjDzZ xOZ:Ren38qJ‹cu|#iVww3?aْg8 \.`J=wMZ#R#-5dye5fYKK(i9zMMėe٘_K&6w8+QZ4P*KdjjL$9KD4kYӹOE]YoIYdW !эp0Z/ZxOӢo(5-a/5e'#:JO1ot!rɏ;I.3I AڨPBP;{Q/m/owRXYdm(<15R4 +;Y Cxإ:OQ&: 51ѱ!Qы7tgNbjqO[[w_I%L#[zy:RAyR Gwqw"hqYU5PNZ5嚪W-q 31$p)=On=[RR?2OS4ۭۏԻAŻr::ów%-+4eXcb.t5">-`(j`皲3tBߚV;u!҄ÉHS$vQ,!&kvX&7ˡ`':  ua!Xs5[B{ ӽP3kK;۠u!npRVa@!GrB|5[ZMS}[.k0I! UNY=5 $/,Rǁ(| %1}<ܭ?zR -?cVNB+$olء` 6"}FOӇqJ }CаO.ۍ @QQ3޳zǛ=(G 1f56K0B[q04'm YUE.;݈P*ʢ^?+E[~Ooٞ޾ŞnqU,NN3䡵AF 'K#??ꩾQ UI-_"UkdIz :Yz\?KHCkvΨlWAC`(?߭ZWz,v÷cp>q9q96aVq!ِf <0… :|o?{(c=yǏ_$K92$ʓ)ALȘ3U2$ȕ6eެYS ITPYFXiŕk .AatFMݳQG @X:?2A>ز Ir,s,.`Ï%h6]ز-3r:)Qv=L;2.*b6Ҏ.qPϊ%rQRO9Ih`V"Fڃ\pmleY\0:Z ԘAbiC+2\?A\@:,ݲ+Ԏ7?t+ZA.K +IEtQLTTxFhB[=pVSYhRF$,3㡺T#K;.h+`:Dd?r0+ #咁z \e "6?rbGYO{nZnR#>iE~$TD>*+r>r`Ӳ-A.O0C".8?ȈyPe0NH,++PPOtN. CA HtuYԺO ۡ[[R[K=$Y=R3.b ,!X-{.(H|$"\?KM9BsK; r4't&k hd ^pllM{,"l Y*'cXG2)d ُNiemj !ɑQ *ZQ'\!QB/g]1$L{UlK2,Em#tG04$+2TdI3IY&"є#QOesp‹MHӖ%+d1 d Bsd'"cPY3MS)΄.Ppƺ{h-1@9I{^M! *Uvc uX0.רМ3WcӴ& (O\~`G 6ӏ|:t!ij&D^ٺ\pwpӅqBM2Җ̝N:@ma DG_U4}1"bq_eUCų`۶ʎ|4zPd6؇:diL&L녑y]RW$|Jb=v{ l@\lk〯[b0:%YH X"< +Tg#D㠀w&W\㊳q`s6,sc֖fFi-pu %#II ~X\U9@]tpL "d!]Dl1eِC aM@iGd)\Ha]z="E9<逘 K6  nZ7WQ IT2XTL h[9` x%H;i88ˌC!B~/ò:a Sry^!-Վ[$ca+ް9'LGi ~BG؇rl j8fv0/ڶ!VyhӠt51}̫K nVꤔg<]"%ì)qlu ef1BPgtڪ[o] @%k')([ΰ1+3 J9А K8^ڡ_ʢR` ;I{0`iZ#;U8 WYKSV[΀ {%W]˵];_X SjQmw e˶fπ z:Mi{gV˵i˵"۸UKvrhWrN  bK[+{K ~0 +ۻ kۻ뺿; ʧYJjK ۺ˼+۫;ɛP + [;䐺п,+=|o]bվlF̓Jë+"Z⬙P}:m}ѵ8 h] +pB @Ǭ˹aƓ b@`ѕ|ז;|֎ʴP|sӽּ \|l|u 0Vb~b ;~G,x 9 W[z.@ }*֗*@  =/m,. -ڕ @ˎ[L,ai|u|8L `ǣ}& ` ǥØ}<K%; 5㸻Ͼ* tJ 1ڢ󙧘m)ӎ2m>.Ӽ^LИ}ϘLI +̳sK`0 0  O&+ {5 = o0 $' l!'/!o?o𜮫ǙNF 9_o )p 8L oOo xviH] &c(o%kՎ0k?i$fo*O'}_cow?wɰ̤̣ZkS0"̞V1ooe_r_%O_x? ZE//|M^ /_OgF[z{.NLy?}+ X[K}ѐ SlZkϠ9KVϞ{.̧P!ƒB8QŋU'/4h~XR H U dː/S,IȓXx0O ~fW2"tӥM"j%Jk]U"%GvҲKzEVbž'k7bFBlܩfB;v1ۨ][‡@s^ ٴi1Ƨ"hHt=zNQk},Qbm{O/Μyi?s^{>}n{g3nE׏&߾~_zp^ڎ(#0HK+>+ˏ//yPC̳9,ܛMD 9.*<NH6KA\,A@1G 1pF , оrHKzJ(QXIN *1Kc R̒D?sWsP$\+w䜍ƻp HH'+M=!I3OUFUU%QUGcA9sXb,6HKE4nYǎe~o/7,=ۀh}ϣ:ĥrCL`. |D Z̠7!UЃGA ,]7T +<8!.vы5!qH>ahC!CagC#P8*Z@dbqW #w`D,6qm:G:o#Qyv# 8HAҏt"7#D"IGq|d" YMn{(3HNJ`z%,!Zc|-mXҗ%.u] &,WӘ|3k)Z:Ӛf/LdN35Mpx~@*y?І:D'JъZͨF7юJd'<PVԤ$5I!҅4.LaJSڴ7eCsJәT=)N}:T:)QwjPU&IEjPeStTԭ^EJդz`%P@̧P*p#PpbЄ0!xw |# iPRBx6hUw ܠߐ @h0pp  ` $ *@ ) @pz@א{b@@" `Vpg@ $@dW@P@We| b0 DG !FfbsuU9aqP@&dR W U}P!xր P qkP_  y@&@ ؠw ݀p  'V`VWX Hإdhq0 }@"0`` S  kpxiT  < 0WVl j a @ 0'@e# ڰ < e@ @0bpIZgW)UOu kn`p  wp00 Ԗ! F LYmo@ V 0t@0~ pـbPr"Ve  P   sБJGZ8$X56TЃ ` `y ' k 7 `FPV@ qGJy p0 {` ux pb` y  xPVp ` @~:.XoQc IQp&gQ`yӥͷP %8UqPPNʺڬ:x8ںj!5ǭ:&rm䚮jQOmA*jޠ گ*;{ P K;{]*x7w7a}?\0 |D&;&'),K.˲1۲T0*90˳: PeE_7nH+dIkW0RP$O[OTOPANQ{OW O_kRƂv[nl*PMr;t[v{x GSz~~uUnj۱7r`EJ(@KG@۳{+$%ts ˹k[{{RZ_iwrcLK= {@" ϰ/۲Rp09 :;2۴گ됲LXE{_^KRqKPK|0 u pŰ# `po "۾& ZJPk@ ٠#P 0$ p) |[p eS~K|g෸ P{ pPt  x0 @*̾)  rp | SLuEG ɚ R Y&G~`q{k| ̛ 0zpypz 'л" )GP=}9 ӫRnl0&y ̮[Vg7i7^J{n48մ̾ ~@ * cy0 d pPPp\ǫPz+Ƌ`   n\pŭTEe5w7偮}|闎^>wx.MݑQ"\DLai뇭d0۔.T.\jo'_"mV*|enmαx^.$^gN= .3d"θ6o.nw\N~^O^7<7-^<%U1NM|V#j _\u׍N쬾]&?/8n,/[0 `/f_`Oab h co`y|{ } |P.3*܅P _PY0_ o rzk:[uRP0 ` / f`o ď m_ 0eP ?ɀ鮟^$ed0 ɠ _pɆ (d_Ct%[Fbŋ'Vb3AR9\ÆS\J.cIS&LC2g YS%PCҌP2^Z1(QEI:W1*GW I2ijgMn]yҽ)ogx\z2!4TȏA^Rlc#C}:ˈ> UStUӜI:{SyQ Wꦏꢥ}afl6{m>mPͽ佳0WTZm`yVV/ UZ'+r3̺YߞiuZ\ts)YOF`|Za=<ۅu (AKUʘIUR gt?h)ws5,o_6צ<w(fB|7|/)&}?wmzt(#D%WkΪ7s 愯Qo*[Ѓo? " aM3M "tO|'u/C|9s8Y! qYw/ yӧ=}T!uh}UĺQjC- RׯIvMxf:Ͻkd:`~R[~jn}̫mb8EXq;X~FmtO-t>Ȃ:4bI F`nPE61Dh RRN%>rKj%hG#fD{>FPb$NI:WH|漓6 aE7/ 3e:gf!\zM1>>Mf{Hpaśl Lؓ9[v9hWMV(#%0 Wm(#Ɔ|ꕆfW*k>j!9ZjXEKPvSZ,9ff!޺!5bnf X,4٧8y.vV;>\!V᯺a;/.ES4IYēDƸJohn}9PŹ8l%"c9Bx߬ uTF"ĬU0[G7`CVNekguoxSp$& Hf:ҲP'upW|OSzL,{<͂+"8RK<8B<;Rhh3K#z#L#Dr``Gs #K){#,㼔"YeǕ*X&Y?S>Ec5JW4}"cR<L:'tny7zP`?Hj&L WZ+6!Cp5akC6 !pG,&"KF/~QG>-v]"1q^cɨ6эo<#݈F8t\c>H+#4 $ HB L!k,5|$HIB%l](GIRL*WV< X̥.q)KZZ &0LasL&2f2L3hF(jv8MlVĴ:vt$<)wsd=|: NJ4(B N2tmCPC7 Qz@Oӟ'=Oϒ40;JN3MKMSֳ?mgI*ԘꔨEAil,5TӠ&U.V}2զ&W c *U[u0P `4Pxխր+[YWgԗC*Wnsk=NrtpUge:u"-jjygCy"0's#PV:] T ȀRa\ װc@nfXCnMu׍.k Z#d-|+ ԕ6+[;2]0 @ _Bk/,P^Grv[YjX]e}kU:R=m3|2*2ܸ0nk5lJclZ֔7Mg=GLF! ~]s)ӵge1alʸ' iaٷ<^/F0 <W~X5満Tj} ma+>8ħ,YG Ԩi60${PD#91ݚ]msj 67u0^)g5y Wse{ufn}wZ_vu)\zzHUgaZGۮnxSji:hzNܨ _'MNV[hюo|(3~ UЇNHOҗ;=Ym#"XϺַxCHЫhO=GyNw^:v`PA{^j<__|')gսO`Go9k*z;=4.p/Oۿkz:?vV | %>?pP|vwxz1w_OzA} Ȱ p G}xP~ᧀugz IAQ7pVgg'P"Wg})y/rg~-V϶~'O} DHi}'xЄJȄnwP8KQ$}buO '@؁G&(ILj؂J|y~U}Ep] LpGZ8|ŧxhyȄLj1x~S7X؊Nz8zqxYUH{}WT7~Ȇׄ,wkGL7xf8~:OP|ySvHLx8@xx8 ((8 ɏPxWvwّ]U:SvpEq'QuW up1iq&qgs(EU1X U7U"N\WFyXOgUhPR9TYVyXZR 7e\9dYfyQȨNLQbprY^YLOo9z|R`NyٗYcY=Ֆ?5٘9jyWxq&Řy^Ud|~~)Ɩ5Yr;RYp3ْɛS9Y 92՜ɓ srrIriY0&UY{+sU77wZ;1qךə$YTYIs=9gviO㹌鉝yщUvFs2)נWvrIrTA j h㷑NGyy{L yHQ/Ԍ -XyTxhg Bxeh8Xm؋vxV㊴=kLpޗ{n{'zXz<x|3F)˳pG]]K J87KLJ1tط(kȁBhoK簁Gw踼 -Qx;vd뺢 >B8[״G;QXX[: 84p EPa}Ȁ[K;ckdG}ȇwf{}(xwjH| @LJp艠(k(cE{Zmgy x{{:{ (c,m X=fJy\]ָqǎPp) ɐ\ɊPɏ܏flN9ʤ\ʦ|ʈAqnSO QYQ Z#; ۝<Z7̬:u0\|؜ڼp [$լ[M\L,|H }4W|T ʸ=͢Yf3 " m݋ł*iG 2]ZiJ7.ҿS$i- Z9mO3O@s:.yUqiU5sZ=9h^~0=R (*⫰.0/2^0~M ]a? AB  F>IE^JP@NMS䏠 S ~U%R^cSUjnQD~hn娀9n\s櫐 | {F}>N}玮.>N鐞 ޯPa~{3硠[ P ^ )P F~zp^슎9:ewN -(n6nNn >gO%n>־ U\ @ > ^.뚐!. ? 8N >nx0\눐:8 < r  ,/>>PV ]:ScK;zP?6`눐d>@-/䏀 H#Φ ?g ۮI P0 Pn? q!OԮ[]_XDYc^U_wf{jϛFr]KTJ  4@TpA}B"Hb4j';iC? qDK4D+4T{l)"l-BFsqGI1++kHQ"R4 # ? s*J.ɓ&<,/tG&tqArL+N's)+lE-qhSE7@Ӛ>_Q85#3/(ES##esO@TBQAdRC[,M4SD=ųGMmnEaE5Ur=`tPay=V %}t l$=Xqϥ(]UNu+c+m o`Dre$wJSb(NV|۬RRC >nd q˝]sIۓ3@d܉dY(DT?leK3Jݺk^*1\Sig Fc+[sDTlMmX{SƋ,FHQ:kkZkdvܗMF:f#Bݍ1skbv{eg5-ruyCqnv?f>1mx(apmI%vL{e4Nv)؍?Nzc {|_ŷAnCߠ6r~X\eDnNܚPnQg`߉,t! &lYAጆ/!ҔNX (<-`A}BWx=.YB9,X{-5ZrW䇙eJUPmU Wʢ,7Z!cSqHD&Rd"HHFRd%T(JPR$(RRde+] S~ǎ~"'7ry؃#-q9^rFt\2f3LhӜ&dS2YSbx_RyNt震2NLDE SQ҄NjFղs( #t M@yt `Y ֲ9󠵠\e((`Ph}gfYNֱTbʾ)Df-;Lzvw,hӚ[݆'| yXxDWӥnuK\WnwP=5{^Weo{^5b_1}I^o~{W&p?+\CBQ}`GXyp)La +/\ ;8p3, )&.11b'Q!1o,P0`#9E>rd'GG^2)?Q e-wha -?i2d8c_s5r>5aiL_Zt>M_ZC]jR:էf5[iQwZբ>5/M'1]׻iaw,vp-|5=Ӧv}mlg[vmpg}qnt[fw1nx[7m[ߛeL:ox NW&\ gx;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentLabelSubPanel02.gif0000644000076500000240000001776410534177571025337 0ustar paulystaffGIF89a9999<;9A]i;^;};]:;];:}Dk`]oklTx9* Zme"ipshłc٧jj9Z[Z'9VVvٞDVrEblƙ_hkY5s9Λt+*I(P`eźEޞFzW\aElVیՈ _|'uDIt%mPP_QRmUϬLǗ<򃥿K HLX8n"|'H Z¸7z a-Bf(L W0 gHC"- }Ç? ("ч0&2N"(|ѡ0lt_ E/f486vqoL#(G71ucXF,q;Ć0ABL"F:򑐌"H-C̤&7IMRҒظF(G)RL%*WV򕮌%,g)X~a-i]ʲ/[VdĆ2L/6o|4IMgVsִ&69Mm2ӛ&5 MoNƤ2LsNЌ<)Ow7O|81OsӠ&9ʼnPz ]h= ,2-(?)yꓣC5Q6: }cx?eQrS'MM>mKSZɋSfOb htp2/->c?L$YE;}i :H4F ףc>Tȟgķ}zt4zޤ~Ustz'}ƗWvGvׂjw{QT{f]Sjs8#Rww y>( @0y`0{{@{,agqfTdzrp|@PPdH g|Z)ȄRȂXGw}g!D~7`||' ؈``ا} h}{zwcyRX1Ogx~܄w~ t8Px+Hx(z[hgd h{-xUHrhMRRFz:xwuX{whxzu{W0XPH蘎؎꘎눎p8X8 6GMMMw ِ׋8xjpONOTqdn5XuWBpq-)pGUxn3W6y/gO M>@B9DYFyHJRUp=TYVyX MɌ44Yb9Vٔ8dlٖkmLMjvyTixQuBD_)f|ypa8PQ9YUPtoٚRYy9g]{UX1i*)m(IqY]9p&Ip9U05q Ǒ0npWL;Tמ5S֝tQEU I o nd{ 眦 Ʃ aY٠p"yOQ/psL%\R%:U=OןwSgf^ umIɟ9ZSt1g٠[[kl4[^EZe[G `WÉ: Woefg _&]_OUX_ iU`'9ө$^xEk\b. c3gb0bAF[V7o B*1^RegPe_vU4EeqVx#%)}9NJ UV%ifie&h\5jU0d9. Q~:a٠dlEl6ME\gfb$[y]Xz:ɚ[jZڅĩƠ:ipyT֣v 1]Z]'Ozr۱rgxY&rިq0XpL8:hwvسc<@smvo#L.+P2 zY҈3g}zz_+WǃgwGXXyNj0|"a uf+t}+ xs7M=w`O|zg|ȇ&^}y+Өۇg~VWb d+l7~W(P{p˴r8KG;kxYp0&8}`{Y);(}hQ3*g}[y+?~+wY[+Eooĺ@z`|c$@8]wx/zivxYhMBp> p N`}k}D۳ u|Hkvxָu;yx|xz9܇z hoۿ5MLQ8w礚 ðxFsݘrŕP8ȏ9v,X}=pi?: )R [nJq0]{amn٠ڢ=ڤ]ڦ}ڨڨdL M۳m۠ ]ۻM }۲ýM]ܸ+ԛU `ݯ ۽}ݒ -$͒ >^ N}Y >^~ J$MU6߫ (~( *,0*3.01^/6<0IOȴL-=>81RP)N S {ݩXI d.e~e>d ,nkhltj.,npqA> E~g=Otl>歐^`椰^+ p m +p N~ kj|^mnc t)ui c 뺮뿾 `Î뿞뻞~ ؗ}(. neZ* v`Z\~ n~뮠堐|CE 1y00_`鋰Ϟ+0 P }NPU`tP `>î n Zľ!2$:NuPb"b8 . BN /La4 (떎P :3_42+nd?؎"mAu fa -?{_+a. eP+@_rO!]]n pJ3S 00[;\8 ].+۾b?nZ,& 6  lJ+ .dUC%Np$U5fۨگ``[®a(0Ǝ r͚6sYӧW8ys(O?uˁ-a 1ɒ'\9lN2%TٲhǮ}[$ׁV<ɕjLlw[,԰F/fpbǑ!GF<0LVER%^x\iԦc6\lT@e6'ѩг9k /̻M^uٵo}籝+ye]Ν՞}{Pϧ_}><ζ 6B3@TpA_+Pdn@Œ@ kpC; i C%C.Z`Dѫc|1RFBۺ5h42AH%<&3DN)qJ)"@ :)AqD9 /Q- ĺD25$PS,O@tPO<<@4<40'H%%mrLk/AS44O:m?3J%V#?hE[wըAye OJ N@t3XgƌY#֯ncXa}mj 6=o XLg}LVUB {CVlY@PYab[_ ne]}Kbx_~VW(^l^u#Q0okbDoA8aՖ[zL|@]yvbVQfީaV09P[j׹qsy$tF8^nb`YNZ3ml Wa6\WAXƆeV $`!O=B.] *pٹ]ѝ1P  Z^3;@=fs:q~ꨧI`ۻ^ʾc%X*jX_. Lnb'0"cx Ll .?rab .Vd8x (]LsӦ*DJDrJ, SHa \)]a Ask]TLz(bQc$cXQkdc0#&OH1vGR?YRЈSp'55 Rnz0eA=BlLShd}5vh5+Y99VhMmcd*YJDBe0 `GǞĦ)$Q2RxdVlg=|V%miM{̊/Q)]ZVmmm{[v] 0B 5ZVd#uYb]VWnv{]zW%ow[^7=/{^@u/z{N7~Vw%p{ ^>ԝGv+gp6 gpa_x03\b c +cw@>6IIWWBA sUkp|'Ol$0 0} 0ƌ~dJFivvw0%z\ij&oqK?) ",|D<:}loͬ03n>˛8k $9|yo<ݯơQ?:ΉRcT}ieS*PJ;Pk24!oAC+R;)<`m(M"QL0̀5 }B@D S3pU xF$"qtñ\ 3#OE[0&N릂MlxO29P '@.H\Gj Kw &s(5ŘKTeBH=ޣu]Ƙ'bٓ~nuS@P)LA\MѬSy V9rZ$vzGlOHgevĴ R\;&pp=:"K74(%7h{wM+hTL\^@P<B Q6l<<lݡWa'j2Q;2e)ba*$sKC@_ڢPtxj: ȣLzS)zsupGe1z)Xat(?k>o_B `xYТڂjQ(pKhFT2zHl׀sUN SXS2#! 1] uE; lֻ3+}z7I^j,oC24wϕiSP /LJiw_NPn d!C @q7YR3\S @FK` Dx#hx4xm0pc/q '(W 4@Q"}C0ʄ~\WwB5WA%4WO4ْ[<-' €T0 ay ' iA @=Xq=dl5mWR!YE|;3ON$V(uUbW҂W22@ d|TpIhyBSH1}u3ɴKJ,wA411u9kAxT31vS3g pyX:(3 UtEYpvIhB,%KۥT "phRVu&D#e~_KV HV@ crF6HTGyZ>sa0! wg,&rӱ=$$12/[;e8U~M0$^e j0f3 /$bmkRhTqCL;Z2{H+4!H)[G=#r qvP0t3!E9+4iHVNxD8xR)>33J&W' ll V`qiP!kBT_0O>uQX*Pt]!qfERG"BtS&hut"!@E$e -1+gX%sW}g9h4y,g91 >$LCD,sejSFK8S@)%pDKBYD:4gFҔZS6!V>u™2R$cc) 4' & |0srz:#g&es]y%yx JHfbfB Tm1p{; p 6y&EZs(Y C" 5C֤oL:- +Gj-R'4Z'r0ÀDfcJjz8rH  y ~5| 0 p} à S (m Dچ\p_=` @*"PH 5 0pj* ڨ 0"hY`LSx<;&!aFYGT`@چ//6 }4C@(R#RDR$'H%$?;S nt$ŷ?JF1+IZy*Pw7 4^i3hgDN# L'`W84S76)T"*r&/ VǞ{THx&K KYg" [7a[DqjV`_hX+U0"Fўu@pj&dLC|JQb5g9{Sy|XJhi@$JU, Tj6=8p  Èr!ʎReDO*Z:hL-g~5D0rcİ=ȗ3Sp%mO^ 8[8AJOHAK^;G#B5v$Dd:U O7: `HqsJY)S2ϑG5Gjp1uWB[nR)$l Zg1 Y7+扞٘RrtaMiQd;8J67J8Cx(+QrD&P# )R6R@^C(UHGcDpp  P`PvNUC:@Y|,,˵WT D ^n\n#Go`2d$<%<NþӐjYNed[O UM0UMq wOKbY5ݬd.J۸c1 0jpec0ZW< `ޓ1|a}!^>;VT$\>Y7apk(WB#<dD? ` ؀~h4"h!0` !w^3:tcK Woa'IT U,?am 30}VEv@k\ ڭ4X^{_ ]o"ԒG;'p<D0FzxفF*M*Jr=@\Q_,pJ!SCe$f*oJ4"DP"Br" scq РT(S  3%Lc%BÎQX#DD$YIܭr>@YItSqhlЈђSqӉQ &IcPaO@```ijPa0) b+.Ev7꽻1M6Us&4/6ٶ*נ>AjQP10сc[^- !]!'ܸ {,:=/c#U6sf>s2H-c: )E#2 ذ9G휐Vj:Hli!- tP 8H[l99,1cۀ2H ! k7y7;Qb/ D*S$Rj-5 B +q8 o{I4@:4q/Lg@ :ؒ'U2F 35&H@T(TP$I*Q9w&2Қ< 21^Q j<9hGՍ 0tPż&`?}& PFQQyD$L%5C*#hBLPuc@ t:a}ͪ@p(ITm[)m$ Y]*<͜M"SB!gG,;H~Ӓj(Q#AxLJex' TEj+4S/ǍE`Z r"UgİǴ t2iW`+$1I;:ڪ> ;#1@ ,܍TQa ; @<\}z{ 9cY⪮p <[G|ҡCO/+IZ5R὜Ly9d5%{[]t@w}~]oQ)X4ш2yʨf=lm^ooTZE8Cpd͠ *@4<#P\[1da's,rb2 /xED ؗ;@2]ˆ#%/HU4z͐2sP3x@-YpQhT*WO(%F*}:@cyK~]f$R9C&`E*HbM=t \m44,RoXAip#Cȥ.%*Uy*:$Euy=r[z i.xcgs c!PbPY$_ЖuKn=:L$o+{<jOa -iM@'FQ " =v # 1+~uA*1wʈډX+QEi\ NHSܥ$CAa0%gtALDT|@h˖6")=J*K ƒ{11ȃ I3⍲cr(A0{ * OQ z: `#Q aHA8W# ~T'1~"rZ"#*!(|wQߙH!;ӉZ76ꡐ:X污hXP0 ?X7a>h¢±1Cr&9I Ҕ" 54x )&WK?IZ$)z~59Y74:d"ZA \|?dPus$D\{~B|'+;:yh#+yѵ# }q+ЁzYGv +0Lƅ3fA#8l0y 82X[:D RؘH6c]Ȑ;H2Fk3@ 1*qzC )j됴 B󿧬M¶ȈP ͓7OČ~|CJA3Y dHPcZ=4|ː)@<47 1y' &Jyib`f<zKP,4ʷ3ȋH(騣AP8E֬rɀQe2p B˴\yHE0I):NEL/m&f33`rd5y϶da5ZId Z J°9Rl2X"H$PBO N".H):OlZ8ȃ̪0 =KPaP]<]T5aO"M3>P?c\7AɈqG.J҆,} j'Lκ \uTA:x %S*Mվѡ+, fبGzTh@S4IS|"Xx<BP) >C/3,\)]ډABԭUm48Ã, 1+$44CB.0v%w5*B}(JކZ^g8?B^~š1p19dbH\<-M2.sND gjY-*5a/%$dV fڙNEf`mQje`F duJ .;0y2Z̝[4Af((IR&m=-a{F=+M\;]RPwڕ EaP Z-mh)*d!xkUhE`G&ZvƜȋ%BgsMZiۋgC2z椩hg@Ȩ4PېǹWmE5|F^ …2 WeS¦Ti#Y%>AP"n Cj(giQ 팺laj.dkPblM3PFc2լ &Zeq誮 yiL\^Gi˰gD9͹kxn2ͿM~WCIy=S³1bx&fF?@x&!Z^kF jŌoiUhɀ9CdX> ! ?X Ycm@)C|_)6f -GvNQd0O ) _ .~.׍3|V~m!,sil>4Tp֘6 x`g*tt6[slsɰNiN.ɯ&֧2zÝf2~__:!T@!/nqVD?]d.POre,wߴ^& ¬"#!y3^!pu+CGsNCGt籷bmN0r>q& >%Z/ D=T&mQ3 9Ro#@uGF*x() UXgc*tqyMQy @Q~zyDn'ZR,+u;W.]uhJaNÇסTIؐefb]R@PADYF;R(SlmS w2ݽ?|_tL bm%i_S^MYC\62M'(A& z: XupATdg~a'MؗqX`рhHJ/ko¾eTF*1XIg접FueDBV=((yFR 4x0$BZ칐BTbdѕl9-'m]G?4&GIhtFb﹖)%Ik(Gۛ^KtKN,ϥ].|m RuRАCؐ)PhgZyys^]n^1ǥzwᠷ:i _;d yWpKbr&60_E Θkvs֟"4A&- ;B u%b[St{ā D1,Z|C&gBƛJADذ(2:7;P{Cf ϔU脸|! c[!OQyHb-q9qw^PgCe4Q(d,^er"Zt^PHG߄d?!Ei̇ ,8ɟ GBf=̃r^H%KvBdb< _$?w pnd䴁GM;cN$vDR E͚갆 7:"y@laW rG:T'ЀrN\:Eʤ-݌uF M9',XOL`3lU"έb=F[`>d_EB־>Vh\hl+f-؞ޮ qdWHSih8i0plHӲqZlh 0@$ .{9ky(+Ent C Hs&,b#fxqz(c8OW i (&0> c+6nq+b:54G^/G¦ #!I{ Y J3lh(>!xc$LW*l_w2ʕD#qᛄDA1LdQcs~V%,d%7CE p xսǭ7&0ʝg&@Sۂ; =tޡ7 N#\zu $ >*t5U;iF >Cĩ IVE8Y}N<[$㝗LMm@hm4 ٹ@ DCuOT!@)^ذy$> 0V ҀdMWW9H՛@@m9`ٞh Ȁ 0Cٙ$@YZO_ X \aXMޭ@ !Z]#0h`ZZq^1 Ab#  #>bl"&]$6Y ^&&&()"@@@*RV|]b.jБ(0*> G> a@BF4N#5V5^#6f6n#7vc5b784N9)";;#<;<ң؃I$J$JC>DIdJCLGLdLC>CFCO%POeEd>"KM.%N$NKBBzUƣ:=WvW~WZKj,YX";%[ve1>7%]\ʥ7]ڥ7M&%`^e;CYaX&%U%c ;cM>.e^&ffff6a?Cejhj&9MC.he7CM8mf9&nm&9@Z$hp gi%d:D>`"AD6CmFnJ'oҦ5ЃsB'unv^gvr'xVu =Cx'8r;fyJn|wfc#@Jf6̥]C86xC]B^=~5B7N(ڂ\x~g1Bz66h1PÇ%5CM88C8yB8(9@'tz_G]6\^(_g^h>hhiZeM8 ;!;,9CK$M ݆ 8B$A3BA5@-xCzC8h*XC),6i3t8:'FA5xXtBC=hizC15@8j1j1Xhz^E*A3x*R5Ђ*pB11lãg; 09B$ѝ$&>D97$G$hx+ BtJjj LCL  lC88j8p@% A x 0C8'ƨ78j145*1Ĭ7j]Fl/%, +$A8B kL6h<%:'<:P/ > .`C D-)V M!,7tC<A0&8jR,gA5؁5Ё, nA37h.躆&/ @PH(Bp8Pè*mj4,-4lf.C6&H:)9`xA L.B3&!5n |CZ< A&4.@>C%C*C<>1B <#!8C$pC$C,}r~86,7 7(B8f&9@B5xl)B7Lѱ(m17p1˶,1D7c'5Ԃ AB&1A8Bg3^%<<(x&@31]:@; dVj(t&fC+pC!!Ow"2#nfhyҦ\gtzbs:q##2'2+')ףAҭϊp>*Ă+f{'f8CoEf6̲0[&2Rbd/dj(]63C3]Z6i3_36_5d5g776#݋:jĖs99g1b~s6G3<[o"%c&/#g3h^6l6@ t6c@AtB64cj@DOt͎*;FgFoF+EBD/4BHosHn1s2kUsjd>*Lߴ/BK24r:QQ5R;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentPaintFill.gif0000644000076500000240000003720310534177571024374 0ustar paulystaffGIF89a+++++39()54444I;;S>cwH./J56~n%)c/2i58}(,t47ECJyB^q]$>>02b#${ ٩ps-^9.HwQh)(D$?\Ј)s¥U>*rf :lIЇr>!@&·,_"BsɁ0A" Bh /`#Ie}_JB.g(k3gwi'-9ܳD.&X2%t߳[F+vL ]4B ץ[Zj)ɏ9jh@K>kp}|YR\uzi^,2>jy! #BB ̣)UX%e(-tK1i}'~n[QզAr7ol~)'+p#+zǞ* Y4BniI|9SpJ/*WJ"N]&:BY#rY$~- d _ )rKRƢ=PG*n'ޱL`| K:мENrp] ^FKQ;JlF6 rRZ_D#SsZ a=lA""| A}a"ErXVG>}'|Q Pc84 a/pA}7@qpwt=lÑƳ=g4$xV nynz$$'6ꏊĢ}N+9y LGCڐ*#t,Z*p G@N-H_XJDu3THyBQ*I"b((@$S5q[cEgPGĮzIש<'/ T9؜ӧI $8WfR͒Xs*0Gs V 2˗ VN=V[eZǡ-QʱY6pHr>Ţ]yڡU3;ka [9bX`~ HNYx$ť;Wl40w#xfSN4*ӊA,}z9W*<ҖVG /eS!!]ME#Z0Bw3 P[:ϑ3uBmm5[وQKA*ZGR*NІ.t!خ$E#\ Aٖ[S,g;:``eQ%RkUK}*ù :Nc`Psw#iTK`_T3i-ecs(:u1i0t _k+u=ՔsM&mv)Ȥ&lͩmYT iK;ӗF8:WM,ҽ@K( fu_Ls%Dސ|2I Zj{k.Fe[ۂ&]<BG 3d;3HCe$@krng%6:JzmlB E >m_8/`yB#% &3:k%Efr( ?5Mw=W_}b E#4ArCb*m׽/ RJZ _a=K')hpDá\C7O&V>ڱT옊9́0$ y -/weSQ `"S7#LGk'vP1!f[%f*!H#!wpGw z.\p !N"n&NB*gqTNĀuEk$9e#"RSH?  ~sZ`4@>)2n'N;(3~:N.΁d}I{U}sZXM1W|#CS)ztzwiVi.0 ,"/Hn|"MiVfwH죀(R?d*7Kb)TFHQ:6WQTszTXӦ~W00[ϑYVć1BwMBPZ߅}rP+`HVO0@URxꨋWxp>XgY':Eqȃӄ(+w,Miud#(L 07= hpZN3/2HX9K $AcQNyxryX"x{WL܁Q2iUy@vUhw_pfT?QlƗ5''tD-7}/J(y5Iz8WJ(Hj+k? ~hi_w~^NAnMS .Y#'<*"J>L'ZZWxBœW9- yHI?wP9N|51̦t}|H6QAa7PxW3'G(5MdCh)d&r qXtP?"2,IyU=9•W嘹!Wc9zr2>G?8* Zm2pM@ (X.;RVEDCR|rI'Ӟ5r$s$Щ!UOPJm'×/3%>A),_(u&3a'yE)uATW[7`0ab!X4Z6t);*,Ce\FDy"$ kvWD9SGբ!:z/ E)7nPd=*kʦ2=J4;p[T%zz%t֪A0TUlVG'SMdRyuho(: &gb.Xr;$jH *0> 0Msr 7t[VBmBTFMW܂k朵[%NC劫v~[\Eʹ^Z tkKu D$}HFA4V+r+VCu@{HYa@ڗEx8; D+D;$"Ihסu$hBd2&:}d@X|! O-%s?Gɫ@[ҫa>(H6G%4K&^$LBK? Et&NG{+$a*{.vM4J.J8\K+܁tի zvQq[X:L~tR BHK Hsy *b|;diGM򡾣!ԱE#4$\ǝNI- )@*%T\ (/yn[񅿜;dlƾrqmRBq콠ȠK L{,%"8F;# &ijb +\\ث%cCC+]ȌwmRL٧||%r @!;4ͣ)my Ʌ4]b<˟<Ό{ %+4̓+\*%ĺ`NDž)SȤANB%=ϰv@ hw}yeDZDڼ3/[@%PE=DEm}oM1FMMI@"4f՜| 1-F<=N<(E{U%8m:<@+00V}-Ô3ZRq_4;º =ۡrc';=N1`%-pKmY]jxDI4oŲz< 1_¡!m3@ Ƈ; 8&g֔Mx1g*rAֱ-\<`n}ړq]])\;igWF٧!@cayqAuzۜv,ϡN]Qp8n]GyZyT8*~  (3uǺ@}&7UCl0U$lI(G[Ә|w8]%J,G2#f(Pᯀu-\;s]bN$'"e8G wZj.HVf,2 !6N3D*.N$Ί,m/k~-<ij};~ēevQaKvGǣ|AQMTMK#Ќ >Y $:2]0 5J!6VgsNPnzza-C\^[NwrWM#H @[hݘ:|mLLח <)K|0ƭ `nfθp;motuaRفhM/ 2a_D %f^H[8@ -2e3P ]m7Pӻ|"FOM } VZʑ:% [ۥOklP<elEJ!wZ/ =:E>C8&OpS 1Zȑ$GJՆ _MHQŊ#ft%)^]*RDp!CYJeG'(QNPQI&CSbUJARD%pbO:Gv-CP :i?sk< +@8hHo : Y^H!yZIeRDJDm LH!#GͬRj k#RUDs"]VCK'Gi$I.ڒ+V4{COMTzLԥ.j2%#)F@Ht a;|47 \.Г@4UA>Ȉ8" ̂3\,b  k|?.b? "4Ҙ&8"Bo12+T>@~'.@*?ٟǰ( +^3?@ V9IBgg )^uL4NpCED gnH4 L`A(d C$s- JW2 VHM RK49g&ˮ`DĈV %BTs3Iy'"|@#fJ{ 4{MA>ϤBy5HI,HaOUGfK@2OC夀';d$wRF#_|ӑØgXp0HϢko2*+@"0??RMTa]bArqwɋ@t,NSJUH6r{\\(ZiGM;{)q]SRHN`oo6}mPr:8&)631Vgæy>eGk췴!nT& ޶R`9^  S.L bc Z~iM(Q+Cr"J!V`~מXh?I)4t{#3`yBGzRq/QŅk"?z$i'ʙ1ڵ9B h2$b1cKk?S!AxFh᛺b q[523ji#K9|# au /:?q3 b h/S] AuyK50>͚+"t"J2ɨsZ Ћp UK ĊJg^Px,ݐ8s E4:b>'&8>3j@TAё=0!1dJ 5+phuuFul݀ T ĐK x \O`wDEQY;qx> :6Z4E`Fj LFQbu`qH&īNA=g`*;{;;m><Ę'IhwG#<[FB⡜,lcG,4U:99K<395<!)k3KR3* %d hc+{B GAś:|<@`9R<̞|)" 'G˩EA|]a\,B ~Bw̽ D0tS4Ej4$+E82OL L5?# ,BIb\wtM[39LE#006MRsMxJNTMdjENq[x$:˨j Fs@j| לL (S۔?{l{|U,6HLPSQ恖۔0fƑLJI e79SH@(s P'J|M*M*O4DNiʝA@Dq9 %.!Q'EQC&ąU)m PIH2 =ImdS9u;/1 XP$ >Bh OTT2 % nټ"_Tj/9wJؔ<at$Ex':QL*ò-:%,4@$DD#L^P`> Ba;ɋoZ!=#TXmymD@3EQP#,vX-VɈ"=CIk "EFp!M*I8h9ƃ+hQNScR$74W~奏RW嵀ATXYKR7KٙM҉SZDq 7ţ YAԠ=)S_[UKڶ4K74P ֚%u@FaM̵pq7%V=*PijdԹiۦ,4M; ȲumM ^vQα`m5RYY\mmU^XوMPۀ ¨RP} r ڌY#pJ WEPEm;p3Xbu# 05˱%Xз]T8A%^F0KDWZI^y\^ X@CRU^^4Tr_ҽYш(躀` t:" Uۢ|tNyߋPCwVc ('0) DƦ3&3e"3ĕhB"{&d (9ʔ>T:bB #xV,B/ ɍ.!+AB}3k"-GD-E$N9#"20R,eiY49;U R tA6"^dmQW+E|c:b˺$!0vV@d*R̡4nfX[W"dJ((gc.d􎂠#\f赲t.T@Ieh\ ]ۡR@㧯З؁Gy t:(n-fFDfMq!}ƍ/;q+c9ѹXA.ehi w0  Bb^JH=&`OI窪ip| t6񕜊g@ߛGjЛ,6ךCN栉ngUzQrS"G,6UkҠ YfmI `f.9J.KpK.?v)5Lhu]u\y ^ظ?rl:lΎ+8Q nEg6jnT^ZG".Ž쩑gMgQnVFp2bL# '280 M:P)nVv4㡄URpYvc qDB n9z_ײɸr}:ʘѼA-"Ť Zh񰏸}||"ń мa%f-La]] A6zZinXtcM[Wr|F-Dቤx_T9AC9U`B_004ҤKl8"R{pjYCo^Bf*=I$rH9HI'$ZRF\q#qXKش + yTW+EDi57R~$eULY`X Y$mHR "zE eDԆPbW~|xDMZh7j(_RI虉SY [ .Xhm\BQ@ޤCՐQ1cqmQ*U'E b-lPJ Bf)i[bP !DjB2֑m9/T bũrڧuiEdjw# 41y,t~qy%e$GL:h^J9FUd_I0Ar1RShѾ Ij Ժ.P :3wV-o_aV0W 4l tW@prZv*YFu*f"~ Bv 6O \qVuL7压WWz0յ$Y:ցIJx[`1]W gUD#}m.`@lf4I03G>p3"bfJjfm- TXNݸVd06I,k [J%Rt[x]Qbjs6?AȎbV&/B}QJyE I0A,^V,>EoWQ`><'92'Ag^AhE{l3HSc&y+R+ fL@qH!lmuB#)aFPS*KbWB3NjfQJ(<@XIyNÛcDŽX"zw|QٯL}z1[8(5"1s'GC>C^Q•UƔAy LE ÉA LcdGMZDx[8^yL ̘ ٙ݅*r9-Zj @"))UKMFj[1&_"%c}T@Q%1c*cx#\=c9ڟ7#;;#<&#=֣=#>>̈́|Bo>A$B&B.$C6$BC:$ERD&$D&DfE.DV$HdG@I$JJIf$KdC(M҃=ԤMMM"N$OePރO=$MeN$Qd=TF%TCTRTʃ=J^UʃUveUJ%XFeV¤J>C4%[FC1[e0e1hQB$=e[^]$=`^%\e6CF&,&c6c:0L*cVe^&DYL6p6|fh&ihf64g&kkin_e4mfk]$=lgrppgpn1܃VsgSnfHd;Xifvjg5޵C6lxr<$C2` &h4'FJ/K&\ (4/C"86i4*d9`C:A>HyC;uz(1+H)+T1 C.*LC64(zJ(0Z05 04(5>C5TC63D0B6*<1C3hꃏJC5 %@&(/(A B,6jd25<`$-TB8Cڂ ,A50\$Pj/\+0'B! B6:A*3+*0<+L5H2jf+++d*gxFwrhi*D*+\BP l(@,h5h)Ԁ p@  (p P5 -(Ý֪A%'987)> |'&(J.3Pj-'46k.`'(C.`B*+44,+()x^þFk5BþB.<*j15"$H%tL |@(Ch,,nC)DA! @H%@ ,- @1o%@<vZՎb:|B )؂9h>C B$:8A>t00 iE>r*H*066nhJbün6ph-3*4ҩn4Dn1x&\P *&l,5fd C&l P@, G%@AhhC$\C B-f 9LC=Z8y8unglg; 'Glv1'7&&1h6I. 2_!Sd?IZ'v2lq$&/#O!%Kr&f%'t2p&2H&C6*)gB.,˲β52ƒAƲ-2.(r/r- !C[e_%1e0;$3e4K3]FC6;6\R+W'd;h:;6s;3<>3;;3<$>3>s>$>C"@A2tB42#d> BB/4C4BctFctB>8#$ZtC-I7$HJ(ΒK4LǴL;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentPaintPaste.gif0000644000076500000240000003673610534177571024574 0ustar paulystaffGIF89a+++55488H;;R(I)+J1&Y&1T1-`-/bcwBA rѡ˟=w+>k RL@Kbkn6Q0nC 6\r(w(]} v܈L0nІv\b(.%kLP)J $KRUiP ,)[n5̦pT kb_+#"Døu) );hoM`&m}'dȉkayYЕ`>\ Zˍr Px^\ؚf 6rݜfÉva c s#okqh}]5줯딥$`; 0"X@i]< K\@Ҧ;ʆ U+)*ac'؋BJqܮ)jzs]I` oJJnxn3b0[*xi*t2X8bJ'(|'6)l0',< ](7Bb`F&:uxɗ]I\܌w*q2'Dm̤~|fn´0lRwwhywK\v6\V ilv"y(1Ӟ|gߟɭD-!]cr٪^X~ XRC:m3hR)'5؀n1 t6XqT=ˠT?9YU($TD<CoN&EQ`c25tG3yXz\}V0*[HpJ ]CQK tM &#C0@!QsšùBBqF9ܚ ֱV$Kq,1 31p,,Xv?6Ipe"X*pO-5A>1ĥFѯ 0T-HQ$,j2f,(=nQӛ<-<.5KHD{ӿt[sl% bP,9Fw#^ nLͦLͶ)8%.h"əad":EW!-H*,}Qh23 L;D<׊W@`)H HH*$DAvTtb\*bpƅ0N9g'H'Q >%f:S  59N6a`2! @X!,"ۇ=zW"cǻ|pG4!XA:;Tڶ3fxF Q9aˑPQf(( ZĂYU3 1-.hZ S*.P”x3D͘Pu˃P䙐`}mZa ]@ \t{nt xv-4ѓL (PתWf? !4ggc9>(2l W(0KjvYx%Y|([aIB|t\m9UxCFQ.T&!caYrȩ &ͪ?& aa]<-jK=iuR_3GLVAGg9Bt,Rda `lbc3Xa ,mBjșcWwC2I"Ͳ]a5o&E8,/E*P7Gv@\0CVt7I2VqHo?T),){ՒcuiLh679| ]pdOZ`Wu0V/b#ץk7Bz5q5HBђ(e>aVpyG;r#al#e[``y0787!Um 00R\FA('~~"L$E@ 4ww|{nEi73#a gz#~[up%xxxXBPpuB3rhAD[Dzz^ EvNt3(V4 `w^xe~puQos9' P{'lAqu?@Bd4$r|b†Q IuE'{#X*0(p}(t)R^'ub[l@dHER}!0w{2Wd-4JlX*Bei^J8[ }pk' _Zҧk ؃KvOvQW37S)MODk52PeZ eZ[&W.G+k4`0 4k7P,Bs']0 ?I ` @" z7{G\ABZi7RI j Ӕ  @ m@ B6+16&jI-s,MrE04`0PBCE@BpF`9 ɐ' ԑ 00 {` s L ɀ 0 h`U0KpP?D`Y0 ` 9Ȱ @ jYICIy \ƘIirVU>!5T/w1'zJk@cVO^Tvl+8@ 0iqp9wT̃Ax VPazr zjW'hl#^!,#K8Z#¸Z+3 p7 B 9MqS 0XU@ `UVP|Kh73*wiN3V;V7#,^2 8:p%IM _'6IBx t=P>V|6 C";eU;k.s[%;`»Ez|"i kUX`"A  *=/* [% ;ϪY*'D7NW3;kŀzڨSG Md6r|+^w%dfHqŏ+3P1`c >˱TkEZAA9cL gʷsQz=Qnn3aǨ)-@={!밆[k`Ӏ5P} r(ٺ|hnJTk+9\@ - {"^r[Dt9WhlAz0KH&\0@Ǩ<ecWoB#bx4#̀<0΍[[)v~q<݃,͢_˼^C npNpA b|+kVfH(5jZ&% wiFшSJIHpɜ'rb` pA\v$ ƃʭK3z8<=k2ҬhC e=򫪴@eI*OAXṔ 8ȷ-=fLr8^ lKA֜ 1+\/#|"< }9 w@ o7pPd[K`"6Po/6( cxh^٭LȖ&"Ùs"biP= XQ"V]g1JsA0>A =3ܣȿ2=>1tP z Q)0\JގqH fzA}ߗg$Xٹ|ɫ(2̳_m oA E `K&HDca`& ]iHa>\7,=m7rR$Q7bڮ}Y%"c[Pk0x `KAb4Xn:U/!lwO/>S6* :"$޽Xzt` t`nP~q#y>W'f@>` N)gH%.K\Zʵ 9Fk `Ax7`6k@ 53a(‹$ЉУkŽq׭q .F('  e4b1MD31C^Ŀz]k\%hgUBd'"Ð]n^KupՅWanDm@}f#|l` VÐd2s 3as+[?(!$M<~|0d_f|vjMpR0 yƉV^/:叮)[wY_B$2*J ` @ @ Pkk(@v$JV&bMGY@/\#%.n'4]kBvޝ Ґ ~5Ѐ B6Ka2 c;PQ:c&Q4(n9M2 n27?f  $8";y%"#MⰇ[aưy!ͻGc(-PƨQsf͉uD"T\hcI.eڔiPNt i}'n6P-Ztԥi *,8Yķn9, C#eQ . %ěp8F_^=2 4kyլE7oY`PڹtB[6bA_wa# /%%M6⸉;)wر_¨<:Ko׮)(ѣP֨O*xBBN"uTS/IKb.˲pPLQ-ݛ&)"v:h%,|!Ni"PdD#Ȕb`CѲBw+C|Sxkk'lۑY2o Sh(x#-*Sw9b7,@J˔İI #!^:h<,b4Hm`8xu!/+H$MT.0+Afu QڀT(mښcVP  `ɮj-q1KOԵ) 1I(m$yB6i&cloN&Bnϧ{wwSiBwCvs8L!07knˆ!r!b.@-#Þ{k[Sq*8N ynm7iaMN1޴ #nJRQ7 #3:2i qm`>Yk)"&Op1NXlX!Y.u#~;jܱ5Bmg%|9Zfl7j|"!VEZxCb- Qc0tmpM?j0{7HR4AL H/}uS[8֬&{yJą-l R1h8 m{25 -(D`Fa.m o*ȲNxy N.|ߗ1"H.c^R8 /hb(7pY!>]C_||-h)4E+9cI:&pb cBvpN`CY"KX-ڭJBy(8vTcQ!4}_FC ` _CefjpgvFH§A 1qz@s 2> >.w [vK*8L5HҎhAM~fN.7׽op8iet)Ӝӥ#۷Ne#0!  #21^Q.K??1t$B q_͙'@)$P 1sJ`AN $# RU&8lj ,X#O()` dM=>I+lJj/+O6 SP59e֠b[] daF1qQ㎦>w9讵-y(xؽThx-&"Z˲ 8AYI+-Wq @ xC)F^D3 Tӊ9 **ÀR:9 h($ (C--k`73C]3_ђqD8xxC%Ì {i@<, !*Eȳԏ6 `zcJ*v;8R(4AqQC` Dy*؋p${00#8 16w K>M;L *xpD|@ޑOO{-(Gȍ(4=0:+BȒb0 LJ=e\"#XIwkڌײ;4 AJ'S1CFc%s9 v17 QIeFCRȼ5{dї2\M528MRf]cbhWj[z?3pdG!B.d:=TؖÙ>3eB6|< !;IHg4 :](]8.T(hDO~0L\u֕] )v@ipة; Lk$830ޒd#LCjN5F^gXg MbF.4VcTNvqk2"ȎXTmBߟl3nd&AC6nv2UXbiHK*%m$zGVVow+E ^\Z #cOMN29bN_ nAޭf ;RPH)^ omX1bpվ15R/?ɐn$;R_ 'n7:i&&xyS8w:6fmIC:]X€W6=ƞ?dip)1 $5i)Oϫ`y2Lt=&9ӞG1qXOh'|Fbd֢)rf=M..P|LvjP;GȝI6 v,U(ՇrtśVHP+l?($=WfWwֱG'|7o!2 Pes 4(G M ̄L.yعP >4}>h„ D'N T5ja"G1ShcC%RGqja#O0``tK>kIT*%~LqXǏ RYsEHFײ%xP!By 8nETHՒ&{LEfКSis>ֈQ' E̙SΚUҧt&hSdgߺpn]l⏳jˠ9Q Z1`lQ\zP'8dT~z(eEFiPinDp\P}G)2-sYmJg6aMd6P>1Pൄ Fl{\G[ѧ^U) @m%oph׏la ]zV CSP]y%xф b1wBP!'iR9$YmhC Peg^9ERdo 5k(Vp^edDQKiW1PDqaȦPE(t7"kB f՚Q`Fěo +EG~{D+*%|gbf%)'K%].5 X3ᩣBQBiYN6⢥Kp`}9(VBƓU\Хx!~m҆fzY=ڊW8d}^Rt/AԯQdHR hB*M0VRO%Fq(hu?={u0XM*gQaHSƣg1-e1()ɕP*nikw@酾-BRK%19A'a0 &0ᤵ*)rźd6'E&VK[6:wW`22txY;Yb=jB;te!V]K:0@X `mwм݉n)q ܎ :,8[dD~TP HRit*8lF}Je(|T$@&CD5`GKHZ:ɤ],$j20%uwQkX"<q"(c9SYC̍*"W*L{ɬeQɁi&z/g9y#$ACg ʺu܅p sA<]׾%A7 %/@O :!̮B"JEc,0Ju }pp:nL;O1҆;~ad ^N ̓s=0 rY`QRfÝ|iy־?q Q狣(Tʼn`@E2;AV ס&ODS>t9@L1i;wZ2 |0pKp[^M` %TOF#;DOgZZZF*Y-w+9ˠ!ᬿ8TNS>9\8/mT,jPE#F&+bLJpb݈zIs(ZXBU+ Ik/5Z =O qynҖtX #:E+L rEix+Fvu> WX ied"0yN jDaIЃXC`* ~T$|:_wwS9 h>fO h|ynbuT̠vQT)@MXjd@ ']]ᐈ=M)09} 5uQىI\@\5seviؽvP ʕ "Tx޽y4Y 6N J `ى4\C:%!!!ơ!a  x!  "!!""&"".#>#6 $V6% "&f"n"!J"'~" b"(~`)^" R=+",>"++,,b+"+=," 222.3. "y 51-b6,!)(!z`=:::^C79<;;#>;N>787@d@6|9'z"!c>F$>1Fb' *>9tG~$Hd9|CG$JHFKzd9CI7C9MޤMN$O*F8aKQdLdE.Rn42%SRChC8U^%V5TCV~%V^C=6x%Xe9lC9Y=#?TV[f%OZ %__ eFj@~C8C8 daf@&f8@V=%@b)&aa^aa&dN%A5eR&7d1Tc~d^=W9xlCmf9Tl&\Υ&%YbAfffgCj25Tqga~VΥ80C%;":\fC`/$C)-x{fB <. X<86`C8f+T!-B x|>,H8gtynC! 6Χ (/z^laa^1((lCoʥC.`5-$v3|h8&6Ԃ+i.X-7g"<39x&(t>C\B :@8TSb?6.,B A %Ԃ tÐ҃d65@) B B& A @ p8 f8" $'0X!@3'kN67CËh1C1Cm$p8j6Lj0@')*$C1A'HB ꝶA&!H*jE$h"U'<B:t0 > 0d h-(TJ(t)C<@<'DBJm5$5, <@ @ptm,'. @ TC`$B-XB7Ckn"g9~47 ͂f\27Ɩ/('H,$/P(1;|&+t!Pi1 |18%lm$XD X"d83CkiMڦ*`3- tC*,&V%N֯NvNB7oZr//үOdae^P6'p#0dom )UKF0Jp8p j=xgpNdBePn:0 9eP_vxVcFҫ@~6'@^6?2@J`5hGo;l椊O*1S~C$$1s#]¯ 46\qk!!#2"kP2 #5*%_2&g'2(+('c1C%qS"r_/:d5p-+Ӳ+Dz:e031s@;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentPaintSelectSome.gif0000644000076500000240000003737210534177571025560 0ustar paulystaffGIF89a00+++++3/5/54477Li+D+'o'>cwQ00h//MMECYX8BD}%S6dܾm -{w,b(SX ֍Y~,q<90ցiϘڕ[a'}&J)LpZƞ)e1D#Yaq1tpX`qg m]wK,Zgs%@r<ԣA pop}1#Lvֽx{ŠM)匷`IXZK/alݜfmf杝)"t<_Yi"RC ӏF\ۋ/t)|f)ݖNWiЦA %9stn~Azݖ]֖V̜Fnl-hmc1c[|^r^h xR+*0+.K#bIghpL'go'k%+2dPZ #/b=hW߉ML0{*'mP26XwS{i ~ d/$i<o"Gқn8$2`[(^e u pw c1=-jkW,׼_V |]`D p2Rrë`ՉzFHv.0s6O#6`g?1)[pbӐ;H \Xƴhz_#='6'PK[2-;ǀ!1 1t6OL!$ʋTU$9E%*A3b$+IiqڷzPe..d,#0QRn̏(nӭ7,>p EBHb0LD*/x1wH'>zM./LkPJ&І S'h`&Q'/V`y4:)+{wEpq[~`#xi 1pAN0bэ87=%NpN0%Y!,9A+<äIqzt\ Fƻ J[P @ Vxʢ|9ޘ. B20C xV2AHyVhʢP,ޑPWnG lRW.,?CF17ΰK@:YpM^@5V3;9S6!@Y<94"qePER' &(! bla* ` ,N1י۱A͐B@hae'e;ᄫӵ6AhlĠ$[jhd0w ([K;<"ߜYL.,  ԅ-H’ ى" %2@>ډRz\hʔÏ6h4!|v} Lu*-)$Л 74F:`ۥvi0Ȍ$'~ԕWi<'9M/v$[f^&&Ha/WcXvnHz݌<[28^0E<ս+,zcR|͊N6!<4bCYѕ3YK^9>eh|V,-,a t"'BV'n+A 7[.NGҚT`Dᢓ3(]mJOH}~3ꭜ9 B<ȁ^dUp &M ,?Aa_.$}a P\qv#20|?}FZ0AW{>E "[ Kmrsb"Ch6`" p0urx%%C@' 2F$VvvL~."'6IQ35 {&vY@vI0_3h_[Cbaf8Q -H͂!UO+ }}Px/R8p^ PMxw8M@j'0 aG@yv9j^tYKR(2/}xO4hStg rC/<~A(w{nX7A Ӄ}̈]3}?dK72] hxs"0"4"=4]0jK,Q0HNU_A/[)ÌGaJ2"=7g3БXsƋ.E~)EPPw+ݑ4+N'kk\a~2DSZL;zwVa#B1QPQS@fYhiNNp)'n6#a:2K`#%]9~Cw‚?7I\'-[ Z 9yZPZp@2W7-!OwX0C+ nb-;9P2ǎD3Q'6Y$B)bXX1@$s!p='F +)b>eCD;Dm2puEY.Ę5@ L6Pك529ʑr#!,Ӵ+cmuiuzh82Oy3% +2/M2P AO$x|p3R=XS&'DC+)XIY. iŹay⏻7f"})93#.q  CJ32(>9ts"'BO^X膠^+~R˜)e!2^=.wA"$ 'P}8 (4C";-kǪH.ƀGğVقv42K[6Pz"|RR=Yr30Q4ApX{)&1o7??A;A9s~$<$z4p*0kj2$ |'=)"mtZ'~8yG;b>r&J0rd7pZ5|JO]Q:0Y4ט/j?7K*\^&x)r @r= tfQDګ8vGɮ,4h5 Rre"()%D%F/AOA  CefqZBfX4DUU?t+z;I,$/"t;*"ø[:+XY@ž#%gե˴I3*#&A1rM@6D Y\˹I9;$`eR-G!0 &5gSٶ+ 5?ҡl6q[#(4\wFCdwVLÈ}$"51Ibgځ<#W{pqQ0Hl?;SqiA}GypD(bdMlF,VrJPgqcK\*O[2$I./\CpD `R$dzk1Mj_e1$y|J0qxV#Ǫ$22 .%Epbq ,ߒ÷[?2A4(Cǝ+, vʧQShWbB@xkń a'Zk\56@y5Ô,Qͳ`P) D$wїb+L@5:tȊ5<B[A`AҪbQ|#e;I${͞ ך vPs <duA &3Z!Ok@a"<<uW6żIA $^; bT6 C}HS5 ]2;N LT-wy#'DY'ɲ)*:p̲ ֜@ ERrFfQwHC7JȻaV؇K{͒Kp,˴,΄qP|U&&4])5=\{s Ѓ\q!WY$Q"3"-%kpTŸ"H B8 }|+$'R#0! px4 !(K@a$A($V^:kَ>40AdF`߆GzPE| ހOMS< mqAr05]#~pr2s6`g l?C pxNJ"ֽr*]Q<BN$h=З$N Av!RL,p  Qm#Qt5L^'`d! 4TMVPrhq k 0SбX^G(#0?17tⶻ^#?!B4{s aXaT tj\ QHP@@d''hd!o?CY`d n ¿,e!3} (P_ۑJTL g!NS +5v2bŒy>"np'Y d]e6`bh"yA|@R:1Oϡo?Sg #/'Կ2{̌ot ěJ(-`<$oQXdC  t^w28NogF 4, .dؐ!#u%Fćp_5nlh!F !E2INDcY˖ͤI8kΔRԧ'dI&3x7s 'FA<铨[j*IgAcvUG!F|7"ed8A (S=&v̜]wvKId6Ùg,A5xhJlË]5vkk'{6.l'Dqտ%kɕY-S*#Z**ULzȢPDK,l %[Q)1CϹ[9$Ĥ.V" 1@ѥ]tH6VE-5MF24*cPJσ B;)0K44 s#ڒ>mH<3T84UIFSc9UzND2/ )BY4㲔4!I$ P7munJ T`;6;i%go ]ui&_-#%6^Տ[P,Z*M%;!fZ2%AMOf%wXa+!=ŗ Eǜ~QASZH>]y Bu0:X`3z ME1*CıDDNX1嘠O;Z< d*DUӕCnuUݢRG1Cl35xc2 %ˬUP"+Gm~⤑ ۢ[ ޫ*-%ֹ5JQ0$qC.+7g}b2Zh.~b7NxLGLH$s$3:fD4g@O"|,b4/!` ׮*"3mp9$*Ԛ$BKV7b>DTtbT %&xRP1HčJg@"5cفC ';'x"7bOu HO1،2@~$LǓN!e]G+Dw[Rgw( Nu}x>:"AkH@H7^dZe5?*{y( g7h[8(1N!F؀ cp`168c8)Nq >o@(@"F3d) ?7 (lMR)|6π\ X (l+iW%؛8 +D Bv3[){{ Ȏ "%%HD!$$C-S2)14! N`+ " Ӏ Y1")xfzc{ " AMJ;KCB 2zAQFRz*ڌx )f"> Ȁ+E41>! :< D,2阻ky $A[>a$:0r9¯R4t9d7Ro1ϣ>JJZ([E@"S ; GJ8įGK9` 7YƇ\dP:ӀQXW,n#d1 16'럖F,?PgɜL: 0{oD6U = 1\+! bt39ɜ˖DGɵdFálO@7VzQ0I!*wP+bѰS>y }=K62A]mONA,", {=k6=9 `>]Ry\\)- xQ9=M9M;Ei3-cPJE Q7($>=K >(Mb>AS+Y;SrF @T`ηQg D@%NYոoBGZB" lB.bbԚ $tQKu z<AU>,{Q"ՒH]#x$MAXà+c UO U[#MU QԓU k$\#gLpӑMV% {aO1UmW<"I\fLK8|[Zʅ0>ܽ u蟇Y8ZyUA]P ˫{=\DWQ(XU)LIۼ$=9ĚmZY"(EQ_1' S괋79M ۤ(WQVY!z[acYQB_Nϱ(M@/c+( J.#(a-^ڠZ-a\C)\4#l3Evd.b3Ec!0 &Z j!` %;EǗBsZ0Pd)S5v%1:fE F"0Fl›#:6U6U;='ee]Zn]l[ GU-ݴI ш"R> YF,x5r]xBM>1tΔQfj%\6@ Xfd=0dڅc<)aߘ2cNҔ(4(h~c?"TFh`wU8 =6<1nwhD~fVi&EmȒә߬l>Y 1(I orkk;iP j"x mNYcZud["K{2WFOLQ}]FXlݞ6u_~ ~SYkB@r%MY9Sj0Œ.%캾mcNYKmnnjQeUWnTVe̪ŎJ*7O$6 g4s4ABXxAyޤk.bc ͆h A>jv XWծ@2$&|k-iye:8B s ε," 7GD⊩Y*fjZM 1 # InRWfr멅rZN1g]٫P̓pa6,՚t$3X(mykZ&&xnpz lvo]pqoAf\KvQV'4fk;bXǍPo#8?*OQjO=DѨ5q+elEoF|w $|Fs1, 80*i^{ow!Qw]ȟ!AekhΠ@0v_Uq8hoxpQH9ިS)y" ܓ,l_ut9x?'#vG zzpqy֦Mq5믪Zk$zZpgh7d )_M Crc ;.]* ly@sᤂl휧ȰfC>{7w0xŌ#wl ";L8etgxkU,h #u *'p_'R+`LN68qPu˥UTI);e!YƈAC1l0m*ʼn111_Ɍ'ҙ['Ӛ:#GMpC{ǎ>$thɍ`UflGQ,Sr '<4C3Ӣ',PajѩG-hZiȜ)3ފYv,ۮkݶV]Yi/vսcwol)ϡ?UY{vחzΪϞ?ltUEM8Pk #]F$uW*mdimtIeq!]긣sY\ Yי{fWVu}2)]g]{7@pQ661HY$X,"G4Q(\ mxW[rI # ݭX~Hv˛ gG2%Tf pT 2,%|ҋ1e=IL'IVqT8fEݕCZikww9 ~ f$IdlwHF=&myݮ$rVdz5TPCUKU"g1bPz'$ 'h&Lem)=虉a`NܗeU WdI1ʰUa6uz'S{,UYʀiTp'HCv`#b چ,yB\$6!^c͡HF.!9bP1tA%&Q, o*jЪvI5A.'tgb٘ѩ+([٤F-(7@B/kf曃[ot"AR%-J!cPsR^9 ?koL}7IH|`A?$=b^dY{D,$Ys*W, FYwnWEqFb'5E0~: b3MTz8 pj a u"+*QJTtF P*!ڠg0M&/U{A*q$H2vn 2gr pS*QW:FŲtYvOz&>(D?OT P{tHLY79h2/UyL9 m磘v,HE;EX$;=xNA_DSiCb ZÕÍ-R~Ԕ  w›&f2J~~2o2= uT8Ѧ?z'Sk8 i0ƓAg'kU|ouȧ"TɌUX[-K)'7ѐ~ڣHE4b,UQVn}']U{O4X%gjA]bnǢ@$=ӢbT!c) 05 oX<55tH:_(YEEL6Y~Xs̶7K u2 hP [d:Pmb/N4*p䑐5hB03^!? GHGmV a8k.Jk,>+ $"[xq8+L6+@,\Cѝ'\zW|û V@!yzysddV˹O숤(PG-x:YO: (:TwS*7h)5DTsXPQ6&+".q|;2JPf>uQ#L)2ň"$+638@v=['BQ3Z}@](BNx:AG\sLLP ..4I,tnƳT ̛ͩ|ȇ/B:تSrOy8_- _#,߶LXUX|0`lɋQf5TF"P DX!ōyM5 )Y`!X&%R8\$D@@ 4(tDEYXlQ)Y{)6Ջq) S lZ兘z X}k_$>4' T`aN\ȇ ZP؀~l Ez ֎u=RDuMـ 6╢/ AX},ܝk~ry/Gʱ1a/m|)\3܏@}}5j@U0n_7NcB݃:/0B}3#=֣=Σ<ޣ>#?@>#=c?@ $A$BG^.D(1EVE^$FfFn$GvGrDzdH~$II$FCXH$H^dKdEdLfdHL$դL$L$ENM%QeEd>,=S6S>SE.%TT2UaRVRZ%Uf%=SJD>ZZYOjP^$U6WbeS%T%SJOVINC$=̂a3,$&bf4C)bN&eNf4NJfenb^fEC7d6dCh&ihf6d7_ePj&g&m>k~dLeQ%!>&8p'q7dT,'s27T=@=,gsVgp~C7`=l7tgwwxz7DFtRugub'f&|&O'K$DP7x~'zg4Qw'~FC=zgvg4D76g)n ~7hҪ@46p)74 FFC7 C4ij(\jdv7D.***43D,6 23B,Xõh_;2dB0$8HB7X;`A#0B= dO*^J&5D6dt,@*TFЀ06ꎶ6pC53' # xA A Xôhb $d@2+CzC5C60x0j)}_hHЮlfx4xA(Tm>5xҎ;A$%B/>$:94$¸%R论jt|A6HXX2T6p*m~V@CDB @Xh7T.7T 3#,'X`&%Fo~ C6DC0DC6jf(l'7T '˜AH`jA_;؂xA/,;B>âF1B>8C('FF*k#p\ #p#hx (z(HC2t+ \ `6Bhw0X(|C+.`mwVCvvCvk70@owfárQ"5tKCqw  q!A4.qwNoK6$ Ag ,:CR Dnr +hr*\u.,r-xBrx2/G61z'ܦ(3c$5T,s-Ks. +sG|Ƨ6ðgu73s83q/FjA+*Ko3kvFkMM۴RENP45t_5QtRGCkZcwL++A,9@>6T""k""p##IIWWBA>EERRCCSS_noXuUyXccukxxgguuBBXXFF]]FkOwgg{{ggm|uu ))/8::((;; (%%44KK[[DDQQffvvbbKKx:,/39LVOX\Rnxlukjy~lqoGBr^qǙƘԅ˔èơӧóĹԿǮ˺˝٪ƬҮ̮аƷ̳׸˷ŝƭȶȪͺֺˡӮ׺,H*\ȰÇ#JH1a3jȱǏ CIɓ(Sno˗0cʜI͛8sɳϟ@c窄H*]ʴӧPJJիXFE篫W -Qh+Umn ȝK}vh08p 8pVvE 7/b c&As _3,6P+4mЅTlJf6CÅ.(Dr;U[UBY3dzs_L!4?Cb%Yw,}dLw_t}7 v z@BdL+= m|FC BPn6]* ']t0D&`r +ޓr ,؈wa˂Tvgݽ }dYxP0cKU(fAХҊ*J+mCs[x+8s2J⇣tg )=GNب}a9*vZ^I9JkX1@4ڊtn &bqʹAuBr zt9(]$R%̰܉bsDҀ퇟~_vYjZ唪^꫻:ȥb諡ilyC²mBrwwr-1ߢJ)HR h0w a !{67jt. 3 3˶Czߗ@Ɣ n6¾ -Cz22t=BKm#*J)3<#_97q#*T.t}Hk-4b[ =qϢ{\qe/ޛ Ar@K.ws 2c :ɤݒ8#L2ޝ0*It│*苽944}yoXʝfsaל\.~=]!QO8 9׌kWtv#{= K2ۋhhh d"3`3IsRq}1̣!-hC` ]xcwuKQ~=~sGq>n+mzK˄;)ZQS::iMiCNssE9[}v 5PgЇ!3 ]lE U^&h+fBPXA'U 7GqgI _RE r@ǫ# ?,av7Fc^o~BqH*Pܚ BbNrjER YdV,e|Jd0Abg94Шf1J©g;~)&b|tj;gȣq7@@n"8Ku1AрRI.C":>Hd10Vjb!zj4MKkLE%s0w8՜hz%;r7:ylT&;dRjK.I%]2rI bץu*Ny@ZQC醒:#@_ji@iS51\Wdy*UW^;L#teb Tl.w@nV {®Ű k֮(7 ْ-U/cLxpX׍tInm͵Cn!ow}pkާ M(!#xЇ+8ȝ<+*HU/TW8+,0Ѷ&4Cji&:"I})Nm(O*hx.X -\0YU6M~FE3&8YT>@͆!p.\D\*vZc]Tj9ܨ>ЅV]gW:uzkGYrR;9 ,Ї|3 XJ;w# P^ڍy,dk"}ӛ>`y<xK뛩 `.=p~Oq_oy3 g^ U]/N3fC@g/$lw o(Vulrwz.[", d%ǝlxz^ =O~j- 8W4>/,TfҚ[w( +RS\v_Z~m$ȽP!cb14JUgsx!T`ݡ 3<~7VA'+3ezF'~10z 3n"z!0@S^XP T, Psx`@%O_%BKH%LWL0c}S$ ($74JPP akEQc= =X~fd,$V2 E6eT_L8dS6dK`58^o'Ƣ dhyr >p$h5Mp'CdNxN#"(0EdDfK[)dׂ3 5+XS2{XnWcYqleXM .J1,"!2geNxz;"DM\#zQ6rcC$|~`5t' ::x.|`P4[6GX j)ԁ2y%@D8 "dHt&*AHǢ`exRC3 W&B,5MgT6Yp\QK0"2gNI8uvA[B{w"OIN6P>. u6WZ.YX&-2jv{XLOঢ়Z"'fu _(IbI䑌`6"ԃC5Rn"II1QtGqE7aL.O(IG |p{jjs0AI> *fv8t(R@ @%`! B  k r! xOďv}mħ# 0R~x4>r(pĦm:ɦDw葻> o n# ΀ ΐ `@Ӑ n ЏЏ b! i5 %!JZp0ʬ p( @Ыzp V~ *UR K0!o,I2FRsbO^-di,REȠkO0ښb &L1zQ s@-L2t"! N;mg?f(AJ국bbǗ3kLj¥tq*CЁ ob( K6o, :| BuBRNÓ_,dOG4}}񱊕<^Zn 1PoRTp!  ">. D<Ը2j E2k;]H*D|p"WcPPaJ95&I\ߡCavG̀imz44 b@й" +Iif'i`jؐ;Pɲ زZXz(p'$#N?0 r''Āx ף l뀠1(t58Cy;v6!/IZPSKnQZr5Dit_ިe _=l![^BG^$f01Bh)A̱ eU4d\̖i2+&M` jK25˕*SB R'Bȁ5Y( ^1LGJc,ft!Q\']9Ҵ}6SC3 0!R&ULh`#k\˓ỞsNNfs֊5، "_33 gl͠0%P(sBB!р4XxΞXv˘F*ۼխr:8H\4X? aJD_mt</"Ҕ\Q-]=}ĐSoX pY:xq!3DM ˤֲ %G?40IA;:ۜ78U? <`wݦNj / RzMQjESMc'uGL?=0+,+Nq$(פdc4(T#PA6|AQI蘋pU$W$ 8v ?Sŭ !.NpaZ 0Z.9Gҡ1Jғ\؂S:LSg$,Tb%. }0‘bfnN6 oagqJp[8M̓|>+qR,qH2MY(~V&_x7xr|-$1 ՈS ض_yZ@އ FC?Cc|]u"O%Km5euþN=N] AreA5sGr &,.> ր_sO?ƞgA 췯A%N$" 100хT!CƬYf%Iley0[ƌrֳf\Æz,d%@Qj(#UUBYKtнt ˳dt q4/BXbэCz hfH*(M"KlM3eZK9=;hHPAhLHMV%en[M%Jp{W^ ga= [++Y>yؓV0êcF{z!KzL@S4Wʺ_f1,|3$ΠSA5 ꪬ^e;R%$2kiWziWnkFR@54`Dzf1r[P-{[) Z(9K4pNR>DFtSjr^фRࢍf! VS-> J=f.fzR7$KԒ"rI`ơhLTTqHZ 8')fXSR Z#oly^{Q 8@=2_Ym\ ATGML#Kv"x: M1Tm²^IEV+%d6l \`<Ĵed# r L3%j-2$[ ;*m;5%f-VWX >{[J~_` H/da42+l&b25&HƝӈ6HLėJݔ~rR1êBB`x3 ~Z %v:lXj0:boosteVl ;L_n=}KؠVM Ѧ]SuFߖJl~(1C}eOjv~ EWg+wdɾJJ .,bG-f[ޘNUE.~~+:PO'ry%뽢Kba(V=E4 ئ}—8 ik~4PEFĵ ymFd }Fa Uti(z籒*=YT| lJD %R+R9΋X6 pdlA C!#%_t^#i18HX,D0ZWKL)KU0ryŰ?R pX냐sGXIB08*O, ΀lHB=rRR )8”}3Gr;(_&U@/FTyChB`׽| ,iDtK&"y#Rt,ymxe|a1١Z困lq'ն a+'tVc&E@%iRaP D[f͘4(A8uG{̃=v2}y]z2\"X<5u!) Y!X+FB2Va3L&j3TcV\GOr6EՂPVzϒdT&'IfF~ҁF02ЇB4,:%=I*\@G^WG_Rj%(l%pN h%+(\)Q=!(Q2DMrAÒAv: B[=IrVժ**(VRHbF_^0ϭbJENK2Ү,a#ʌmٻ#;+𦲓4# |,Ա5V["&aU朡 M+rW FZsޖ\$E*?)f:FaEFK 4b9k^(a P 7G=&~cCT*@,.%LG?<(# /z34ر%sVQ+6^h~,@/n֘&@ *?1GSCT2#=`nu=JQAwNԺ2E艫af Y:%S,H(+[[8ؗM-- :eZkh0rCkPX[*U@2StNEWaQOJ$fqԀה W[ Е"! M9p1X@$UNŮRsSKmS #urD!PюYh(%""ZۯELNYM݋HV+"x"{e|ˢ /Hf 4 +& w"UϢ1E؀p-uX= 9i08!.`R}VU&,b}(8}:Z Zh+p<#S-S P闾ah䎩4qaR/sܫв!afj>9Δf&g5$>j(;;2iBH!H/҄IhXQ^(yt`RF"NО۷H6fnɞY3 8$%_aY[6*œ)= #sIOCap =]˶V,4_4N?mIXȌ f(50Sٰ`q`$-3x!@)*3@2119Ko⤡ ){~a=UޢwT[/䚲\:n90&[ˊd^y{xv3QPg.9|,^*Ε${OkldR4sĠTCRD jHhFF3,I(Ml,[lD9ReJv'П-JT~Jmh*dzE0$5e2f&W kfɠ$HPcBBŹj*%iҖԗgt&O'?j<2' VZ]5l2زcҖUXr;]4b"fYS`b5!~~3Ο,fܹxkqYouYhco-_أk& )VHdzBIfJh͢-eR~VKDe +1lg%c}3K1 10dqș6d]!IE$I.&SaJE~]i("ebvTR!b9Y*7J^EC* |$%2ə4fdJX]@P QDsRA镅4+W~PxvʓGJL9a[%C ZTe^'BET()*Zt01Lv^DZvb!y)-P=~eCBk*fY&|IA ! \ži{ UFڤ^dumOZ݇]N T%+\,'h譒j%xB ܫz&0_ضZ-c:Aw9OQ5_o*`1ْ&x#%9nEjC#!JcTMyb+\@ZE0 N(I;Lcv~ Cf5>/OE3 \xW*7Oo7<} ֜Kꫴx1] &?HZGn*Ak,tk>ZsW=(iX*w\[0诅/f)$ foVb1/nu6JB2{[ă`{YGq"yjЬB0syfb@ua FHM*D4V >[MgZҁ 8KUD+~ZAmC`f: XCBW8<,R0XegL ;zE~DZDD@HBV$h. 敱tq|f-3b}Gs#ҼEGI2%%Y$I J@  <SIQ/s(43#G*,MΐzaF`A#iZͪ&X )HNﱯ}@ʞP@ĺ$;H9j^FNh54J6Trs!`L+obfAI:aB.9? H>#tT#"G~, 36l|'C Ʋ2G(&3LtLU&*0PO3U9 ,xg.8p/hpLJ"E[z7&؏B4©mM< @ѯZ_N/Msӄ!4-ơn[K X Q.6W#1 DAdS"U[,DVO:*"W˥\Q㮰~[+ H u 'ܺjVDzN74 Bԯ&[\W8lʡw6BPˬr /3@a R4DL^p0DrS*l"5j:rQliMڰngS@-^oE Vbb nU70E0pHH[K]iFLH@Y52u+dah:99fV ӘT]k> ZΡ :[M̀T_x5Y5b%ro"-]'r DzGHC,Y[""2oU'o]~4궑h* ݘ1Eˈ#B_n/XSf`Ijm,>L"~BDaݔ {0gUy%_D{H/KyB.ˈqV`h[W!P${[f+6Qキ f7$#?'}esN)5xjn&uY/߽\O .>: O nEV \TN,Y-yd_ pWؚ$'? :p"LX@ӝ`L Țp4č (FDP`$LB ɸIYȌXmə]_ @*>>C*a)^[uPC@"9L`pxd#$8ƨXRaAF\y_pQ>0-2>C+b 5!z" TpО X}q0mID}#x$CxE ]F ]ExAqTpړ;üg !Z)@p +FDh@4d(@J<#"!TQV%H$ SDr! t`a>CM.#PQ5Yq@% _EG*< !> @M ӽbQjb ,TC`]$dR!yQl@01h/zY2$+#\СGLQc.$ @Q"j+ S$@ tN]b_E*"_}b@ߔF5dQ!djM!O @Y&p$@e[ښ z\H%Y@fqp!u@fung8 ef><@'dHzgvr'z,?j@f>wBgwCf1RAf&.(6h>FN(N(>h.("@vh^h^(vKBܤ(!(֨h!h(ʨv ڨh!&ih2)fJ)>>))i?Ú֩i^>)Þi)R)d>6;0ã.6*Jڃ>$*>M8d8x8x8jCj6xéꭦ*C***C68*:+z"j.l+v~+v+6鵖)"騂9kƫ>(>x+9kk+ʫ+k"쾶Z.^,fn,ƒ8zj똊=+,ˮ,8MClˆCC8Ьζ8lɊ6,&-bC=ĢjφCՒb-9CΆ8C8Ԏ8`CR-ڒ8|֒7>8Xmzݎ٪Dz+z,mn-b-݂8p-Vbk64j.5f*pó>+z5*;C<h970nl"2(7C W*7.ܒC62,ް2 .fn pbq C.؎8p"12X­Ӳpn>.Cl2'<Bp7Pp<'4:Ȁ,<$;:6@'¨Zo88 7h7,C&olo5q^1Β*oC"(M-.9/;qG..B>lA 67A-HfdlB8B Atm<(HC/X>z1'>2 .hR!2K2G2T =(,c7.*D.30 7T2O8T67q#Jv-690A @7 5288Q'56<0D6Tp7CB \C;4M.(;*E?0ll޵ҺO4ϖ7nZ+F6dOdW2m!3vuf8F?veϫdh?_d6iSkk8\K:ok,f* ΨC^r/r484-/s3_7r-.tw7v./w7usuzRk*{÷{7+}S5lCj7ck88 wv'8=8va?8cC8xw/Wn8j86x86.53gwfw.~K)Nsgfsv쎛qPM' 9o'Ny/9O09(;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentResult.gif0000644000076500000240000006321710534177571023774 0ustar paulystaffGIF89a .##,+*)--45*+666Lc"U77C:FHQ M+-n j-0PGGHIKUHWXXBCSRKWWWOOeO_`N[sXYe[[rZh^O`cWhiYjv^pk\psfTQpR`hf[hhhkkveujexypn`srhwww'29+0=9>Y QTcj.Y4h'xNfimi{gunyyp|D[MtGzaWnuj|v}2lnouvyz|~URqf|w "$ 6jȴӧPJJիXj5+9J~ۃٲhϪMv۶|% er2aС?~Ç|ۅ&%|-Dc%-/U,KL*C9I %C[~4 Ljjml 52]ƨS&6nldF!ZL)K]| RG[%kŋe&ۋ0tDK,Ma8у_-]dL\xa-1^r!_,b]Qx؃]z㎾Ba_ȓu3vvD .LjTTK/.KD-Vd b\W-_  x }@E0-K-".N@^d:f/in:ܐ5>zO߀߄'O,$.x+~'OވK8WyL>訟z꬯OzO{VАC;/<>|>/=/<|*gwسoN)Bp0oן+IQ Z# +gq0 6A{tpyFHp+` @ Y0@"GXH#@d#!*qCa B' # ᕢ@6pcR wQ K=Z F`( ъx QG£801e UBA",Q "h*.ъVdP0" @p֯ f-- K; eaي ȑu/ Nr'- iڲ= ZZ(rX¯h0DH_ |goyWvV`i|Ҹ {"FJP|u%KK:H",ZV0 6( 5.mEFإ'Yfʺ0Ng|1Yc~Yto7mBCYň@Cp±0-\fpB%e WYiNqgfΣ "Q^4z^N01GʘxV|+PA/뇄~Ѓp i`uLE髵6}8Xyı=1A8¨1Q uc xk'0u+4'/ p% {Ӯ`){ XA,l1]x#ؼx\@E(xԢ0A ^B'mNM c2B 2W-Gp^ 2Ȼ t6]P|NpYGOғ>GITUt @(yDۀlƴ") n[P*<{5knzPt#;Ο~v㏇;رz8oYL($nxwmPdv2P:K;g Pd&?k7byz0␂0,-.1X45(mZ.Dt]Qt{" {6B1p0jC\\JF { dD/t]on]Lg}$؆$}0 p wth{8 080@ %GC4`uCXlN8n{wh* >&o]Cs~e Bgf]lm2x Ӱ ͠0 h ( ЌΘ@mF#HC hnn nk1 C(D` ` HpD؍*FgyyH& Ͱ  p )ϰ ˠ @ P0 @ cU6[D`>DQГ<hmm n0&uu8䓴 *[ݸJU}N#hV̀y͐ OW pP ӀA_yC{WR=`P h :)D SWLL, `y} y2. X WP O liԐ UEXXmFhHwXyEv QZ0\K&x0 bLx"DL[UXٍb P :$9)FJXJh%jbD&v({GP m) ǠXi9' U&9 '', _D]GWW\T )n{0 nVi1IKvitE:dWJlUL `ĕnix7Li P7\JL*4;D{H`H {c倛$ 4*&@'0l 2 'I  Az_Q`tEQtE$xzФ6FjoeGQ T)x|uKLJbzCBTL>9RWo9WDiEx`8j6\"RC"fH {C:Cne Ϙ pЯU /* PXpΐdBp*GQ }pȫ`0Df1pWTYK9ga:|ԁF}jFUtLF:L4keeWKk*do%URxr'J"xxUr' ` Ґ') S@В+1&GU@ `?K#UaP DH띉гHmU"P>dJ(LN v&xnL;LeVK {:B *`k'X ̐ Ȗ˒(p[0 x)쀷ǜlT ah]\Uf~{*DЮLuLC[^E*R}TƔ /ǧ=CX gi{i7dmʴhTz`+ #9 s--, j ΀:<CGJ{ |}T 9ФB>Dz*x[H;x'kK$Ϊf5"KKV`  iSI@ xUۥ[4"f2EEm|(l9<ɓ,ɔ|ɗLvF`ICW@ #k,eQw Ze;GR\~!EQj04?'o!sXxPL<<G0behİǴM 5"xI\K agoC}XK}rH@|PvZJKpEx4kL{zKW`DPFfD[‡ʡ&Z( LAhh*u4>K+a P?4DU$ѳ`wܧlgl%XaMi+?Qr|̴c襎LYPJ=, &0YM]P}@(rrGh{\ii6xP z1s qs`LLujXpFr]lyELfqSMj'{ (xmKuIyCʞteD Oڧ]B_VuŤ޶]ʴФI1DQY|ia^2}-j @dph0smwj(is]i#w h{PhBO^w猛R$ m~eyp4`R6RY||  *`]&`"p@VVˮ t'* ?W gp('g`@GJTiJdPC>i{$R[0ڛ'p ɐKړl,TG+h奙i˥L PH+.n0ęڥ7gңb3(F=ggm&a ?xxj`L&{ZKZ [H &:iX.7j~>ɫ4ӕDtdSWJ` U*{ 6_:pILPTf[݅6gPxNng$j1KPP݇^I[GfJR%u, ZP~NM-l\j 0ѭgC'xM ؠeǥih` 9WZ3-E}0 dmĠCJf {Q0 t%Qu&:O XNLSk[r`ƴ6M`YK]G $X G KZ LPGM=!E3RIEZъYVze)F+Tre ZU+D̄&D H#K!Ji/F+ҭ;lڴeRL6ٹuݛo_|*߷ rQI_VtIr9Z,[~r7WPʕfXE͝S%BdA >\"  88)MǑɓ J幥X+;*+XX#lR܌eʪ0T-{6-u9fPQ|WR.$D`!: Bb$7TL$D2@"D T$ $@B1 -xsG/J'evg/wS< uxVgǃTHji&G|)_ |(d$ZoX-GÊ:2=v7hLV@(Bb.+ϑV\j%B Es:IaQxٴxqav(8-~1vU'g ɕW^ 1D=0_L3`YR,)DGj+ʄ:BN9 ijXIz?C(~*xKj2TD"bb;`QGp< r'|VYhVL$3q%_NE.E@L3bZQubfc$6{Vk%5[[?B8czl-*^2L+"kj,TXDBd{oB$!Dd\5zK<3#:Tfg6Iϼ" n,*FAx!!^Po<t%3CpD ,W -gF  Vҁ1Sz&eZEcB8F/) Bf"uwX6!2LT'G"ԍ,CFwPG<=ܸhlfs } l`x}0}0qv87#_ c{E`j1U+@e) ֲD˄NllsZ,c$] JI$ @APFs +%:bUf0FRr , 2h&5d0Xr(LejS!'yCUrgT$G<hا\|w bD @ZG>ե<PgC=4'Y[S_0RbS "ьU|[!J< N˗w^'|AS`U}ft.).`(\sHpγVv;Kކm*~opCZ+^鍎[F@0P -b]s7:_:q}g_~!xYVzIզ+ d^XJw=&j%+wbJT A87X zʽL)hv E)@ "Ktk@o>an>uASZP nGPYRjҢ3 Pv `Q!=AR20D$N+@:7pz@c)%l )@raFxyUbFx>1 q>zЇQahJK@@@F0z@uWTDz> *@@B `83#<# v!#ERH=VzN{+5Ҋ7`5X6 0 C$3 "D1"@ KG@3@s< Ǹw>sDkUkY@aJPJU\X,ga `!#+yEZ@r;ۣP UxS H A`#2H5aS ̈: h 8Dd @3H%ryĄLsEWܾa@KX{@ڀɣXod$ 80rx) @C 0-ȄDt +@A 0 Mblh++XLU)LW#`lDJ,"MDIT"! kܑÇQJB$2r`VHF$p7~#xٽ ;pdiO 8:P0L4BѠ%+!z K!B! R#-R"E!] SYL)XJ#OIW "XT;,ZhrCszuB(H!|Zh﹏̐˰*XG3»q+p GUXPS5ռTA @—yHH `VZ@tO @[tS= , D XK F("ݻ*xwtL`"Rc:sL4Lr*:; O YՃ%u@'-J:M35S BО,Y@UHQ[9P FiyYS_LKFf\r8 p % D*ZK)Kђ܊ұ3[0Lˀq90UQyz!DPKx)Fx]U&6ʤٿy>Zuᦞ5`xisexb7 |b(xx3Dzu;Bt Ļ tnv0wXZ^՗ü;'wS.maq$D1+ZXhY&hNꐊ/\.qd_{dRd}@6oͬ| E@-w u@Vp d}GnU&XM)$ ҪKPRtVZl*V0l!&G  @džqJp#TAVS`+K=j#DJY`diC 8 Jj 1! p % q沛]ww/`~ke -Xsd)W%P1 !jUX*!섙0(V,E :cʑ&A ^q܊WhIFAԕm&QBh=*VBaY 0\8B-[nu]_z!N=Xm!JBZdy<%$R  HȈ"D9VЇQ UmB GQD(m +J6"A;]Zj=Me XXBr뭇ĔSrp cG=mO:u_zU``xJ>KPBJ%e+؈Rm PAti f+LP]:ܐRYa' -٩vOzn@Sif9 fZ!$W9P (D : c!f.ވ% RH&3H*:kr֏# @AtVz@g|s L,DUL Jɤ@yzb` p] r5aFX*`#2Cd "#pP(0 ChHme#bveB]v6;#:嗾w=0D&gvle\!"b&bsQ#kRT&:+<9+Z%W`+hh\OIT6&Lq#E!t_^gUP)PƀbBNȹ<<hP[檇pk D"O6`Զ : >3D$BFN"G+fyG 65.Pl`;#eum(\ 0˜fPȖ%,`Q<#7<#ìC@ぢ"Ftn(YVDB!!U)RX1"(Ŋұȇf#72*! Du\] T A$gE``AwB4HػV|s_Jh.&u[}\HG!(bsT= aE2|o#DݐBU,9ȹW^:b)rJ歈GCk2tXWD勢r\8q!a +X"fpu*'#dr)D=]@7 m(PA`Qqԥ25KUjS*UB5EFRDܘ`2B}SU!Щi?*`r;!W`H=O*IR@ǃTsc:B@lc1;ͦCVg$Ȉc¢,~Xbq'gfpFTJoXS,rx.t+Rm*OBmumΊeE:_ zHԎc|++kXbFfL.Sط4ͽˏ/npabB3**e R;PإJ` ]erQہ>Mh .0> G np\`UhmHm Rx3 P,XřՇ]r_zĴމvqxeDo~%n\j/Z|3?̏ #h9q~:$̏nlYגD.SHXr !-Ƥ I|+ $ *"c`)HяNy1KQ@+x ~A 0 =ZԂڿ-q^ܥp·Q "n* y{r!ꠠ<=E1²fQB.6 67Q&DE1l70goXh%jQi f+c4*FXCҷ*RO3:|Ggwqq1#pIJ \aD$5YP(-AB~G7ʱ gmŁ["^LoڕN!Ƌ['>78? [&ў5~8p݃C} q?~m(!?ciG_Ϸ_w>O?' KRCر~_BUð__ E.=`#B*[=XD< ! ]D` `=CsCs9Z<$sك5:=ڹs!aEX-!t!a &*%0b4#8pEC]ٯmb]pfY\ Cdq0wda0bi0 \t3 p ǰ0 o;8 gb0gp sp 0 _ǰUB9\V{0\<ºB9pe*C6,knC=)L b Cqr@? )]C 2 ! '2>C*dB*`1+C,@PqQ`9  ()2(r)r+r*_&\r/^-[Ӧ2132'2stp\/%&]/B:238UM-/._sCm!8]A=߳=3>>s=3;O351;[Ӏ6:44<@DCDOtE? 4&XKՌXY/1WJ~yH4\ y1YXY4^ŴWNL K__XD:HUgUc5-^/tCU"UV"w$Q|G8%Qΰ@+L }`H|Hxq#r΂K=Mj B*(>(.C[Q/m9 |P4LzBw1` 9:O6x\;P|9 6D.-C;K?@׵_ű_]•m%QPsQxmw*\Bl` H Mwi LsL#rq:r 2 #2"+ 2#½FsK3r./^M͘{Ӕ#cxkƥ ld ǽWY?T̅|:‹ؾ#4[7#(u ?\7ō Bw:d̠? ~$BAxHj+POF`w4S#z';2Dr,XѺu @D)VxcƉhcRTpX1 !A"F=T7ulfC @ţXbz4Vz5+ZjʕkrQGzfzb\qa^:";X.@Z <c0=p )WrfHbJD^2zd#8,3g&Ak ŠDTU>BB lKl[K,B{uS #ˌYWTJxM|_3%Z%$DRIפckt*5(H p bx.eD8$V@,`ĭKV󉬷.a;~ G!c![2#`?Q!hɭZ),KS 1" 4 Wh)ZJ\26jzEIƜvr;;#|H!T"i>(5h6L$L-A}nj1]Uc04 q *zE-A M\+}RI$ѸpN/J)lFn[o2>s+RհS.PqU58 2&`.E9 ,P1MbB@ HMPR .Ek!yFi)U>yY!/MR]Mӭ#TLzɺ- ZW_58`>scFi$ (ƀL*zU@vsie{enfn>z%xprlbnfũUXmZjz (Lun+wVrh*BL<)"Eowf)-s3 uǵԞkŵN6 #`,H=j# D]*ٲ rgZЛ4rN, Pȅ$,Rx ^p27wHzeBd%*Y ij"4e|{@=+0G  A H*)B5PJC40m‹b; ,'B\x`rP$˸`4g->"~9R{5aO T$%yתG\|jpڰ 6pC) 7_LDI#yQbG9q2WN㕯4,aYfPq!H@ta/8_~|JTR?h ɤ3DB E+fH(jE# Η؄P$BVmgOS}ďl#,̀P졐'I"($FT6Ts{J.M>)P(K(5*Y 'IJ=ӎQA"P3L!T#pvF|3'а$5CgXIͤUhaՀ144y'W) Ǚ js^ܡFBwġK!5#1SHP Q%, BDYPA4%x~ֳ97aLmh0܄ OHZ L2Knd Hp%$5K~k HQ![6LP^{tpG>Q{@@tSTYѲ%vK\Eq߰V-GP`qn ZN%a\vH`fu#cp+f8"D9YTb- E+!WTk,@bHoC;`( WRDE;A_RjĤ&g+#EyUJp~k{4(E aA2 G%4AĊ{*ı ʐ24\qSL d0J8G= FlOoٚUxQ)2W`@"aXt#8G6ΑY@Z8ґl"1v)Q>44E1atCrB0yYC%E=qz뎈T:IaG5N9Zծe4rmEԩZd:E[)!ZB@=, xc!BuL1s)Zݻx sgj !M=_h5 E,VV 4pT`#lu*`TZlG6 E~òDoT`DzL d &^CJTqN9TH3A.@o#- o$IrJ@`X~H܅D<Ϝ GV1V`*"%S qVҜRb Tb6;-BWpjjG"AjjjaT;OH5r=d"p% 5 AKV> \7 ahl aS):p N3:))azu83;鴗 - />m# DqzjT A`'2aXj tHC\3Y4@cZtdHG%vC^",5hB @j!jN֡AhAUUመ$bQ5Rc/*ckfqnԦ`LBS;lSZWdOZBԵr@[uD)=r!,7sr7s'Ws?r+7stDzX?5l=B"P7{Nʪ,D,#-ĂS_@-%NH~ڎ1.&Z :r6 ,1!!|Ƿ|b 6_m;CPxY,6`(&rKxADoQ6)PHi= =0*BĢ^A/-̈́ǡڢT8*c*Rڜ ,#V@SJ?b 흀% tTJ)#L NGp D(q/.q}2^b3*Lm|'[`A!؎Gh<v3M$ ˰=% . @CLCKb" Ν &1$k6XZ xT<K<133^Qk7HvJfF ěA QF軋,]%.[TtB@ 4Tw47)+JbW@ >V. GCHS@x˻4DX/^]J~y ` ߈ʫ}6C$3V P2 :hbXQU0N op'c!*!Y"v%, i+EhϬiUWVA$Lh! |xwSվ6 ̉;HGG˯!"O*7$R + 78:B8*hKaDr lFv:p@c0#(MpJl~/,p@*U ;1Pȓmb0;ǘ%٠l9$ A8<1X .4j"ƨ#Ri+XI&(WTRH`WXƺl>#1e(H?‚SaOPV#/ [;Μ8\ HL3D~.JJK> nȾ/#-au%9> "r ZҶݖ`d9Y9VFaC=C7iQpb'D3G%}.xbԦNdJ)WӘʥN)\RdX(G ģp`od;=) ;ēO6M)N~bKŀ=bsCnULQF˄ѡDK@MM>zD#ӊHbi`Rv w3:ԎT`Sg1lMH-8G.(11(8pP)pH !9PHQDnPIT||#TȩB ])l)h׊Cm:/p؁r oUWBeH(  zB1q`K"y2KbKDڈ|(1g(#69G@TVP\ A868~Py؀#a7}`h? <fVk4ku9w$Y4g;Cxs(KOk(Y JK0LƁGdh9 (@q(,Zl8bVX3):HA6 C9N#MwY);zS40T*pS͔ e˙*b@UtpA|CkfSفj"xf6E4?4=B) v-a {]Imln<(+#fJA*W*@p[Yjф%" ^e{D!`P1D)t!޼=k~W]ns2w}bn*A6 Ôi@)PZIڦzEDW(M+Rk!}-}rK~nkrhwé "L h 8` H*T)]6բzqHES,(D#G$Nía[xQ0u{]kY8\P%!Ɣr[0 %^KV„stX#Z*kʾb'ʄIRqJ8}c$\ً1mNǹsw\($"ukW[GH] /: v-VJaL)Y`VV8NWc[<#*L{pE)3xuWcn҉&}lf'ʕ0_KI_Lm* jbxMD?r@.0T F;0kl{O)?.QR . <r|B΁G 3fLp# do\Uo~,<"S3T cfk9L䫼TcS,"g0+]d#l+ѓ0ܕES|/%YqKqK||.,n[xv4{}痲>ҋunlHc nOsBP;|ooɖz7}ġ:1PH @4ll~ XEC~}9evQ/1(3H5h7Fb$<:@xGIK؃=I؃]a$`TO>(Oh$R `RV+\ae8Gz3|mn\wsu8wXlxh557evF |׆l(Hȕ>9g\HgsXs}xLjevWF ǰ؊p\簊XuȊXU a p xp\Ȉ\ȌʈfXπxltBlE(hDhDȏ 4( 9X? 4iix D uDFPa6wlh&5a1i\πI  Z p PQ!Ǡ=Hyp I AIp • IDž2ϰ  s 4!r 8-wiUǀe WCy cǀ\ p  9X'e'}X|6 pR TpP PpfR[ ͰP!~) `T X O@ 404͠- &p[p R-PP@ I 94ğ yoy py& CPX Z-O s H 0 @ SƎ\4I7   ǐ ݒ @.w R ɐ ( p)Yz Z* g:H$ ao9A 0Bsz~A @ Ҡ ˀZ  e9 * \3DV@e?ʣh(sk'lt#Jë:A#( Ր ~  i:0šxy6ٚHw裱}\耯*Nndup ⴯ {\˱ֱ! ! ezKʯ#۪x\V9;˳=<[CKEkGI{R@L[N۴O;LP I@XYZN[0C[Jkg{\Y;R`L۶[ekQeI;4}K5з7+4P;[˸~ +Kk˹빟2;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentRightCaudate.gif0000644000076500000240000006432510534177571025063 0ustar paulystaffGIF89a 1##"-,///Pc88G:FHOMNL4bXb^=poxv$OPPOOeN_`N[sWWe[[rZh^WhiZks\pr`[Xf\`hf[rrq'2+0=9>Y QTfj-Y4h'xNfgnjzo{nyyp|D[MtGzaWnuj|uiw{zy2lnouvzz|~URqf|w y z0WV}R}wVVk` HX7d:iO}|oR|bzyʓ& 9,TzLmQmϻ ɹ6ƹNúkp4)9IpNoFpΚ狤ڗ؜ěߋթᯯɰƞ֚ͧƽܲô֦ް浙ȞƪҮέӸ¼״ʳضŷ¼횆鮥ĸ˷ȖƶҰŨ˶ֺ֧ۋμ, ѵP1G*\ȰÇ#JHŋ3jHp@V@ɓ(S\ɲ˗0cʜI͛8]i$Ϟ}D'RHӧPJJիXjʵk֑(H8IEٳfӢ]-۷ni9aDֽq#4/^XYaDa@Wɍ7*ENY8)U%,"߰y摚[Pj0m۫ E2lZւ hq@C[ ++j`c%MpB ,` \敂ٽ$GXJԠ %s9Ђ)r,I, ,ݐ_PcI0a)AITLp8 )" K(a" <6 \L8qxN;Zd%>w` C,.I0-KԢD,[.B,P+ЄI +p,|+,hHҜK x -,a|\ KzcV*@뫴N+]ĕĐ@21`0I%ıI(է B1AP6JĬy[x$J,a*b{b:{A0m 8+~D{Kx+K$A[dÆ9Y^'D2D.xhbE^`lN $J>$€*;Sy(B.' ߋ8n97C97.ᤏz{ꆋ[NϞc/nC@:$?O+G/=(cA_߽ه觯Tԣ9>pO'oo*{C<;^: r<#z^< X%8`<$<=̠7A |A_@:0B 8 AAh!f0Bt…О: 2!!N _HB~h Z1 m` H2_"'A5#yTE':AE0PE5HB(d #(8El;d'q3v@ E?xHPH0$LaU*i8CVف ` `.YUPD' Eg@=ut84H `PC<19[ÙIS0*8 0G'@x5axzpjS P`+J,&]x Dm`.AA R0E!Ӝ, oA mZ2ϴ5m!ъ6%QRbf,`rC ?re_uy TXq:b"f!KFdcGLg/xFs#|ΗoKηoI@0` @Jy5s[ .:\d\zK 0cz!8oy[pE-Xq gp>&n ]55Et* %EOBR2(R6p3T y˫:qzԣ:Iu#sJQt`~P{P:7\B7s͕aPb"JxEL^_p Š x@ Ơ H @ `'MpEx= \r5QU`468s8ehdD\ͥKKxE 8IO 85v0Ű H ƐP@ 7Ġpa?!wb4KVK8QLCI~4GIQsFQ%AkքK U.90 @ ?`  ` 9 A Š@i h 9GUvfzD9yx ~5KW晬WV>6iAwPIi1XI`=t<JQdFi[Üw0Xx@ F ` 0–ƐDP (K9PkUJd`jD``g0X 0`HX ]XKofl^MtJ QFLbz&x~s'x"d k¹3~U!D`9#B(sB'`5I Pp 9 BPH@J ?D3`sj zu `_P3pr*~꧚ք*,K%eL~ eept`vI1jQ 8> V[BNJcYHǠ AP P p B 0H D@7FBbOY[xP/4=X)tS@DeYCFsGA٘kۨTCV bL&o(3=E]Yfj7W Dԙ9]=pI ;<DѩŒPʪ p (E7 D{`LFޖ$K JEu`lB=(aT,k4*FP^ ^sWDLE霁([K[] tRZVg[VnE6ˉ/5Y ̐5 ۊP HtxF*>K҅al`vP]{FJJIW+@穡ʢ)LЋ]LB{)JfWW۽g)$J$~@JhB*l-8hz ʠ g 0LbHٳ[C'f] xv3``rV[禮4ŇkЫi}n8췟W @Ly k3zBOj( gX|YZX,m`0P>xaY j5{s UQ 󩢊X{j: A\ɨ ƛsKB0B$V P)4 {pnh_ Y^|WLPdvJ9GJ\t@~ axe\(M0xL~y~6E6{3 vw<iީ6B XV\txЯƶ,˱L״ Lr3iWJ9ͭ z i ~C~,a|~K Y2~ҋDu [C4QnΏA É=@ȋ==H B 89 д<]wru\m]Z 0\$}B\Ҭ fDJ5L;n'a4[D`5D[-B~0 i J ġڨO$y=”@Mi:@* - Dvbm;vZe Ċvj~-͡k6 هV.Q~9E~ߋ`ab@W0Ey )\_>u*Q/@Pr#Y>: ?p DƵlcԝ@, tPmK P&h-Nl,Zk@N;7n \n.#IݛlLQ &o=>ryG  _˲LCGB9'ڵ \>T FʈBl~K s@xD6ז.Ky[>,(AVjeaCSBej *S!6\m)ţĐu̙C˦&6͜y'O 3Q`S 4pÏA6*C٩áPjVNjn^vN%!*Z"]#8! `$;ርSE$ūls?㡙u@tIVA#BȌ:( Hbe =HȽ% S95J m t;ܾL`pz„)B":)eÏMȌc@0Þ@7ԠFt/D(`8iA!/II{bj ְ}Q%>AY11P\K$?x6IˆF2 gj;DžP Q؊iT' @,q H E֚wu1JpPv:^!Ʉ?_PA0%]S ЊJ d5EǮCꙿ)ȷ[k`M NN߼Kp  A8˴+ChkȌ4q@G)IԬ q3:8QsLS{(kL r4J/X= ԀĽA @0ۈ* g1HQ Qs5k;D)fԐ/`lJ lFʕG,"EUp<52x 4I0``cCBCB#X0Q곑ѵABU EɋH h"xJ( @ B  X󻄡#GO̵Vܨ" \;SSڛb L@Ɯ PX8A79(INu K CNP .0 'Y&X& NC=QC-Tl A= +EE -H 9)r<ؓXD#& QDIŘ[5I{HS p/#688e @و$ FO PPjkVk=TԢ8hIY,@{VN"硆S,FU~ kD@Ҁ3x aU)N }p[!*Ñ/0M#H hP(Hd' 06ؐ[Em9x TY U!FH`+qIU$dpBhP/NhP%[ HЇ|B%40q&SkK$ڀ4 U1栥JS,mPVlY5ـ30Hi< 4A/Sp6a%L 3Ќ(h\zKCHrNu\`u`SA}‹?ȈS؞)kN3s(-V4?spk_♀b(ܽ30@%xK _Ĉ س(Yiʊdx)(Q%UOPp`O2P e0@ 1 /0ˈD@1SO&ƶs`n'qׇPkqvH!T !)ԿX1v0(>PoI nЌ>&ds?|Pgpq Q %HsV|(M&zBVlNhf^KqzqOtOׇp/ijv{U^ow5R"'>5mІzysopDp5AXVC Y:o9P@~ӈЁ pqhМ <vJYV6VTq؆Nqg-!fNAV :R8AߐU0/8I>0:|؀clFW5GqjW p?ppm[,ɔVPrPEB"ZPyBOtu`χ:1n ,R?*HFƀ˺]!c2D$r; o2'>SX[iw9ipSd 1;hc1TaDnzPq`Og}֧}zQu ψ:Xh~;7 ]T?q;;MP 61_yeEtݯ!p+*4؈,4+\#JG!ș;u94'!Ċ^1;P(i$ʒL*4BLlESZj%:vt? ?h9&MEVM)bOrcg5 &(٤ @$ vfxpP`XEq`ĬY2 A"ԣ9@0%(_!<R*P!u8Kzd9@)%%K+\bwvUahKwVq~U'o}A]~8+69ِ39֓[^9媈u6c9 9)]!r0 8͘UBVTNVW}5W5tM78;@:P6X<_=s׳\kx[W7YTuUvb.uo5L{(_"`?q{#(ZIgH: r0`?A w;l*Wu_6d5_SP+^`6 z [$()B6^q.9(q!t0FΜӡoMfK2403O<͠MKLg'X HIRR,B k @¡zh$Az8)c i:ߒI!0hsinp ' #;-S^+m DG^ Qz##=L9BRЄ/b*zf\OBoKt "S @@&FxoBc4# AzI<嶴p ! >ŇTb $ƒB8t9J2rP(T#([5W9V4"Eʑ8Q!pH| !$f4c3@CH 4l"@k$l4բQ},78s&e3{Yfجf9YvGf'b(G3$F0z"VƇftcG:in_㘇8á2VYk:0 YcBP`3,_Cs ǙBJdOMAwp^2y0qzt\4\q 0Ģ`Xr 0 `p+R>W!h+jZZNao4xa :u(TC'*`! Lt]שv *.! D6!!ar'LVV5CbV!!&!E,"EҦmmǁe'v7AhbY)z**f~܁;pb)f(֢.^b%"+b+00j"tv}34Nc4F#ֈCY2#0jc7n'b2=ZY:;#:;?N9G] D Gp0 q T /6?Z=~'A="aY:11$1,$L}~5pMB9裖}!"COP%Gƃ8R1c1dJcY89X:C5#GC)@ 8`_BYZN+Ze68D6F/%(^@@d%LA P1ADQC!( `ߘ>bFNԥhj#%1d@0d"LB h1Hu!@6@&<<!B/>p$,K!AÎ!(`m؝ʒdC:Djzc?%e^~C~?楅2LB d$1 "t%,dzP(@ByB+5 hiZONZ]62#9vz^(!2B#`kf"C#`\$A1*3l!:l9|B+%GeC+BKt*Tp5* 9,p)D($&iZmj{)R<103,C3,))2~A_c]N(|jji阉2CCR(>3,*6jZ:g58ZV9) ՃF]JhѤ,vibk$#AB:Y|2PFG惢f))(+/ Ü~jC84+U:kó>VN BIBdvzkv+kk~酁kk<+k<#zZxk:+kkz+kb")8d8(C8XC3@5p)M‹lȬ)Kl(,K(Kl꬏͒P’,K-ЂڬӾh(M(2-"̢BllDlٞϊ֬Юv-Ԧ-آ-l <*( B+l*(!'B)=G5@@ZrlhfSYrv6bn66t躮28.n7.G>,3nn..&znfBoV:dZ-*)s)R5;'J=<\LA `}A<@1&P p 0p ð S(@$t)ɯ1Zܣ : 6\O1W_1W&=Aٞ_J 0pO +!dADc1qkWtA9`wpE>"&AT#?2$G$O2$oq)$Ar!GX"3ELK\Z$EFdo>tA A_@_ /K $32G2ʌ0kL1;8233673IsDn+VmDE^d0d1f8@#EC}it^^Gl ˲DMdFٜW0MDV!Wxk,P*Gn3pIWMpHp;SSI2(X<2?Lal MB|X'pL/vWDAG0Wt5AQ!D P߮6h' &I6! Q;c18'٪Y<u¼KlAG!@M.SK=V(dkF@3mI+,"C$B!93F찣K`=U4+(y)iY9}EODExˆxdK؟0IuM$`cďKdMW0t9hP)p73=0?ã/`NO L ~ݕ^ \@ 脅@c}ڤu`EG7hMR7=9!,\} ,>"[˴HTC#}S;zXa}/P#JudcvdG<`źPfkH=]#Ce>8B>7Y zT!D-07o2-TḡD sLԶb$}vH MBʿQX:2>8%4_! \B &Ns੪g @F9v2v6vڠt'za͛3S"PB2zBK݄PjdBTSNU*rn4HA:TRCCg oC֙3c łJ,ȏcAܘqA-x_6q  cNӧp§8y|4(]S"UDPU d9E~*x~]7 6|p9߿3x@|`4MC|4zE d.SHԮ8S:)d*<8(t`J R2?d@G֩OJ>T0Qj@Lb‡8T7.Ec C :qF N +UR=/JȗPERIA'43"5VVm5WC  TDO"NLC z:G|l)2-t5/+܏bY{7{w_A?FWʀ"Wv-#2]1|E L$jcdEJRIی=?Bp/8'JҷZA8xN, ?dQƘQZj^Z^PU0]u^$ @/VS& LB5ceC@{ypUtj,Oq%幸AJy4Lba@AK`%5Λ@оlu䐃l vl98E$(:9.6} 6P!(E#B&WJT%cqK)BK&":8fx_d@3 64*W32;l@%O`E0! aE,8"a QC+^6 qK1RaMP"%(1 b b,>0UBw r(v;9vCw( d;hK lcNט5.\NSpa138TGom'zb 3m+Qhha fzf4/jN3 ͠&[K-SfHz/k%èj%g[bR#`9IF,kC24|Smo96nq[=¨$D #3f%+ Sp(TdUyjIW#.ji?(*g 3|49m.:ȹp70) QR;RT8CyNZ&It2b 9nJVt"aml1آ5$:$q`zLР \ ·Qy=.$}cbjnC'm9 c  C֠ g,SSƙ8@oP-K̳; ߈tCċ]5Aq<Ϡ9 pXy(c 3[vvN9+ rˉrqZ89ˬaD@ KA"reZy'{IT"g<*+2V)^W6N/L6pG / *BF*#y'2~:#C&P;(GDK'2&6MǶZaBAaAsP!X!z0B$ LllV؉*p#p:X\4`/6e' D""w)C#n&߆尺Bx%)&GkDAy0PsJ)-VVͦL< `M4@ PB\NnEp[A,< *b 7@{D0j1DX@.2M/6 0A4@F!"vhU0LfERkvǿP"&8 hlItBC"ŤND Vf)lF78[ `%g$+jWbcL'.( .0ԁ%]r%ab%W&[%/MIʌʢ%Kcubr~c)f1 g CTo =T ("¬' Da7n` ȡ.//r/kl n =(5"1(), -.r"('Tze( D94{ + -gR"7f!/{7.387 dv@H41%7(.$: `1;"&)17ziN"M$D1a4@R[%2CH 5 -NtF;H-$uqdATAAAT.!ұ1?:3b1`M@ DC;42ƟN'3[l<9]XA+Y4p 4ZNe8c r>vyJ*Bo9 @@b:ô$HTLSN2g0 61$ V,bD 9यS'R++uRoBB@ `sLAt:3 Ġve zS*RАEo30`)ʢ8J qo!(0DV")3 Π q̱R4%S1%# \3(]3BfE ^ObV l*@ \O#&UϠ7j1VCoEgnH,#I=Jp(4#Yì?b鐩/8M[{[![[]g4 `5` TM8B `X#ޱhj`i54Tcjm̀] Kwa = I ,d 'VM*OKGMm)q:0 Y )u) m `h{$ Сl ll>l4mhӆT$Ȁ ;k'`44LgEiۑih$̩j@P8!8/2M7mHͺl4IluoȮ)W{"3G GoYP{mO0gYx/oNhs' Zal .]!$4S0\ձv k']kk4dimX m'^MCkE@u#VÜBlßnǪ-IpI&q(<=%& Cc%"9f(o2eS2w$g\\tP\V!tAZa*s'3‡=w`4NKl#v5%b KE?!7v2xgHD(X;S#73%r77}s7١77l(9$ w`^ͩ:ت]$Yx@zxK砠 zM# 272p)o.So\ɢF0-ē~)[mVgp)R%ȡ>$'[ IJ@+۲)%B!:KWvεj 9?5ٵ,@j´i_18XTtb))3 H1|1@I- 6'o+j4ˎnϻL%Ȕ[ۀ!R[S}a;vv#7lX%Y#'YM^Uh]̀ʷ̵]W{#:,d;9$2*Ֆ[ R6w?6ЈwǓcd !7%h.^GIAȚa F̆ ^Ķ+$V|x : w|x+HC $7_sBJbS|B[E"O2x{Lb3rѰ JӣTD"Ӹ Q[ȍR&A՗AAsAiFk`]s&``k:'=mVzu^7yw#ik6֪Wm !o2afjz#'[Qj&XoeՒ堈.dZF!%@+5}4auGk2}ާ@dv#HT~Vw?TnK-U@z7B@C;(e.Z[c){@i>OfDF6Nƞ!̘?EHrc)qc ~Յ@u\4&$`.@k^ڇt2ujUyzCSb2ag# "\@''GZ|BZy2`,cTFPʾak-U|#*\0agA4 㗁~vLSCK X8`RiY)Hx@ 3D?Z+4CC9&XK9}$I;/A~CE!TB,E"mDţĐủ[֬2B4%Xƌ5c&cm bƖgbZ\Dlf`ii`hht &[Gp6r D$$c t`:"P~,IB 'SeRJ1TdpDŌzm-# %Hp1e'DBLL']v)'ag_%HdD 9!C:luPS 3v3*Ab 8*I9B<, Ȓ~@\'RyTwX 3WlzL8ˌ1bK-V(gz魷hpJQD<ƒh82R06QmL{dnN=8!^gS)CM9e2&9 Ë/3Ș-*Yz{OnP&G/ AP!AA`l ` H!ԣIAp>~LT<N8~xL)tFk(BڊqK jn=B""RQ˵E"hZց8qSO8Ҋ|o'Jҧ9n=_\=S.9yI#4==.lЁ>@lP0)֘r?U9Ax̆q^!t54-Q@BbJ~g] . 2f#_c1hՁB,o/aL!E^yK rݳS4Ch $щ ,q9mhL:pEJ[mp3I9:FmR SF`K#/ p$\4H`  H?Ya0*LAE<2;aHKt4)| 0 (b5$i:D-k)4 kPB9O:|#cS泌CH12q,{ixmF*̟8Hqv$J >/ 8 P6YZ!V\4ԯ%%XEBi<8aQ,AyAҤҖ%z8iҒF~aU5$R8"] BhvqG[ :}9A5!Pbp3Mfր)䎏ES`KH&:2gCbme;&Eļ4q_KUJ׍dCD*a1udM*xA̵@ ]HdWh{k)"(@< 2 J$K SlɌ!!GN+ y[I+LM*b`6ShFH$!}r~fP@J{uSCe%ҕ@䈝xYƲV%2 5hl D] R<kE6`@ Tbs3Ce$WGȸ#2 g1CF\caG5hб`L@`J}p@b~~"zuK+:$ qs < oxn?<(/γCG;і@XW*MT$&c,Яg@lhK9)&6+#8KP;Nc]Pխ.n|>X~2KBԃ Y#}ZKsqAg ypF6ak]=v#@$qdnbiTdMdY;1Nn՜6PfZ.[ĺ\mIqc\ '4 1xh}~55I!:TdlqJdIlr!,T0/P?b;VZ QgpE6r9AA%W~ȁ}~}Wdh3A /!t7I`XKBa/K55b#0Ko(%9mkhk~3 (}`(v&xdvX<#C2 9/ \Z] @5z?(wPf 2JatiQ{w+֗煖؅aȅۗ}G'H.x5_T+HTk^+AP{?5b#UjJAp&XWcJ`ȉ8~w e\H(6$67A2`<L?r+mH HkkAJ![8x~٨ze.Ckn40e-4e7zW>qe?؋$؋(hC:YEl6Ei'I"V"T%ݑ ِBI 귍+ QU8XqfW$㎽$z(00.qoU耓tC@E} ɗtQ I`G;!VP@s1_EA}W$IOS#ݕ h7X= ,6CC ٗA9WExh#ɛi#Te)4DOEǗɐZW@ ~yɅ[[(Ј-RyH<`0.XE)؝Mh gz|׈~ >h h \ (zH(x@  Y3%CJEjGI@!B"Pj?Q*JjK:[*^VŐى~ H"XVf}km l*o:q*wx}yʧ{vڧ} eeۗm: W>G :yzYfZ}׸鹉ʐ *J*'ڷ 2: Z}GAٞݗ? O,*j AZ *J ?ɪiٟj:␯؟ܪ<: ʞ(~I6 E P [ PPpGx&G FбH 0,_p p  Ġ }̕DY}  Hp_H pG} ΀ ?* Ϡ )y9ZM  ɷz}ʐ DGD?` 0 :FK p'pA @`:Em!0WkE0 @W:аt~ N[, @F` 0@: Ҳ}ΐ   R =:cZw!h@ ! P [ `A k  'q @ ^! ikp ° @:㋭8<*` yD P:{ { d+ 8}4 p`H [ ڟ iKLL 鐮%T,0[d҈ [Ikj(?ɰT̉`p8~tǀ< ̿2 ~}Ȏl1ȇ\V@ !޺艓tǑ8`Fa&0l˷˹˻1@, \<(FX{vʼn&qʝB}@%؆l4T mL5p ]TqpCAX3yuPF)唶|ȥp}\PBpg],/h1C߬qoۜ;8? n @6G)T6Yyer-%% Ha0Hr.1M s" n0r؀ $ 4цa 3 ]0s,r03X!m8k +6!9TZ;[gƠ剕`ëc $8@ m`0~|fRh3Zc 74ZqK E4Hȁ ذ< 䠠"Q Ft|E4`9#Qʫ4tVgZw^ bwF5ggӐ6k5d2z)@%m:?t1JṼg8ZHӄc4؂ U<%\cC4h<4)2B: ] R6`q,Aְ:uG/VI?GCWG/痯>gUoϾӻ/uZ)Ȱ(ո@ B*CMfXh|'2`zs:]Vw`Ap-bAg{%HB5 Gn!>qpG,bD4N"('&H$EMo]TE Nh xltAoHB^@0\+a SVك° mX@G,r 00mXؠ6kx+{ Z2GL -KZ{e/_b0%߇F4p BI]"s3{ak3 xͪGhe*Ӽ0 b['<'4,A @JPv1 \gY0(2'J̊SE)QjӘW4fG3JҒҚը/I?{-ZjTf")tӞ >: jOq:T^H-NuzӁPMT(ժRSͪUխJvXzАhMZjֵu`7K)B,hWZE,׾հ_W&Veb;Blf7z h VV%eQZʞֵlbMK tF<޾cPo[5.r.nt\JͮvjoxK\fW.o|c/}[_p >pL`;',Wp-lax~,?V/gLX6f1c@?vlcSp1G0?>a E(Ǖ߈ 0 LP U  ` p  `ek`8VV հ`  ~p Pup ppe ` ٰ0%8~ܐy9eVxXƅvŖsav&XV pՠ @ 0u{``9Py `Kz@  s 0 0 C ` V[e x  ޠ0Њ 0tဋKxx` vpP {X` ^eƎV態uGsY `VFt ݀P P` cް `Kx ` `ezP  p z p@ Xkbz`[` vpx  u0 @!0 2@d ݀:dz`"jVxx(uwW c}p ِڰ ڀPVy ЃX@P ǰPxpڐp i0d p𔻹 I u  {Ѝ e Yyufxw\i` t0 0 Ph p j U9 v)𠦐 @  ԙ zp0d@ Pp p t 0x`P␃z 0 `p h  WHhyew5y9@p @ xp A[ ` 2v  )P )H ܀ P 002VP` :?Xߐ @ :Pw 8xf\xJg_FsQWjZ}&sWv|J}]V  vv|[Ǝ[+hx)yէx&ztJwV` 0lV+[֟,v2vWg+fj}Su7sviwqc(pxGc[7vGwmFp=jSvTvas'x!5'q붥'eeg+k pƶ\+Mndz`ǧJ+K۶PKЖ{kg;g5 rkm]tQkng=k9 [Kukh7{3sUUۗy慌bgVGw3+v zkj{V)g`_hK|o p>gvV gxֹƯ(Fw[u᛿fvr;\-{ p|z5{r f{<{,[{w[ D̿9wk[(N,>w:MLiDLLfZۿXe><*[KL6,[L6PBO̹ oyF%}hz` ]mN .nmlg΀ &.&S,(+1+6~8~9[`=@/D^F.n㉐HLnR&MR䓭q,n p`t坧Ϲ+ibn4̅|s xvF&ɛv>褷0¼LPwN^2萞WK>}hPrzM .hUn~~OMye zi2|ζ؇)va_>^n~>^ ҾNMym;v.攎cf Oܠb[o ?{GLw;1k9|h/4_68:ڰ@BO'wmu pNvc3ePc[R)OC|VO@ѡw}sӟKAmZԨWZJV[Ŋ W8n4Lr͉= J.AC C8Avnf|  \\+mw ҎjcF|ZTL57'^qɕζnq`ꌷ;릁ڣQ"vdۋ-1ص 9u"TwGuQt`kh#ѩw *x;ߖP6C9qI߶aK!.ly keY E Rāgx9Rv܊q#_H&_62n0P"gj6I 7Ϋ{JtDj-?Ӫ@ EOǛ<6œq<0GSY%yydUaE\ՉQ|j-|Q56kYeC4(TTױǑyj]ԭE''zutr$<:7Asv9Dw\rU+fU{֕]xӝ罟ޑ'uwjBwu牬o[4qOsCNkp+3mrpc~8vc=XdC^ɟGd?.E]dQ7{Ð܂-g[.:(Iz飅ޏZ晫:jQkjZzz~fiFZnlnFw{!| /q7\pD[q!}u[ojhV7W=\4'Gs_m_x]ֹavy裗ѧGͶbbѫaO^ G{7,3tb OB#lbm8x!pnzC]@PQ̗0oJD8@(B'4`7a8BN{! SBu,7Afd: Q* )'p})}brlF+k;ɗ!յOhq4`B;{ԣW RU$YyQb!#E+\d!2(ҩObpsHG0%'o5t`IPqOd%+J9e>K`"g~&%<$N$Fw?P I%8r2Є@a C&3e)Jꀄ @eܣ?1DdqIX"fҡ4:$@y;S@Bda=L`(@+O\!9BH,$:Ka*Ox = 4y8)zaRІ$z!F-Q n 47Ly8`*4Ӗw|)p )* ؆$AoD?!U)X#hZPa>@~(> ? P$^8[RO`aZR!4Cd\B1mbE,î49Ih#֠>d[9Ѷ<3k9 ~"h"$P]b<j$=HQփF>JG(/ʕ!1K n)/Nl8#YS*yBG= _Xڰ/&#YI^r e,KyTN<'o9KΆ|d_hFl4pf鬍NJqr ^9* t }hD'ZыfthH/zt"p>ƫkU*+.~ȣ cJGSF8a?@`5Qku:%21j\6!k:Y\K]˯?Li~7PQv H? xxG?GO$~5j{?Hx`4}<(NHXxDyHC!F`{H@q=H@2ĥVKT'bGA b )L7%7Wq1ft ٭؁fh8췘b`P|m;p]cv{\8"@ 鰃(p+M kK cF.wpH uM^M_ xA{ rIUc*jbLx<.Q k`le@Q L _U(7z]h#uT d(6F`b>robH7)ߎn† |?}Xo8l-8$+_Z"@*j=s$H3QK4|; i&7 AFZy>,: |,"4"T|?~ ²,B-B.- C/| x(xx3LC4 1,&쑤 ܷrBՑB< C;!Oˡ:DFı r$@$kXC1.!"E2DU|BhN+bPDׁ!N69`:;VE¶RQ$FT,7 iM|b ðk!m|nm!FVq,D?$_SE﹠WDi/MFbtV-sC!c#PE[\nEL Y'DLR3YG!JDKˏrLYG;HL1| M9NDMd6!3JITΏLdI>KTGKDO|NN~M`̀$,ӡNu ,E :=J#]s$3%TlA=E*S}Pr*-DUzTt8m4=MR:32U%C@dG@}=4Tb5ΠdRezHpH~~W XX؃%u zH}XXXXtI5mWtL!~(e0eHc0Ub0YUY5%ٕ-bH=ٔEٛYM%D zdY-ZMZ]ڣٞeZmb腅MSi}̙*UhZZ [[-۲a4^Zhx[[[ۺRS*VnU5YY \\=ٜ-Õb^b\\\\S)VoOy-YٜY}Mݕuْ_ ^ʭ\]]^^=^_[ GU)_]\=_}4b`OY 4Ѐ5Op\.bЅhU e`Y=YcH,brX\`SM\DF\ ߿ME0xX c0`^ v f\U`1͖eٕ=V_S,bPcT@v]2c=_^4X548e30؄14Qc=\ %# yg '^~ge^8bg=g\j^H܅^0c0(YcPZ(^4(^x$ݳpUeܯ~iexi^f=۷E[N[?`H{5Xٛ>[jF۵ [[=룦[>jxΡ cDWR+HJkkNjVq tv۰>l곽dy/޺u@aHl˝`϶Ю\.t>@^~m^xmٞmڮm~d[&\'}0/n.n| | nnn֟^nnn!"\E4iJ^R"R ]b}p նZgrs}R jU)' QS;Trl?:7U޶l@ ! l4H(5Vqqqqr qVKr"#gr!w"G(qq!!-g0s0ׇ {bb}Xsxs6/s/W"h#??>tA?'tDtCA?G?tE/CtFgAGAtEEOtJLFHtHtMTtVouRotX7SgYszPMW{( x"v !v8vdOvd/bfwcovivjgvhievhlvmnvoOvkvnlGwpwwwfwxqEgzv|wyw~w!8}b/_xoxxxxxxxxxx;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentSaveWizardFilled.gif0000644000076500000240000003123510534177571025710 0ustar paulystaffGIF89a)%6+*8)1,/Q03M20f8GQ9VyK52M7KH;xQG6lM2zc9USMMUe[eVKfgHgrYgfUktUriZtwlSPiRpmiVfffifugyhfxzvfgxkuxwgwxx8 & 7-3&)7;=;%84LGGSKYJKUZJXgh!F E*T+Z1G;T7[,U a+b$v3b3e6v2rXQTRSz\lr[h[rqulMTFRSgIvrlvs&WYjfvxyzw{|yVMPsoxqyT:e:[^XuqUpnzQwq[ituxYexyfyky~WsygǨzʑђïʭĺÅȪµèĶҡ˗˗ז͗яȧخͫӷdz۷ʲ֬—Ǟ˯ͳ⿉ɴØ̳ɝӱ,0pǏAda/?0b)BQF'V8RƋ%?<%˔0_L5qtH\9ɎB$tȞAA,(z,ׯ`ÊKٳhӪ]˶۷p%ە0vKÈ+^̸ǐ#KLˍ{Ь:x"͋7Rxװc˞M۸sͻ /H6j$ 5Pf؅kνı/<ӣ_Ǖè UP"8wXPv&虂.`2(F {vH\h: 6̇ -߁nFCTLFmLPDŽqÐPJPFY:x %֧ ( 1 r APnC4]h8Y1nX 28aKH袌nGe Ђ8OSQ3O,-ljF9T4V62 G/VECO݃?䓿ܠf ;- }hdaG9ڠ ,ġ {v p0х/PiD L 6 8 4lA!n448t$g\H meGH&>8щ&FъSb=)bqZG1bx? |rzE8Ha!pB C; A0aڐ7H `38P.PH 4Bf`A,rY# 8V0^:fb ]s/adf1F2p f νs$3d&* )̧>)ݯ,B/t $BІr˦ߢгlF3`ztufG5:̑Bs"JLttz'Q,E8љ^"ԧ *PA`4C*ӦLMO*D)Vխz54_*X*ֲnnhMZֶ5O}\InwN %⯀ `KMb:d'K>x=ó;8Kўc9qruiK [w6}-jOܦֶ=-jCZ}pu[[6.tR7iq]f.xw"׷v&7E!}~ˏ{Cp;'L#ؾ |q~xGL(NWb0A51u @L"HN&;PL*Xά;~l.{`Lf+Z2\6p|,jfg: YπrgAC fF'v\h1+ʗ1#-h4߹я1-$lt7Vȕ.5ck&zԲ!i[>r'POs\I'׋K 4/׿rYMl{ԫvQiW>5_}n*k=z{vlZ[fwMp} ޮF\k 73o_cn.t}]O#ߡ.6->7{^}_~۽rFnK.w={Kos&?zשNrN_v<ι#NV[M=tx'zl@8rB  kZ=q#^G5Qh{ߎپqg]~Ǿw!;xDQTB%H~|c*(y">%Nz@ PEBUX?F,, ~=` Rlc`}` d@j AFp w  @h`  Av  @{@ 1  |&rlKvD(lz7_؀ `  ݠ  y0P\8!fy` { 7v@y@ 0{ @S  !  0c h bP 0P}h x p۰ `@~6wwwwiķs@gX   80!z @dr @ b{ {0 ߠP`v!p ` @ayKgP (y #HVxGusCs[Ɍ'vd@FK(b p ި# 0p~@ܰG P}c# ܀0PʰPy| }pz!V @P `{ ` pO ` {pyp#ps榕QzKs!>Dgg u@ k 0 P ed^   "  |0 @ 0 T90 bipvpv١d " wPS袍0^ #` aRf`{wv c#0 v)# p #P y0 8pЙ` ۠ p" p b: j y" l b0y  0P *`:#NY'jw֝[x]nOggeUih8rVz>vwvx   {٦#'wj7tz`e`Zwpu[dp  x7֯v6fXJwji {qoGXir+at>ز/\ɮwyrֱ<'x7r'wn9c&6n*hqH(qki͈ DZthWlV{1;@wqp6ku[KsJKl^$Yz4U^+yz+w?W+{;g-Gl]+nO|P yi5wB;vzfZkK{+b˴wkKkf qnk'o۹ۭkwq֫Q{{ul EXk拷FkWq[C˽faϻ˸_0ۯ7Khd;[! e[[{h{"붔Z˾KnV2뽖ۿ8<uk(KlF$|;LXy{#km{# K[y6y,D4!۳ѫT{H7lS”MgԻ7L@:|"&v?x+mE]T d<{݋x\B akj ǺKata߻Ko[uyַc,<ƹܰ",qLm \L\Lf,)͔̮m +L@W(gͥ|r[MμC\KrþĻ7̖\fw^Z蛾ܴL̸ MѮL+ϒ_+M ]sZ|\r 6n^fdiΘf.gq._nNwVv>؝{@^Np芾膞$@.Nn>^$ n騾.>n nKqb Bg0lsv~؞L׺p`J@K PѰdNe @ >^  4(k@NԝPڞoȞ~^ Pri@@ рy?y # @0}@ 0Ө $ ./ 4oT p ؀} p 0a pQ` e   ! Ϟҏ|@>d |0 y0 @~~!@ }w   `ۨ ~נ w0P΀ P`~0Հ @vp dPWQ W x[dp -P 6ơA<,KoU]&t [.CՔ~FWCzX-#&2欟=?KmRwrRYnWaŎ%o_V~Xz OܭJ7ɷ$~|g7LS0v@ P|]siX'+tYce)_~V<`V+HҏO4J\Щ Cŏ'_x{󈔈;wݭiMXĬ~̉;z2lB UFm|Ps6{(~G{zهHbq*qGZM>/Ch٦ VEk 9Ĩ,!IoPnHDK0:O>܂K. P³ǖ8Ss,{E0O@LZ#T-KPH#tRJ!3(8c?~6X/pu&oTaL%9js!d+@n${:ݗxpcV $=lp~D8.JGc~yWd#x( W#@9!t|;j} 1Fԧ" <0 :qdxG-㗰 Z*2͂=ԼyVTjZZŅF0rC>qx""H`zLu,qydrd?z4Xc@+|b8UX!֣>XIF6Vk(3F<uk2M-V%nq;\{Wŕ0%F7Vt\nw_Ucڥ1%E7LMWb/^OW]~UYLo{5y{QQVõՄ+LaVXÍ;: K8.ղ)nޗ8 3Fpyi-ǣ7vw?&2}cw9C6}c;M2\e'yHra*2^ܷl}(5-mmqQgml[2yt\Bc*~(wF04']LB̻-Z$q`wzxRgE~y[dVOQ=s6}piu|G{KjӮ;2Zz={&֊=PUx(hFmXMOUkOVH0Df؃(BDvX$:L̃8=u !.qAH< c,O땎_O]g{쾋nvx״ Q,m?P;@׸`znm3Pjj0oHTztaw ?C~7A)6{]3S[Nn0l hn|`eЀ + nF`~ViJ@[* p̮ 9T{C;c+K3Dmox($>"@ @ nk(kT  kЄ}Cc3;RKCk1bl|(r[U(BHx8O{yІs~r@{@8yF\RzQTP+ GHGkA#;y6B?Rۨ;5VHDU[=9<^KEl9=~hG@qpLĂwCzX!;`oִnOFj}ljn#fCLAy4ЀRp!,L؇{@ XTpaNZ@Cn0~ cȉ,D{$UeA5JqLqέN_X $ -," C8ePn?hxj7{KEP' P1<?dίR4ʗ1,`H8N,4vqo3d+؆O؆(0ϣrȄmsG$n*wڛ8ūA^7yHLmetHWHUXxauJel(a}oO$P?3:+&ҴG̥"Sr R˲Yո##$OHtH[lc1[EXH)䀏8̴Gkǰ8<-t]Ws4ǺQ]86cު{#۹a-S:5>Bvۿ=_λ߭\&W S^%5QUa f8fd8.YIE ˘kcE#;:>^^ bʝ`=&^㓕n…j^k0^<˻ҼUf{jVgZY^v<ۉudvm]]&lN^n6\碻^fm]emh~}]UA[m`nlṅm_~sK-VŦfj~Eн캴xQcmolfdip>NKeiffTkM>W venNo lWf26NpIpl6KKq}jn>Vdu~FlVe OoC延<&W Fs9wfm}9oZl]`*DW3oV痽5j&'o6q_+v^v 7Gm]IgxhWuXWYXX2Xu[u`n4pep&ݎxd`ivjjlvfvo7oG\Svpp_wuvwqUAr]t6W ؅d8\S(Q0`ȅ/RPQxgx??f^વ؅y/y?yGqWB>kgfec?S ?/SzyGxz^P07R\@ _0dzzz7^ w|.og?-w&Х؅\c{dz{|z]@|/|Ç|ȧ|T s3Fgp# ?{d( jؗ}}'|؏}c(H0}_ }7ؑ?"wAw.]%h20f@f`oE8`(dPcQdG7^Ƙ%kF2ZMI˰B**3]$Q! &$h1^ OW%Μ:mygK8kP:"%T)ПNxYBZIܵe&PUB L9-fhŗs1[%)i̓14)fĚ]j:F[XU}s&UMͬYQ7=s͔tӚ^-j,ʋ,nI ꀚla;2VW@ Z"UBo & `|-@>JdMmY&M׭;VM5kR-U VL"Hogl$`V5S2. v(]2X$䲁<6 %iB%ft`B2M/jm }LI-ZRA%!]z%2F2q0ʠQ(c%͔bLK^qv dl&h+0 Bg3ɨLHʗ$eߦUz)S>)LeZF R))2+Ҫ+ĚA^0KA4,:۬>,V e-lr~M~ Y9 NRy|OQ4se1\/];1뜂/J03+[|䲒re-|24N^C[-Z;1l:- d7j*krLjnr-Ë)]{5asvc{K+_3:_ۦ}6t۹ȽRFZ%h6]V~%ֱuSVtSyc9sι|~礏]dў X '#2 xN(M8i#.\Y>U Y"Je#%՞:eOi6}ľ_rZ3z_vπ t^K1" > jA(Ѓ$! OXB!l aBd2, oІ>a u v!;"L>DD|"?S)^ш\"1yģe$h4(G7sT#:1}c7qn!yG=1~ԣ?Rr$(HI2k$'-)NҌGL8EhPYҲ\-k]%0K`<1L\*lf3dN,f2I\Ғм-)hbNcRL'.&'>}'@*Ё=(BЅ2;itksnap/ProgramData/HTMLHelp/Artwork/ttManualSegmentToolbarManual.gif0000644000076500000240000001506010534177571025247 0ustar paulystaffGIF89av!!!!!!!)!))))1)11)19)9919919B1BB9BB9BJ9JJiBJJBRRJRRJRZJZZRZcRccRckRkkZkkZsscsscs{c{{k{{2756U6mh[FFUX@6fc`rt3@}k{ZRWRsmWksssstr{~r<9""0.76@?ZTf_zqpoJHOPSNXWFDPMYWc^`]ieqlvsfb2CRmms|}ǼټƥƼ͢ŻžťΪŭέֹ̭֮ȺѲϱҷٶۺҸع޸ʚԈ֐ɳijݤռƨĸگЩڲߵۼ抃摋嘒㤞᫤؏ݗǹܨټʪįȵ̻µȻû̲ȿ̲٨پ,vH*\ȰÇ›Hŋ3jȱǏ C^(ɓ(SHr˗0crl)͛%iɳ'<> ] f>[ʴN1E*oԩKvׯTC;];dXĜrfcwvmBx7=/Gpag੏ 7T DL G_RR$T6EzR~ѫsl֐%#뱺$GlFX;n3n9u[α>P0N}fvQO0GvsninS@pHe $?&$鸆C  І{-pW ~$ GdSyM<3(q8*k]mܘ#8e |/WZ"'Lw e4NG=accywCh9/+qOP$%uΐw<3PD >@_?9lT*飘C{xn ƌ-`~4!?7DC.l +?~CkM {ޔϪ!/,c߼BËEHѴ>Vܓx ?뮺&@g!$3LM)”"1Db^°MvM*DÌ 8yS;ö́běA@DhmH'L7PG-50Xg\w`wMLdmXdMo rtm| w߀-xݷv#7 cMc=WngùS3z槗W915^cc;3S:5To}_w4kO ݏS *[x1 -ѳ$j~ =c Yp@6 \,w "7h z a;A~& Sb" ؄,A'_G jpa@ D$2Bt 0C,p~K`dZg2ꮌfDר3qn7ڑvF-@'xH[| [dF7JC$JZ̤&Հ ʱܤ(G4~#e4b8QiRTJTHdHP.VUʉG NE# tă$Ϗn =-ѷnOELE|ֵ Gi3E#,@ ,d@JfuX^qf!JU=i`Zզ6mjFMmns{ZضVhK! PM"6@M.tPPpB 4Y׽T2uo[{MjeI&S=|Q^WE"I 2>ԟs|)кv>$[ [7laJq% Q:n5`Ȅ%<#B$,'f@'Cw҈]Uĕ0S8K%08ւRDJ h00=Pb$%r OA dB-!s|%d9J2ek)Q@1, MZZ" ؀#4 $>a(pMCHl` P qDjaJE;ѕ^ ^GD"ǶDI C&r؈~j2U)$Ѐ(5ձ5lcYXChqGPLB&Qn2r&Ag;[c Žo艖vѡeznmvkYpMDo0Z~^v5XL\ՔP , 4f>L+j@%Sp؆D` D&PuY$2xsJxMn:Aw_u5^cb9Z dU\eUr H@ |@T$SP P [ev- o MO~gZ`3U[WV\H@ e Mʀ*P UgtO cF]r o  t r&zTg 밖P SXŰ԰˰İp0 RА5 @[F ]8]е9 ]vfPsҥ9۳C+ ɒ@ jDE\U`UeĜ=GjWuUWz W^{YZF]Qz cKWYMpd Y7 ЭzP@ yXp nyox۸{s{;etQKLP y Q P@ N5JFDN;;9ZE +[F@KR~@λ| VYP K[L`e d kldvD+x4QӀ Z EN˹ GKG@Vzw (O; FY`3Դ`o]MsF;I\ @ ]NE( MZpF31tgtx~gGHPF, 7j}FlmaR|ToFG[F3ܺkDJdIQV > ,ԿkىC*i,ff Hhp0B^{P  Ɩ.OIFnB=I\q>npHHЖ^ny @>_bǕlߓD)Ƣypy@B@C0 P {p?{ ~Ԝ ~mg1i  iV8m$`[^ָᄒFn^ m; Ap~ Hppbt$ť-|,?mQ%  P uڣ ~itDig譜Uk BvŭEf 1O֮SP[O| }U|S\LNڃKVrO OPpPD!˱ &OJ}ofyU\D8\Sܼ/]+e XS/r[ m0o ~0d[vи4k }֠ & 0x 1iְ$E5n(G~8INK?tPB %!DUtQFuQH#tRJ+}4 ;itksnap/ProgramData/HTMLHelp/Artwork/ttRegion3DImaginaryLine.gif0000644000076500000240000002257710534177571024124 0ustar paulystaffGIF89a  #+3;5$$",2;:###$++***(01/984443=<>>>CKS\cks{:FF>JICKS[N6ca knsr {zt<_E\iS?@}KdurwCCC@MMLLLKKTFSSHVUJXWKZZUUUXWW]\U\\\WWbP`^SccWhgWhjYkj]pn^qqqWX``[fffkkkoop`stdxvf{{i}|sss|||>0'GWBT`SSmpuy|~   ,' !0 FQ[ _UXW[W\  #$  0&7%(6 .'60>7F@OCR¿ԠýƸȻǷǹȸ˸¥ɧīɮαԴڵ۷ǿ׿ͻ,1PH*\ȰÇ#JHA IiȱǏ CIɓ(S\ɲ0cʜI͕I2Rϟ@ JѣH*]ʴSͨdLիX:P&֧rٳhxViԩmʝ˔F˷/4l};VÈ-$x}LK=SU3 h榋-c^mԊ\_] AײQKe̺7b0gjjȃ@4KVE*0$O^V͟sY];w='\s]Ǔ֩O%`OCقA0`@8C3 2naOM܁O#k=X-d!CQÏMeOTJd?P Oʳ x_s "[Ft"\ Uʀ-ΨSߠ;8`ǟrhgtӦ鏐t\O2+a'+̪AGO'+:?z+{ޢ&P+-*wC%ko}W.wtP}s' H? kOⷪ>{5Wc :3^VEj&b ,BrO"].xr:#RJ+Hݬ]^t sD@iJ`UAy6=Pzg l ʢ} R9['~FD,|  V*0T&0` 0^APx2\P DX?nhAutu+@(C8QP 1] k>/e(В,*RъBO+lM)'?g]> 6ELqD)N T^l<8-bglrBy Fܮ+Q |%C4!\x`OЀ/вmy!:Q&0id`A$&1( -q ,yx+` NXB$ )N}@(P `C,L NP@+I)O)r&c'r(A|E,ZY|0?7(w p#>X"DEM1 ,]U2^:|Oe lЇB8B;~E' 871y@y$V  "@@V" h'@#`@~=!,b0[6 rb*VP^(T, GK.pGikcB<^ _jG@V >v7JFbݝ,~X 0%0P(`f'6 ¢]PZq L yr9B#@@5n{m4u'ppRbњ2}*A7CpBZLq R{\,kA !JT7-\$w S[as%=i)(. |akbS?E}rNwPdke-Wtp(,h Hhtςg $0WTjYӣ+L"?pNg_;GJ1[ @U&EIA`>Z~#hIbN@7S=e21SU R/ L) e7U$*= OE+fn5}5N/lMY3dce #8 1:[s2Xx;nz]t\6ʫcM #Qq݇ČWlU f}7N6